import express from 'express';
const router = express.Router();

// Stale PO statuses to exclude from our counting — these are the "still active"
// statuses as defined in scripts/metrics-new/update_product_metrics.sql. POs that
// are canceled or done are excluded separately.
const OPEN_PO_STATUSES = [
  'created',
  'ordered',
  'preordered',
  'electronically_sent',
  'electronically_ready_send',
  'receiving_started'
];

// GET /api/repeat-orders
// Finds products that are repeatedly appearing on small POs to a given supplier,
// indicating the auto-PO pattern where we keep buying MOQ instead of batching.
//
// Design notes on dedup between POs and receivings:
// - We count PO line appearances (not unit totals) as the frequency signal, since
//   that is exactly the user action we are trying to detect (repeated small orders).
// - Canceled POs are filtered out so unreceived garbage cannot inflate the count.
// - Receivings are joined for read-only context (units actually arrived) and are
//   NOT added to the frequency count, so there is no double-counting between POs
//   and their matching receivings.
// - Stock / on-order / replenishment are pulled from product_metrics, which uses
//   the canonical FIFO logic in update_product_metrics.sql to avoid double-counting
//   units between POs and their receivings.
//
// Query params:
//   supplierId  (int, default 92 = Notions)
//   windowDays  (int, default 30, range 1..180)
//   minPoCount  (int, default 3)
//   maxAvgQty   (number, default 10 — filters out legitimate large batch orders)
router.get('/', async (req, res) => {
  const supplierId = parseInt(req.query.supplierId, 10);
  const windowDays = Math.min(180, Math.max(1, parseInt(req.query.windowDays, 10) || 30));
  const minPoCount = Math.max(2, parseInt(req.query.minPoCount, 10) || 3);
  const maxAvgQtyRaw = parseFloat(req.query.maxAvgQty);
  const maxAvgQty = Number.isFinite(maxAvgQtyRaw) && maxAvgQtyRaw > 0 ? maxAvgQtyRaw : 10;

  if (!Number.isFinite(supplierId)) {
    return res.status(400).json({ error: 'supplierId query param is required' });
  }

  try {
    const pool = req.app.locals.pool;

    const { rows } = await pool.query(
      `
      WITH po_window AS (
        SELECT
          po.pid,
          po.po_id,
          po.date,
          po.ordered,
          po.po_cost_price,
          po.status
        FROM purchase_orders po
        WHERE po.supplier_id = $1
          AND po.date >= NOW() - ($2::int * INTERVAL '1 day')
          AND po.status <> 'canceled'
      ),
      po_agg AS (
        SELECT
          pid,
          COUNT(*)::int AS po_line_count,
          COUNT(DISTINCT date::date)::int AS po_days_active,
          SUM(ordered)::int AS po_total_units,
          AVG(ordered::numeric)::numeric(10,2) AS po_avg_qty,
          MIN(ordered)::int AS po_min_qty,
          MAX(ordered)::int AS po_max_qty,
          MIN(date)::date AS first_po_date,
          MAX(date)::date AS last_po_date,
          SUM(ordered * po_cost_price)::numeric(14,2) AS po_total_cost,
          -- Count of PO lines still in an open status (not yet received / closed)
          COUNT(*) FILTER (WHERE status = ANY($5::text[]))::int AS po_open_line_count
        FROM po_window
        GROUP BY pid
        HAVING COUNT(*) >= $3
           AND AVG(ordered::numeric) <= $4
      ),
      recv_window AS (
        SELECT
          pid,
          COUNT(*)::int AS recv_line_count,
          COUNT(DISTINCT received_date::date)::int AS recv_days_active,
          SUM(qty_each)::int AS recv_total_units,
          MAX(received_date)::date AS last_recv_date
        FROM receivings
        WHERE supplier_id = $1
          AND received_date >= NOW() - ($2::int * INTERVAL '1 day')
          AND status IN ('partial_received', 'full_received', 'paid')
        GROUP BY pid
      ),
      forecast_agg AS (
        SELECT
          pid,
          SUM(forecast_units) FILTER (
            WHERE forecast_date >= CURRENT_DATE
              AND forecast_date < CURRENT_DATE + INTERVAL '30 days'
          )::numeric(10,2) AS forecast_30d,
          SUM(forecast_units) FILTER (
            WHERE forecast_date >= CURRENT_DATE
              AND forecast_date < CURRENT_DATE + INTERVAL '60 days'
          )::numeric(10,2) AS forecast_60d,
          MAX(lifecycle_phase) AS lifecycle_phase
        FROM product_forecasts
        WHERE pid IN (SELECT pid FROM po_agg)
        GROUP BY pid
      )
      SELECT
        pm.pid,
        pm.sku,
        pm.title,
        pm.brand,
        pm.vendor,
        pm.image_url,
        pm.is_visible,
        pm.is_replenishable,
        pm.moq,
        pm.current_stock,
        pm.notions_inv_count,
        pm.on_order_qty,
        pm.sales_30d,
        pm.sales_velocity_daily::numeric(10,3) AS velocity,
        pm.stock_cover_in_days::numeric(10,1) AS cover_days,
        pm.replenishment_units,
        pm.to_order_units,
        pm.avg_lead_time_days,
        pm.lifecycle_phase,
        pm.earliest_expected_date,
        po.po_line_count,
        po.po_days_active,
        po.po_total_units,
        po.po_avg_qty,
        po.po_min_qty,
        po.po_max_qty,
        po.first_po_date,
        po.last_po_date,
        po.po_total_cost,
        po.po_open_line_count,
        COALESCE(r.recv_line_count, 0) AS recv_line_count,
        COALESCE(r.recv_days_active, 0) AS recv_days_active,
        COALESCE(r.recv_total_units, 0) AS recv_total_units,
        r.last_recv_date,
        f.forecast_30d,
        f.forecast_60d
      FROM po_agg po
      JOIN product_metrics pm ON pm.pid = po.pid
      LEFT JOIN recv_window r ON r.pid = po.pid
      LEFT JOIN forecast_agg f ON f.pid = po.pid
      ORDER BY po.po_line_count DESC, pm.sales_30d DESC NULLS LAST
      LIMIT 500
      `,
      [supplierId, windowDays, minPoCount, maxAvgQty, OPEN_PO_STATUSES]
    );

    // Supplier-level summary for the filters in effect — independent of the
    // minPoCount / maxAvgQty filters so the user can see the full baseline.
    const { rows: summaryRows } = await pool.query(
      `
      SELECT
        COUNT(*)::int AS total_po_lines,
        COUNT(DISTINCT po_id)::int AS distinct_po_ids,
        COUNT(DISTINCT pid)::int AS distinct_products,
        COUNT(DISTINCT date::date)::int AS distinct_days,
        SUM(ordered)::int AS total_units,
        AVG(ordered::numeric)::numeric(10,2) AS avg_qty_per_line
      FROM purchase_orders
      WHERE supplier_id = $1
        AND date >= NOW() - ($2::int * INTERVAL '1 day')
        AND status <> 'canceled'
      `,
      [supplierId, windowDays]
    );

    const results = rows.map((row) => {
      const poAvg = parseFloat(row.po_avg_qty) || 0;
      const velocity = parseFloat(row.velocity) || 0;
      const forecast30 = parseFloat(row.forecast_30d) || 0;
      const poCount = parseInt(row.po_line_count, 10) || 0;
      const poDays = parseInt(row.po_days_active, 10) || 0;

      // Repeat score: higher = more consolidation opportunity.
      // - poCount boosts the score
      // - inversely weighted by avg qty (small buys = bigger problem)
      // - boosted by velocity (real demand, not noise)
      const repeatScore = poAvg > 0
        ? Number(((poCount * Math.max(velocity, 0.1)) / poAvg).toFixed(2))
        : 0;

      return {
        pid: row.pid,
        sku: row.sku,
        title: row.title,
        brand: row.brand,
        vendor: row.vendor,
        imageUrl: row.image_url,
        isVisible: row.is_visible,
        isReplenishable: row.is_replenishable,
        moq: row.moq,

        currentStock: row.current_stock,
        notionsInvCount: row.notions_inv_count,
        onOrderQty: row.on_order_qty,
        sales30d: row.sales_30d,
        velocity,
        coverDays: row.cover_days !== null ? parseFloat(row.cover_days) : null,
        replenishmentUnits: row.replenishment_units,
        toOrderUnits: row.to_order_units,
        avgLeadTimeDays: row.avg_lead_time_days,
        lifecyclePhase: row.lifecycle_phase,
        earliestExpectedDate: row.earliest_expected_date,

        poLineCount: poCount,
        poDaysActive: poDays,
        poTotalUnits: row.po_total_units,
        poAvgQty: poAvg,
        poMinQty: row.po_min_qty,
        poMaxQty: row.po_max_qty,
        firstPoDate: row.first_po_date,
        lastPoDate: row.last_po_date,
        poTotalCost: parseFloat(row.po_total_cost) || 0,
        poOpenLineCount: row.po_open_line_count,

        recvLineCount: row.recv_line_count,
        recvDaysActive: row.recv_days_active,
        recvTotalUnits: row.recv_total_units,
        lastRecvDate: row.last_recv_date,

        forecast30d: forecast30,
        forecast60d: parseFloat(row.forecast_60d) || 0,

        repeatScore,
      };
    });

    const summary = summaryRows[0] || {};

    res.json({
      params: {
        supplierId,
        windowDays,
        minPoCount,
        maxAvgQty,
      },
      supplierSummary: {
        totalPoLines: summary.total_po_lines || 0,
        distinctPoIds: summary.distinct_po_ids || 0,
        distinctProducts: summary.distinct_products || 0,
        distinctDays: summary.distinct_days || 0,
        totalUnits: summary.total_units || 0,
        avgQtyPerLine: parseFloat(summary.avg_qty_per_line) || 0,
      },
      matchSummary: {
        matchCount: results.length,
        totalSmallPoLines: results.reduce((sum, r) => sum + r.poLineCount, 0),
        totalSmallPoUnits: results.reduce((sum, r) => sum + (r.poTotalUnits || 0), 0),
        totalSales30d: results.reduce((sum, r) => sum + (r.sales30d || 0), 0),
        totalSuggestedUnits: results.reduce((sum, r) => sum + (r.replenishmentUnits || 0), 0),
      },
      results,
    });
  } catch (error) {
    console.error('Error fetching repeat orders:', error);
    res.status(500).json({ error: 'Failed to fetch repeat orders' });
  }
});

// GET /api/repeat-orders/suppliers
// Returns the list of suppliers that have had non-canceled PO activity recently,
// so the UI can offer a supplier dropdown without an expensive scan.
router.get('/suppliers', async (req, res) => {
  const windowDays = Math.min(365, Math.max(7, parseInt(req.query.windowDays, 10) || 90));

  try {
    const pool = req.app.locals.pool;
    const { rows } = await pool.query(
      `
      SELECT
        supplier_id,
        MAX(vendor) AS vendor_name,
        COUNT(*)::int AS line_count,
        COUNT(DISTINCT po_id)::int AS po_count,
        MAX(date)::date AS last_po_date
      FROM purchase_orders
      WHERE supplier_id IS NOT NULL
        AND date >= NOW() - ($1::int * INTERVAL '1 day')
        AND status <> 'canceled'
      GROUP BY supplier_id
      HAVING COUNT(*) >= 5
      ORDER BY line_count DESC
      `,
      [windowDays]
    );

    res.json({
      suppliers: rows.map((r) => ({
        supplierId: r.supplier_id,
        vendorName: r.vendor_name,
        lineCount: r.line_count,
        poCount: r.po_count,
        lastPoDate: r.last_po_date,
      })),
    });
  } catch (error) {
    console.error('Error fetching repeat-order suppliers:', error);
    res.status(500).json({ error: 'Failed to fetch suppliers' });
  }
});

// GET /api/repeat-orders/:pid/history
// Returns the full PO + receiving history within the window for a single product,
// for drill-down detail when the user clicks a row.
router.get('/:pid/history', async (req, res) => {
  const pid = parseInt(req.params.pid, 10);
  const supplierId = parseInt(req.query.supplierId, 10);
  const windowDays = Math.min(365, Math.max(1, parseInt(req.query.windowDays, 10) || 30));

  if (!Number.isFinite(pid) || !Number.isFinite(supplierId)) {
    return res.status(400).json({ error: 'pid and supplierId are required' });
  }

  try {
    const pool = req.app.locals.pool;

    const { rows: poRows } = await pool.query(
      `
      SELECT
        po_id,
        date,
        expected_date,
        ordered,
        po_cost_price,
        status,
        notes
      FROM purchase_orders
      WHERE pid = $1
        AND supplier_id = $2
        AND date >= NOW() - ($3::int * INTERVAL '1 day')
      ORDER BY date DESC
      `,
      [pid, supplierId, windowDays]
    );

    const { rows: recvRows } = await pool.query(
      `
      SELECT
        receiving_id,
        received_date,
        qty_each,
        cost_each,
        status
      FROM receivings
      WHERE pid = $1
        AND supplier_id = $2
        AND received_date >= NOW() - ($3::int * INTERVAL '1 day')
      ORDER BY received_date DESC
      `,
      [pid, supplierId, windowDays]
    );

    res.json({
      pid,
      supplierId,
      purchaseOrders: poRows.map((r) => ({
        poId: r.po_id,
        date: r.date,
        expectedDate: r.expected_date,
        ordered: r.ordered,
        costPrice: parseFloat(r.po_cost_price) || 0,
        status: r.status,
        notes: r.notes,
      })),
      receivings: recvRows.map((r) => ({
        receivingId: r.receiving_id,
        receivedDate: r.received_date,
        qtyEach: r.qty_each,
        costEach: parseFloat(r.cost_each) || 0,
        status: r.status,
      })),
    });
  } catch (error) {
    console.error('Error fetching repeat-order history:', error);
    res.status(500).json({ error: 'Failed to fetch product history' });
  }
});

export default router;
