import express from 'express';
import { DateTime } from 'luxon';
import { getDbConnection } from '../db/connection.js';
import { BUSINESS_TZ, businessNow, toMySqlBound } from '../../../shared/business-time/index.js';

const router = express.Router();

// Bucket boundaries by summary_subtotal (post-item-sale, pre-order-promo).
// The final entry is open-ended: all orders >= the last bound land there.
const RANGE_BOUNDS = [
  10, 20, 30, 40, 50, 60, 70, 80, 90,
  100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200,
  300, 400, 500, 1000, 1500
];

const FINAL_BUCKET_KEY = '99999';

function buildRangeDefinitions() {
  const ranges = [];
  let previous = 0;
  for (const bound of RANGE_BOUNDS) {
    const key = bound.toString().padStart(5, '0');
    ranges.push({
      min: previous,
      max: bound,
      label: `$${previous.toLocaleString()} - $${bound.toLocaleString()}`,
      key,
    });
    previous = bound;
  }
  const lastBound = RANGE_BOUNDS[RANGE_BOUNDS.length - 1];
  ranges.push({
    min: lastBound,
    max: null,
    label: `$${lastBound.toLocaleString()}+`,
    key: FINAL_BUCKET_KEY,
  });
  return ranges;
}

const RANGE_DEFINITIONS = buildRangeDefinitions();

function bucketKeyFor(subtotal) {
  for (const range of RANGE_DEFINITIONS) {
    if (range.max == null) return range.key;
    if (subtotal <= range.max) return range.key;
  }
  return FINAL_BUCKET_KEY;
}

const DEFAULT_POINT_DOLLAR_VALUE = 0.005;

const DEFAULTS = {
  merchantFeePercent: 2.9,
  fixedCostPerOrder: 1.25,
  pointDollarValue: DEFAULT_POINT_DOLLAR_VALUE,
};

// Parse date inputs in BUSINESS time (date-only strings = business dates;
// zoned instants keep their instant). Never zoneless — that was Berlin.
function parseDate(value, fallback) {
  if (!value) {
    return fallback;
  }
  const parsed = DateTime.fromISO(value, { zone: BUSINESS_TZ });
  if (!parsed.isValid) {
    return fallback;
  }
  return parsed;
}

const formatDateForSql = (dt) => toMySqlBound(dt);

router.get('/promos', async (req, res) => {
  let connection;
  try {
    const { connection: conn, release } = await getDbConnection();
    connection = conn;
    const releaseConnection = release;

    const { startDate, endDate } = req.query || {};
    // Half-open business-day range [start, dayAfterEnd) in Chicago time.
    const now = businessNow();
    const defaultStart = now.minus({ years: 3 }).startOf('day');

    const parsedStart = startDate ? parseDate(startDate, defaultStart).startOf('day') : defaultStart;
    const parsedEnd = (endDate ? parseDate(endDate, now) : now).startOf('day').plus({ days: 1 });

    const rangeStart = parsedStart <= parsedEnd ? parsedStart : parsedEnd;
    const rangeEnd = parsedEnd >= parsedStart ? parsedEnd : parsedStart;

    const rangeStartSql = formatDateForSql(rangeStart);
    const rangeEndSql = formatDateForSql(rangeEnd);

    const sql = `
      SELECT
        p.promo_id AS id,
        p.promo_code AS code,
        p.promo_description_online AS description_online,
        p.promo_description_private AS description_private,
        p.date_start,
        p.date_end,
        COALESCE(u.usage_count, 0) AS usage_count
      FROM promos p
      LEFT JOIN (
        SELECT
          discount_code,
          COUNT(DISTINCT order_id) AS usage_count
        FROM order_discounts
        WHERE discount_type = 10 AND discount_active = 1
        GROUP BY discount_code
      ) u ON u.discount_code = p.promo_id
      WHERE p.date_start IS NOT NULL
        AND p.date_end IS NOT NULL
        AND NOT (p.date_end < ? OR p.date_start >= ?)
        AND p.store = 1
        AND p.date_start >= '2010-01-01'
      ORDER BY p.promo_id DESC
      LIMIT 200
    `;

    const [rows] = await connection.execute(sql, [rangeStartSql, rangeEndSql]);
    releaseConnection();

    const promos = rows.map(row => ({
      id: Number(row.id),
      code: row.code,
      description: row.description_online || row.description_private || '',
      privateDescription: row.description_private || '',
      promo_description_online: row.description_online || '',
      promo_description_private: row.description_private || '',
      dateStart: row.date_start,
      dateEnd: row.date_end,
      usageCount: Number(row.usage_count || 0)
    }));

    res.json({ promos });
  } catch (error) {
    if (connection) {
      try {
        connection.destroy();
      } catch (destroyError) {
        console.error('Failed to destroy connection after error:', destroyError);
      }
    }
    console.error('Error fetching promos:', error);
    res.status(500).json({ error: 'Failed to fetch promos' });
  }
});

function emptyBucketAccumulator(range) {
  return {
    key: range.key,
    label: range.label,
    min: range.min,
    max: range.max,
    orderCount: 0,
    sumOrderValue: 0,
    sumProductDiscountAmount: 0,
    sumPromoProductDiscount: 0,
    sumCustomerItemCost: 0,
    sumShippingChargeBase: 0,
    sumShippingAfterAuto: 0,
    sumShipPromoDiscount: 0,
    sumShippingSurcharge: 0,
    sumOrderSurcharge: 0,
    sumCustomerShipCost: 0,
    sumActualShippingCost: 0,
    sumTotalRevenue: 0,
    sumProductCogs: 0,
    sumMerchantFees: 0,
    sumPointsCost: 0,
    sumFixedCosts: 0,
    sumTotalCosts: 0,
    sumProfit: 0,
  };
}

function simulateOrder(order, config, derived) {
  const orderValue = Number(order.summary_subtotal) || 0;
  const retail = Number(order.summary_subtotal_retail) || orderValue;
  const productDiscountAmount = Number(order.summary_discount_subtotal) || 0;
  const pointsRedeemedDollars = Number(order.points_redeemed) || 0;
  // summary_discount_subtotal is a kitchen-sink rollup that includes points
  // redemptions (type 20). pointsCost already accrues for points awarded, so
  // the points portion of historical discount must be excluded here to avoid
  // double-counting it on orders that redeemed points.
  const historicalProductDiscountExPoints = Math.max(0, productDiscountAmount - pointsRedeemedDollars);
  const shippingChargeBase =
    (Number(order.summary_shipping) || 0) + (Number(order.summary_shipping_rush) || 0);
  const actualShippingCost = Number(order.ship_method_cost) || 0;
  const cogs = Number(order.total_cogs) || 0;

  let promoProductDiscount = 0;
  if (config.productPromo.type === 'percentage_subtotal' && orderValue >= config.productPromo.minSubtotal) {
    promoProductDiscount = orderValue * (config.productPromo.value / 100);
  } else if (config.productPromo.type === 'percentage_regular' && orderValue >= config.productPromo.minSubtotal) {
    const targetRate = config.productPromo.value / 100;
    const targetCustomerPrice = retail * (1 - targetRate);
    promoProductDiscount = Math.max(0, orderValue - targetCustomerPrice);
  } else if (config.productPromo.type === 'fixed_amount' && orderValue >= config.productPromo.minSubtotal) {
    promoProductDiscount = config.productPromo.value;
  } else if (config.productPromo.type === 'none' && config.applyHistoricalProductPromo) {
    promoProductDiscount = historicalProductDiscountExPoints;
  }
  promoProductDiscount = Math.max(0, Math.min(promoProductDiscount, orderValue));

  let shippingAfterAuto = shippingChargeBase;
  for (const tier of config.shippingTiers) {
    if (orderValue >= tier.threshold) {
      if (tier.mode === 'percentage') {
        shippingAfterAuto = shippingChargeBase * Math.max(0, 1 - tier.value / 100);
      } else if (tier.mode === 'flat') {
        shippingAfterAuto = tier.value;
      }
    }
  }

  let shipPromoDiscount = 0;
  if (config.shippingPromo.type !== 'none' && orderValue >= config.shippingPromo.minSubtotal) {
    if (config.shippingPromo.type === 'percentage') {
      shipPromoDiscount = shippingAfterAuto * (config.shippingPromo.value / 100);
    } else if (config.shippingPromo.type === 'fixed') {
      shipPromoDiscount = config.shippingPromo.value;
    }
    if (config.shippingPromo.maxDiscount > 0) {
      shipPromoDiscount = Math.min(shipPromoDiscount, config.shippingPromo.maxDiscount);
    }
    shipPromoDiscount = Math.min(shipPromoDiscount, shippingAfterAuto);
  }

  let shippingSurcharge = 0;
  let orderSurcharge = 0;
  for (const surcharge of config.surcharges) {
    const meetsMin = orderValue >= surcharge.threshold;
    const meetsMax = surcharge.maxThreshold == null || orderValue < surcharge.maxThreshold;
    if (meetsMin && meetsMax) {
      if (surcharge.target === 'shipping') shippingSurcharge += surcharge.amount;
      else if (surcharge.target === 'order') orderSurcharge += surcharge.amount;
    }
  }

  const customerShipCost = Math.max(0, shippingAfterAuto - shipPromoDiscount + shippingSurcharge);
  const customerItemCost = Math.max(0, orderValue - promoProductDiscount + orderSurcharge);
  const totalRevenue = customerItemCost + customerShipCost;

  const productCogs = config.cogsCalculationMode === 'average'
    ? orderValue * derived.overallCogsPercentage
    : cogs;

  const merchantFees = totalRevenue * (config.merchantFeePercent / 100);
  const pointsCost = orderValue * derived.pointsPerDollar * derived.redemptionRate * derived.pointDollarValue;
  const fixedCosts = config.fixedCostPerOrder;
  const totalCosts = productCogs + actualShippingCost + merchantFees + pointsCost + fixedCosts;
  const profit = totalRevenue - totalCosts;

  return {
    orderValue,
    productDiscountAmount,
    promoProductDiscount,
    customerItemCost,
    shippingChargeBase,
    shippingAfterAuto,
    shipPromoDiscount,
    shippingSurcharge,
    orderSurcharge,
    customerShipCost,
    actualShippingCost,
    totalRevenue,
    productCogs,
    merchantFees,
    pointsCost,
    fixedCosts,
    totalCosts,
    profit,
  };
}

function accumulate(bucket, sim) {
  bucket.orderCount += 1;
  bucket.sumOrderValue += sim.orderValue;
  bucket.sumProductDiscountAmount += sim.productDiscountAmount;
  bucket.sumPromoProductDiscount += sim.promoProductDiscount;
  bucket.sumCustomerItemCost += sim.customerItemCost;
  bucket.sumShippingChargeBase += sim.shippingChargeBase;
  bucket.sumShippingAfterAuto += sim.shippingAfterAuto;
  bucket.sumShipPromoDiscount += sim.shipPromoDiscount;
  bucket.sumShippingSurcharge += sim.shippingSurcharge;
  bucket.sumOrderSurcharge += sim.orderSurcharge;
  bucket.sumCustomerShipCost += sim.customerShipCost;
  bucket.sumActualShippingCost += sim.actualShippingCost;
  bucket.sumTotalRevenue += sim.totalRevenue;
  bucket.sumProductCogs += sim.productCogs;
  bucket.sumMerchantFees += sim.merchantFees;
  bucket.sumPointsCost += sim.pointsCost;
  bucket.sumFixedCosts += sim.fixedCosts;
  bucket.sumTotalCosts += sim.totalCosts;
  bucket.sumProfit += sim.profit;
}

function finalizeBucket(b, totalOrders) {
  const n = b.orderCount;
  const avg = (sum) => (n > 0 ? sum / n : 0);
  return {
    key: b.key,
    label: b.label,
    min: b.min,
    max: b.max,
    orderCount: n,
    weight: totalOrders > 0 ? n / totalOrders : 0,
    orderValue: avg(b.sumOrderValue),
    productDiscountAmount: avg(b.sumProductDiscountAmount),
    promoProductDiscount: avg(b.sumPromoProductDiscount),
    customerItemCost: avg(b.sumCustomerItemCost),
    shippingChargeBase: avg(b.sumShippingChargeBase),
    shippingAfterAuto: avg(b.sumShippingAfterAuto),
    shipPromoDiscount: avg(b.sumShipPromoDiscount),
    shippingSurcharge: avg(b.sumShippingSurcharge),
    orderSurcharge: avg(b.sumOrderSurcharge),
    customerShipCost: avg(b.sumCustomerShipCost),
    actualShippingCost: avg(b.sumActualShippingCost),
    totalRevenue: avg(b.sumTotalRevenue),
    productCogs: avg(b.sumProductCogs),
    merchantFees: avg(b.sumMerchantFees),
    pointsCost: avg(b.sumPointsCost),
    fixedCosts: avg(b.sumFixedCosts),
    totalCosts: avg(b.sumTotalCosts),
    profit: avg(b.sumProfit),
    profitPercent: b.sumTotalRevenue > 0 ? b.sumProfit / b.sumTotalRevenue : 0,
  };
}

router.post('/simulate', async (req, res) => {
  const {
    dateRange = {},
    filters = {},
    productPromo = {},
    shippingPromo = {},
    shippingTiers = [],
    surcharges = [],
    merchantFeePercent,
    fixedCostPerOrder,
    cogsCalculationMode = 'actual',
    applyHistoricalProductPromo = false,
    pointsConfig = {}
  } = req.body || {};

  // Half-open business-day bounds [startOfDay, dayAfterEnd) in Chicago time.
  const endDefault = businessNow();
  const startDefault = endDefault.minus({ months: 6 });
  const startDt = parseDate(dateRange.start, startDefault).startOf('day');
  const endDt = parseDate(dateRange.end, endDefault).startOf('day').plus({ days: 1 });

  const shipCountry = filters.shipCountry || 'US';
  const promoIds = Array.from(
    new Set(
      [
        ...(Array.isArray(filters.promoIds) ? filters.promoIds : []),
        ...(Array.isArray(filters.promoCodes) ? filters.promoCodes : []),
      ]
        .map((value) => {
          if (typeof value === 'string') return value.trim();
          if (typeof value === 'number') return String(value);
          return '';
        })
        .filter((value) => value.length > 0)
    )
  );

  const config = {
    merchantFeePercent: typeof merchantFeePercent === 'number' ? merchantFeePercent : DEFAULTS.merchantFeePercent,
    fixedCostPerOrder: typeof fixedCostPerOrder === 'number' ? fixedCostPerOrder : DEFAULTS.fixedCostPerOrder,
    cogsCalculationMode,
    applyHistoricalProductPromo: applyHistoricalProductPromo === true,
    productPromo: {
      type: productPromo.type || 'none',
      value: Number(productPromo.value || 0),
      minSubtotal: Number(productPromo.minSubtotal || 0)
    },
    shippingPromo: {
      type: shippingPromo.type || 'none',
      value: Number(shippingPromo.value || 0),
      minSubtotal: Number(shippingPromo.minSubtotal || 0),
      maxDiscount: Number(shippingPromo.maxDiscount || 0)
    },
    shippingTiers: Array.isArray(shippingTiers)
      ? shippingTiers
          .map(tier => ({
            threshold: Number(tier.threshold || 0),
            mode: tier.mode === 'percentage' || tier.mode === 'flat' ? tier.mode : 'percentage',
            value: Number(tier.value || 0)
          }))
          .filter(tier => tier.threshold >= 0 && tier.value >= 0)
          .sort((a, b) => a.threshold - b.threshold)
      : [],
    surcharges: Array.isArray(surcharges)
      ? surcharges
          .map(s => ({
            threshold: Number(s.threshold || 0),
            maxThreshold: typeof s.maxThreshold === 'number' && s.maxThreshold > 0 ? s.maxThreshold : null,
            target: s.target === 'shipping' || s.target === 'order' ? s.target : 'shipping',
            amount: Number(s.amount || 0)
          }))
          .filter(s => s.threshold >= 0 && s.amount >= 0)
          .sort((a, b) => a.threshold - b.threshold)
      : [],
    points: {
      pointsPerDollar: typeof pointsConfig.pointsPerDollar === 'number' ? pointsConfig.pointsPerDollar : null,
      redemptionRate: typeof pointsConfig.redemptionRate === 'number' ? pointsConfig.redemptionRate : null,
      pointDollarValue: typeof pointsConfig.pointDollarValue === 'number'
        ? pointsConfig.pointDollarValue
        : DEFAULT_POINT_DOLLAR_VALUE
    }
  };

  let connection;
  let release;

  try {
    const dbConn = await getDbConnection();
    connection = dbConn.connection;
    release = dbConn.release;

    const params = [shipCountry, formatDateForSql(startDt), formatDateForSql(endDt)];
    let promoExistsClause = '';
    if (promoIds.length > 0) {
      const placeholders = promoIds.map(() => '?').join(',');
      promoExistsClause = `
        AND EXISTS (
          SELECT 1 FROM order_discounts od
          WHERE od.order_id = o.order_id
            AND od.discount_active = 1
            AND od.discount_type = 10
            AND od.discount_code IN (${placeholders})
        )
      `;
      params.push(...promoIds);
    }

    const ordersQuery = `
      SELECT
        o.order_id,
        o.summary_subtotal,
        COALESCE(o.summary_subtotal_retail, o.summary_subtotal) AS summary_subtotal_retail,
        COALESCE(o.summary_discount_subtotal, 0) AS summary_discount_subtotal,
        COALESCE(o.summary_shipping, 0) AS summary_shipping,
        COALESCE(o.summary_shipping_rush, 0) AS summary_shipping_rush,
        COALESCE(o.ship_method_cost, 0) AS ship_method_cost,
        COALESCE(o.summary_points, 0) AS summary_points,
        COALESCE(c.total_cogs, 0) AS total_cogs,
        COALESCE(p.points_redeemed, 0) AS points_redeemed
      FROM _order o
      LEFT JOIN (
        SELECT order_id, SUM(cogs_amount) AS total_cogs
        FROM report_sales_data
        WHERE action IN (1,2,3)
        GROUP BY order_id
      ) c ON c.order_id = o.order_id
      LEFT JOIN (
        SELECT order_id, SUM(discount_amount_subtotal) AS points_redeemed
        FROM order_discounts
        WHERE discount_type = 20 AND discount_active = 1
        GROUP BY order_id
      ) p ON p.order_id = o.order_id
      WHERE o.summary_total > 0
        AND o.order_status >= 20
        AND o.ship_method_selected <> 'holdit'
        AND o.ship_country = ?
        AND o.date_placed >= ? AND o.date_placed < ?
        ${promoExistsClause}
    `;

    const [orders] = await connection.execute(ordersQuery, params);

    if (release) {
      release();
      release = null;
    }

    let totalSubtotal = 0;
    let totalProductDiscount = 0;
    let totalCogs = 0;
    let totalPointsAwarded = 0;
    let totalPointsRedeemedDollars = 0;
    for (const o of orders) {
      totalSubtotal += Number(o.summary_subtotal) || 0;
      totalProductDiscount += Number(o.summary_discount_subtotal) || 0;
      totalCogs += Number(o.total_cogs) || 0;
      totalPointsAwarded += Number(o.summary_points) || 0;
      totalPointsRedeemedDollars += Number(o.points_redeemed) || 0;
    }

    const productDiscountRate = totalSubtotal > 0 ? totalProductDiscount / totalSubtotal : 0;
    const overallCogsPercentage = totalSubtotal > 0 ? totalCogs / totalSubtotal : 0;
    const pointsPerDollar = config.points.pointsPerDollar != null
      ? config.points.pointsPerDollar
      : (totalSubtotal > 0 ? totalPointsAwarded / totalSubtotal : 0);
    const pointDollarValue = config.points.pointDollarValue || DEFAULT_POINT_DOLLAR_VALUE;
    let redemptionRate;
    if (config.points.redemptionRate != null) {
      redemptionRate = config.points.redemptionRate;
    } else if (totalPointsAwarded > 0 && pointDollarValue > 0) {
      const totalRedeemedPoints = totalPointsRedeemedDollars / pointDollarValue;
      redemptionRate = Math.min(1, totalRedeemedPoints / totalPointsAwarded);
    } else {
      redemptionRate = 0;
    }

    const derived = {
      overallCogsPercentage,
      pointsPerDollar,
      redemptionRate,
      pointDollarValue,
    };

    const buckets = new Map();
    for (const range of RANGE_DEFINITIONS) {
      buckets.set(range.key, emptyBucketAccumulator(range));
    }

    let grandTotalProfit = 0;
    let grandTotalRevenue = 0;

    for (const order of orders) {
      const sim = simulateOrder(order, config, derived);
      const bucketKey = bucketKeyFor(sim.orderValue);
      const bucket = buckets.get(bucketKey);
      accumulate(bucket, sim);
      grandTotalProfit += sim.profit;
      grandTotalRevenue += sim.totalRevenue;
    }

    const totalOrders = orders.length;
    const bucketResults = RANGE_DEFINITIONS.map((range) =>
      finalizeBucket(buckets.get(range.key), totalOrders)
    );

    const weightedProfitAmount = totalOrders > 0 ? grandTotalProfit / totalOrders : 0;
    const weightedProfitPercent = grandTotalRevenue > 0 ? grandTotalProfit / grandTotalRevenue : 0;

    res.json({
      dateRange: {
        start: startDt.toISO(),
        end: endDt.toISO()
      },
      totals: {
        orders: totalOrders,
        subtotal: totalSubtotal,
        productDiscountRate,
        pointsPerDollar,
        redemptionRate,
        pointDollarValue,
        weightedProfitAmount,
        weightedProfitPercent,
        overallCogsPercentage: cogsCalculationMode === 'average' ? overallCogsPercentage : undefined
      },
      buckets: bucketResults
    });
  } catch (error) {
    if (release) {
      try {
        release();
      } catch (releaseError) {
        console.error('Failed to release connection after error:', releaseError);
      }
    } else if (connection) {
      try {
        connection.destroy();
      } catch (destroyError) {
        console.error('Failed to destroy connection after error:', destroyError);
      }
    }

    console.error('Error running discount simulation:', error);
    res.status(500).json({ error: 'Failed to run discount simulation' });
  }
});

export default router;
