const express = require('express');
const { DateTime } = require('luxon');
const { getDbConnection } = require('../db/connection');

const router = express.Router();

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, 2000
];

const FINAL_BUCKET_KEY = 'PLUS';

function buildRangeDefinitions() {
  const ranges = [];
  let previous = 0;
  for (const bound of RANGE_BOUNDS) {
    const label = `$${previous.toLocaleString()} - $${bound.toLocaleString()}`;
    const key = bound.toString().padStart(5, '0');
    ranges.push({
      min: previous,
      max: bound,
      label,
      key,
      sort: bound
    });
    previous = bound;
  }
  // Remove the 2000+ category - all orders >2000 will go into the 2000 bucket
  return ranges;
}

const RANGE_DEFINITIONS = buildRangeDefinitions();

const BUCKET_CASE = (() => {
  const parts = [];
  for (let i = 0; i < RANGE_BOUNDS.length; i++) {
    const bound = RANGE_BOUNDS[i];
    const key = bound.toString().padStart(5, '0');
    if (i === RANGE_BOUNDS.length - 1) {
      // For the last bucket (2000), include all orders >= 1500 (previous bound)
      parts.push(`ELSE '${key}'`);
    } else {
      parts.push(`WHEN o.summary_subtotal <= ${bound} THEN '${key}'`);
    }
  }
  return `CASE\n    ${parts.join('\n    ')}\n  END`;
})();

const DEFAULT_POINT_DOLLAR_VALUE = 0.005; // 1000 points = $5, so 200 points = $1

const DEFAULTS = {
  merchantFeePercent: 2.9,
  fixedCostPerOrder: 1.5,
  pointsPerDollar: 0,
  pointsRedemptionRate: 0, // Will be calculated from actual data
  pointDollarValue: DEFAULT_POINT_DOLLAR_VALUE,
};

function parseDate(value, fallback) {
  if (!value) {
    return fallback;
  }
  const parsed = DateTime.fromISO(value);
  if (!parsed.isValid) {
    return fallback;
  }
  return parsed;
}

function formatDateForSql(dt) {
  return dt.toFormat('yyyy-LL-dd HH:mm:ss');
}

function getMidpoint(range) {
  if (range.max == null) {
    return range.min + 200; // Rough estimate for 2000+
  }
  return (range.min + range.max) / 2;
}

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 || {};
    const now = DateTime.now().endOf('day');
    const defaultStart = now.minus({ years: 3 }).startOf('day');

    const parsedStart = startDate ? parseDate(startDate, defaultStart).startOf('day') : defaultStart;
    const parsedEnd = endDate ? parseDate(endDate, now).endOf('day') : now;

    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' });
  }
});

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

  const endDefault = DateTime.now();
  const startDefault = endDefault.minus({ months: 6 });
  const startDt = parseDate(dateRange.start, startDefault).startOf('day');
  const endDt = parseDate(dateRange.end, endDefault).endOf('day');

  const shipCountry = filters.shipCountry || 'US';
  const rawPromoFilters = [
    ...(Array.isArray(filters.promoIds) ? filters.promoIds : []),
    ...(Array.isArray(filters.promoCodes) ? filters.promoCodes : []),
  ];
  const promoCodes = Array.from(
    new Set(
      rawPromoFilters
        .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,
    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 filteredOrdersParams = [
      shipCountry,
      formatDateForSql(startDt),
      formatDateForSql(endDt)
    ];
    const promoJoin = promoCodes.length > 0
      ? 'JOIN order_discounts od ON od.order_id = o.order_id AND od.discount_active = 1 AND od.discount_type = 10'
      : '';

    let promoFilterClause = '';
    if (promoCodes.length > 0) {
      const placeholders = promoCodes.map(() => '?').join(',');
      promoFilterClause = `AND od.discount_code IN (${placeholders})`;
      filteredOrdersParams.push(...promoCodes);
    }

    const filteredOrdersQuery = `
      SELECT
        o.order_id,
        o.order_cid,
        o.summary_subtotal,
        o.summary_discount_subtotal,
        o.summary_shipping,
        o.ship_method_rate,
        o.ship_method_cost,
        o.summary_points,
        ${BUCKET_CASE} AS bucket_key
      FROM _order o
      ${promoJoin}
      WHERE o.summary_shipping > 0
        AND o.summary_total > 0
        AND o.order_status NOT IN (15)
        AND o.ship_method_selected <> 'holdit'
        AND o.ship_country = ?
        AND o.date_placed BETWEEN ? AND ?
        ${promoFilterClause}
    `;

    const bucketParams = [
      ...filteredOrdersParams,
      formatDateForSql(startDt),
      formatDateForSql(endDt)
    ];

    const bucketQuery = `
      SELECT
        f.bucket_key,
        COUNT(*) AS order_count,
        SUM(f.summary_subtotal) AS subtotal_sum,
        SUM(f.summary_discount_subtotal) AS product_discount_sum,
        SUM(f.summary_subtotal + f.summary_discount_subtotal) AS regular_subtotal_sum,
        SUM(f.ship_method_rate) AS ship_rate_sum,
        SUM(f.ship_method_cost) AS ship_cost_sum,
        SUM(f.summary_points) AS points_awarded_sum,
        SUM(COALESCE(p.points_redeemed, 0)) AS points_redeemed_sum,
        SUM(COALESCE(c.total_cogs, 0)) AS cogs_sum,
        AVG(f.summary_subtotal) AS avg_subtotal,
        AVG(f.summary_discount_subtotal) AS avg_product_discount,
        AVG(f.ship_method_rate) AS avg_ship_rate,
        AVG(f.ship_method_cost) AS avg_ship_cost,
        AVG(COALESCE(c.total_cogs, 0)) AS avg_cogs
      FROM (
        ${filteredOrdersQuery}
      ) AS f
      LEFT JOIN (
        SELECT order_id, SUM(cogs_amount) AS total_cogs
        FROM report_sales_data
        WHERE action IN (1,2,3)
          AND date_change BETWEEN ? AND ?
        GROUP BY order_id
      ) AS c ON c.order_id = f.order_id
      LEFT JOIN (
        SELECT order_id, SUM(discount_amount) AS points_redeemed
        FROM order_discounts
        WHERE discount_type = 20 AND discount_active = 1
        GROUP BY order_id
      ) AS p ON p.order_id = f.order_id
      GROUP BY f.bucket_key
    `;

    const [rows] = await connection.execute(bucketQuery, bucketParams);

    const totals = {
      orders: 0,
      subtotal: 0,
      productDiscount: 0,
      regularSubtotal: 0,
      shipRate: 0,
      shipCost: 0,
      cogs: 0,
      pointsAwarded: 0,
      pointsRedeemed: 0
    };

    const rowMap = new Map();
    for (const row of rows) {
      const key = row.bucket_key || FINAL_BUCKET_KEY;
      const parsed = {
        orderCount: Number(row.order_count || 0),
        subtotalSum: Number(row.subtotal_sum || 0),
        productDiscountSum: Number(row.product_discount_sum || 0),
        regularSubtotalSum: Number(row.regular_subtotal_sum || 0),
        shipRateSum: Number(row.ship_rate_sum || 0),
        shipCostSum: Number(row.ship_cost_sum || 0),
        pointsAwardedSum: Number(row.points_awarded_sum || 0),
        pointsRedeemedSum: Number(row.points_redeemed_sum || 0),
        cogsSum: Number(row.cogs_sum || 0),
        avgSubtotal: Number(row.avg_subtotal || 0),
        avgProductDiscount: Number(row.avg_product_discount || 0),
        avgShipRate: Number(row.avg_ship_rate || 0),
        avgShipCost: Number(row.avg_ship_cost || 0),
        avgCogs: Number(row.avg_cogs || 0)
      };
      rowMap.set(key, parsed);

      totals.orders += parsed.orderCount;
      totals.subtotal += parsed.subtotalSum;
      totals.productDiscount += parsed.productDiscountSum;
      totals.regularSubtotal += parsed.regularSubtotalSum;
      totals.shipRate += parsed.shipRateSum;
      totals.shipCost += parsed.shipCostSum;
      totals.cogs += parsed.cogsSum;
      totals.pointsAwarded += parsed.pointsAwardedSum;
      totals.pointsRedeemed += parsed.pointsRedeemedSum;
    }

    const productDiscountRate = totals.regularSubtotal > 0
      ? totals.productDiscount / totals.regularSubtotal
      : 0;

    const pointsPerDollar = config.points.pointsPerDollar != null
      ? config.points.pointsPerDollar
      : totals.subtotal > 0
        ? totals.pointsAwarded / totals.subtotal
        : 0;

    const pointDollarValue = config.points.pointDollarValue || DEFAULT_POINT_DOLLAR_VALUE;

    // Calculate redemption rate using dollars redeemed from the matched order set
    let calculatedRedemptionRate = 0;
    if (config.points.redemptionRate != null) {
      calculatedRedemptionRate = config.points.redemptionRate;
    } else if (totals.pointsAwarded > 0 && pointDollarValue > 0) {
      const totalRedeemedPoints = totals.pointsRedeemed / pointDollarValue;
      if (totalRedeemedPoints > 0) {
        calculatedRedemptionRate = Math.min(1, totalRedeemedPoints / totals.pointsAwarded);
      }
    }

    const redemptionRate = calculatedRedemptionRate;

    // Calculate overall average COGS percentage for 'average' mode
    let overallCogsPercentage = 0;
    if (cogsCalculationMode === 'average' && totals.subtotal > 0) {
      overallCogsPercentage = totals.cogs / totals.subtotal;
    }

    const bucketResults = [];
    let weightedProfitAmount = 0;
    let weightedProfitPercent = 0;

    for (const range of RANGE_DEFINITIONS) {
      const data = rowMap.get(range.key) || {
        orderCount: 0,
        avgSubtotal: 0,
        avgShipRate: 0,
        avgShipCost: 0,
        avgCogs: 0
      };

      const orderValue = data.avgSubtotal > 0 ? data.avgSubtotal : getMidpoint(range);
      const shippingChargeBase = data.avgShipCost > 0 ? data.avgShipCost : 0;
      const actualShippingCost = data.avgShipCost > 0 ? data.avgShipCost : 0;
      
      // Calculate COGS based on the selected mode
      let productCogs;
      if (cogsCalculationMode === 'average') {
        // Use overall average COGS percentage applied to this bucket's order value
        productCogs = orderValue * overallCogsPercentage;
      } else {
        // Use actual COGS data from this bucket (existing behavior)
        productCogs = data.avgCogs > 0 ? data.avgCogs : 0;
      }
      const productDiscountAmount = orderValue * productDiscountRate;
      const effectiveRegularPrice = productDiscountRate < 0.99
        ? orderValue / (1 - productDiscountRate)
        : orderValue;

      let promoProductDiscount = 0;
      if (config.productPromo.type === 'percentage_subtotal' && orderValue >= config.productPromo.minSubtotal) {
        promoProductDiscount = Math.min(orderValue, (config.productPromo.value / 100) * orderValue);
      } else if (config.productPromo.type === 'percentage_regular' && orderValue >= config.productPromo.minSubtotal) {
        const targetRate = config.productPromo.value / 100;
        const additionalRate = Math.max(0, targetRate - productDiscountRate);
        promoProductDiscount = Math.min(orderValue, additionalRate * effectiveRegularPrice);
      } else if (config.productPromo.type === 'fixed_amount' && orderValue >= config.productPromo.minSubtotal) {
        promoProductDiscount = Math.min(orderValue, config.productPromo.value);
      }

      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);
      }

      // Calculate surcharges
      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 merchantFees = totalRevenue * (config.merchantFeePercent / 100);
      const pointsCost = customerItemCost * pointsPerDollar * redemptionRate * pointDollarValue;
      const fixedCosts = config.fixedCostPerOrder;
      const totalCosts = productCogs + actualShippingCost + merchantFees + pointsCost + fixedCosts;
      const profit = totalRevenue - totalCosts;
      const profitPercent = totalRevenue > 0 ? (profit / totalRevenue) : 0;
      const weight = totals.orders > 0 ? (data.orderCount || 0) / totals.orders : 0;

      weightedProfitAmount += profit * weight;
      weightedProfitPercent += profitPercent * weight;

      bucketResults.push({
        key: range.key,
        label: range.label,
        min: range.min,
        max: range.max,
        orderCount: data.orderCount || 0,
        weight,
        orderValue,
        productDiscountAmount,
        promoProductDiscount,
        customerItemCost,
        shippingChargeBase,
        shippingAfterAuto,
        shipPromoDiscount,
        shippingSurcharge,
        orderSurcharge,
        customerShipCost,
        actualShippingCost,
        totalRevenue,
        productCogs,
        merchantFees,
        pointsCost,
        fixedCosts,
        totalCosts,
        profit,
        profitPercent
      });
    }

    if (release) {
      release();
    }

    res.json({
      dateRange: {
        start: startDt.toISO(),
        end: endDt.toISO()
      },
      totals: {
        orders: totals.orders,
        subtotal: totals.subtotal,
        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' });
  }
});

module.exports = router;
