// Page 2 — Per-product detail. Shows yesterday/today KPIs (sales, requisitions, stock),
// a 30-day-default historical chart with forecast overlay, and the order modal.
//
// Re-queries /api/product-detail whenever the date range changes.

const { useState, useMemo, useEffect, useCallback } = React;

const fmtDate = (d) => d.toISOString().slice(0, 10);

const ProductDetailView = ({
  M,
  product,
  branch,
  onBack,
  orderedToday,
  pendingMap,
  onPendingChange,
  upcomingFestivals,
  tweaks,
  onTweakChange,
}) => {
  const run_date = M.run_date;
  const run_date_obj = new Date(run_date);
  const moq = product?.master?.minimumOrderQuantity || 0;
  const submitted = orderedToday.has(product.name);
  const pendingQty = pendingMap[product.name] || 0;

  // Date range: default = last 30 days through run_date (inclusive)
  const startDefault = new Date(run_date_obj);
  startDefault.setDate(startDefault.getDate() - 30);
  const [dateRange, setDateRange] = useState({
    start: fmtDate(startDefault),
    end: run_date,
  });

  // Detail data fetched per (product, branch, dateRange)
  const [detail, setDetail] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    let cancelled = false;
    setLoading(true);
    setError(null);
    window.fetchProductDetail({
      product: product.name,
      branch,
      start: dateRange.start,
      end: dateRange.end,
    })
      .then(d => { if (!cancelled) setDetail(d); })
      .catch(e => { if (!cancelled) setError(e.message); })
      .finally(() => { if (!cancelled) setLoading(false); });
    return () => { cancelled = true; };
  }, [product.name, branch, dateRange.start, dateRange.end]);

  // Series toggles + confidence selection. `bandSelection` is an array of
  // keys like ["upper_80", "lower_60"] — one per checkbox the user has on.
  // The chart pairs same-level upper+lower into a single coloured band; the
  // rest get their own colours.
  const [series, setSeries] = useState({ forecast: true, actual: true, inventory: true, requisitions: true });
  const [bandSelection, setBandSelection] = useState([]);
  const bandLevels = M.bandLevels || { upper: [], lower: [] };

  // ---- Build date-indexed chart data from server response ----
  const chartData = useMemo(() => {
    if (!detail) return [];

    const salesByDate = {};
    detail.sales.forEach(s => { salesByDate[s.date] = s.quantity; });
    const invByDate = {};
    detail.inventory.forEach(s => { invByDate[s.date] = s.inventory; });
    const reqByDate = {};
    detail.requisitions.forEach(s => { reqByDate[s.date] = s.quantity; });
    const fcByDate = {};
    const recentRun = detail.forecasts[0]?.run_date;
    detail.forecasts.forEach(f => {
      if (f.run_date !== recentRun) return;
      fcByDate[f.date] = f;
    });

    // Walk every day in [start, end] — the date picker is the chart window.
    // Future forecast appears only when the user extends `end` past run_date.
    const allDates = [];
    let d = new Date(dateRange.start);
    const stopD = new Date(dateRange.end);
    while (d <= stopD) {
      allDates.push(fmtDate(d));
      d.setDate(d.getDate() + 1);
    }

    const scenarioMult = tweaks.scenario === 'spike' ? 1.6 : tweaks.scenario === 'low' ? 0.65 : 1;
    const inventoryMult = tweaks.scenario === 'low' ? 0.4 : 1;

    // Defensive client-side floor at 0 — server already does this, but the
    // tweak multipliers below shouldn't be able to introduce negatives either.
    const clamp = (v) => Math.max(0, v);

    return allDates.map(ds => {
      const fc = fcByDate[ds];
      const sales = salesByDate[ds];
      const inv = invByDate[ds];
      const req = reqByDate[ds];
      // bands: per-row dict of every (direction, level) value the server
      // returned. Multiplied by scenarioMult so spike/low scenarios visually
      // affect the confidence shading too.
      const bands = {};
      if (fc?.bands) {
        for (const k in fc.bands) {
          const v = fc.bands[k];
          bands[k] = v != null ? Math.round(clamp(v * scenarioMult)) : null;
        }
      }
      return {
        date: ds,
        is_run_date: ds === run_date,
        isPast: ds < run_date,
        forecast: fc ? Math.round(clamp(fc.predicted * scenarioMult)) : null,
        actual: sales != null ? Math.round(clamp(sales * scenarioMult)) : null,
        inventory: inv != null ? Math.round(clamp(inv * inventoryMult)) : null,
        requisitions: req != null ? Math.round(clamp(req)) : null,
        bands,
      };
    });
  }, [detail, dateRange.start, tweaks.scenario, run_date]);

  // ---- KPIs (yesterday_date / run_date) ----
  const kpis = useMemo(() => {
    if (!detail) return null;
    const yesterday_date_obj = new Date(run_date_obj);
    yesterday_date_obj.setDate(yesterday_date_obj.getDate() - 1);
    const yesterday_date = fmtDate(yesterday_date_obj);

    const clamp = (v) => v == null ? v : Math.max(0, v);
    const find = (arr, dStr) => arr.find(r => r.date === dStr);
    const yesterday_date_sales = clamp(find(detail.sales, yesterday_date)?.quantity ?? 0);
    const yesterday_date_req = clamp(find(detail.requisitions, yesterday_date)?.quantity ?? 0);
    const run_date_sales = clamp(find(detail.sales, run_date)?.quantity ?? 0);
    const run_date_stock = clamp(find(detail.inventory, run_date)?.inventory ?? null);

    // Today's pipeline run output (predicts the NEXT day, given the schema's
    // invoicedate == run_date + 1 convention). Null when today's pipeline run
    // hasn't been written yet.
    const trf = detail.today_run_forecast || null;
    const yrf = detail.yesterday_run_forecast || null;
    const run_date_pred = trf?.predicted ?? null;
    const run_date_upper_80 = trf?.bands?.upper_80 ?? null;
    const run_date_lower_80 = trf?.bands?.lower_80 ?? null;

    // Accuracy: strict two-step lookup, no further fallback. Driven by the
    // pipeline's run_date (not invoicedate) so the label reflects when the
    // model actually computed the metric.
    //   1. today's pipeline run    → Accuracy under "Today"
    //   2. else yesterday's run    → Accuracy under "Yesterday"
    //   3. else (older / missing)  → KPI hidden entirely
    let run_date_accuracy = null;
    let run_date_accuracy_label = null;
    if (trf?.rolling_accuracy_30d != null) {
      run_date_accuracy = trf.rolling_accuracy_30d;
      run_date_accuracy_label = 'Today';
    } else if (yrf?.rolling_accuracy_30d != null) {
      run_date_accuracy = yrf.rolling_accuracy_30d;
      run_date_accuracy_label = 'Yesterday';
    }

    return {
      yesterday_date,
      yesterday_date_sales,
      yesterday_date_req,
      run_date_sales,
      run_date_stock,
      run_date_pred,
      run_date_upper_80,
      run_date_lower_80,
      run_date_accuracy,
      run_date_accuracy_label,
    };
  }, [detail, run_date]);

  // Festivals to mark on the chart (within visible range)
  const chartFestivals = useMemo(() => {
    if (!chartData.length) return [];
    const startStr = chartData[0].date;
    const endStr = chartData[chartData.length - 1].date;
    return M.festivals
      .filter(f => f.is_festival_day === 1 && f.date >= startStr && f.date <= endStr)
      .map(f => {
        const name = M.festivalCols.find(c => f[c] === 1) || 'Festival';
        return { date: f.date, name };
      })
      .sort((a, b) => a.date.localeCompare(b.date));
  }, [chartData, M.festivals, M.festivalCols]);

  const isEmpty = !loading && chartData.every(d =>
    d.forecast == null && d.actual == null && d.inventory == null && d.requisitions == null
  );

  // CSV export
  const downloadCSV = () => {
    const headers = ['Date'];
    if (series.forecast) headers.push('Forecast');
    const bandKeys = series.forecast ? bandSelection : [];
    bandKeys.forEach(k => headers.push(k.replace(/^./, c => c.toUpperCase())));
    if (series.actual) headers.push('Actual');
    if (series.requisitions) headers.push('Requisitions');
    if (series.inventory) headers.push('Inventory');
    const rows = chartData.map(d => {
      const r = [d.date];
      if (series.forecast) r.push(d.forecast ?? '');
      bandKeys.forEach(k => r.push(d.bands?.[k] ?? ''));
      if (series.actual) r.push(d.actual ?? '');
      if (series.requisitions) r.push(d.requisitions ?? '');
      if (series.inventory) r.push(d.inventory ?? '');
      return r.join(',');
    });
    const csv = [headers.join(','), ...rows].join('\n');
    const blob = new Blob([csv], { type: 'text/csv' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url; a.download = `${product.name}-${branch}-${dateRange.start}-${dateRange.end}.csv`;
    a.click(); URL.revokeObjectURL(url);
  };

  // ---- Pending-order panel state ----
  // Local input state lets user type freely; commit-on-Set/Enter writes to localStorage.
  const [qtyInput, setQtyInput] = useState(String(pendingQty || ''));
  // Reset the input when navigating to a different product/branch or after
  // an external change (e.g. user edited it on Page 1, comes back here).
  useEffect(() => { setQtyInput(String(pendingQty || '')); }, [pendingQty, product.name, branch]);

  const inputAsNum = Math.max(0, parseInt(qtyInput) || 0);
  const inputActual = window.roundUpToMoq
    ? window.roundUpToMoq(inputAsNum, moq)
    : (moq > 1 && inputAsNum > 0 ? Math.ceil(inputAsNum / moq) * moq : inputAsNum);
  const dirty = inputAsNum !== pendingQty;

  const commitQty = (qty) => {
    window.setPendingQty({ run_date, branch, product: product.name, quantity: qty });
    onPendingChange();
  };
  const handleSet = () => {
    if (submitted) return;
    commitQty(inputAsNum);
  };
  const handleRemove = () => {
    if (submitted) return;
    setQtyInput('');
    commitQty(0);
  };

  return (
    <main className="max-w-[1600px] mx-auto px-4 sm:px-6 py-5">
      {/* Back + breadcrumb */}
      <div className="flex items-center gap-2 mb-3 text-sm">
        <button
          onClick={onBack}
          className="inline-flex items-center gap-1.5 px-2.5 py-1 -ml-2.5 text-slate-600 hover:text-slate-900 hover:bg-slate-100 rounded-md transition"
        >
          <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
            <path d="M9 3L5 7l4 4" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
          </svg>
          All products
        </button>
        <span className="text-slate-300">/</span>
        <span className="text-slate-900 font-medium truncate">{product.name}</span>
        <span className="text-slate-300">·</span>
        <span className="text-slate-500">{branch}</span>
      </div>

      {/* Product info strip — static attributes from products_master */}
      <div className="grid grid-cols-3 gap-3 mb-3">
        <MasterChip
          label="Shelf Life"
          value={detail?.master?.shelfLife}
          unit="days"
          icon={<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><circle cx="7" cy="7" r="5.5" stroke="currentColor" strokeWidth="1.3"/><path d="M7 4v3l2 1.5" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round"/></svg>}
        />
        <MasterChip
          label="Min Order Qty"
          value={detail?.master?.minimumOrderQuantity}
          unit="units"
          icon={<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 3h10l-1 7H3L2 3z" stroke="currentColor" strokeWidth="1.3" strokeLinejoin="round"/></svg>}
        />
        <MasterChip
          label="Safety Stock"
          value={detail?.master?.safetyStockCapacity}
          unit="units"
          icon={<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M7 1l5 2v4c0 3-2.5 5-5 6-2.5-1-5-3-5-6V3l5-2z" stroke="currentColor" strokeWidth="1.3" strokeLinejoin="round"/></svg>}
        />
      </div>

      {/* KPI strip — 2 labeled sections (Yesterday / Today) with a vertical
          divider. The Accuracy KPI slots into whichever section matches the
          date its data is from. Section header always shows the section's
          canonical date. */}
      {(() => {
        const accuracy_in_today = kpis?.run_date_accuracy_label === 'Today';
        const accuracy_in_yesterday = kpis?.run_date_accuracy_label === 'Yesterday';
        const accuracyKpi = (
          <DetailKpi
            label="Accuracy (30d)"
            value={kpis?.run_date_accuracy == null ? '—' : `${kpis.run_date_accuracy.toFixed(1)}%`}
            accent="green"
          />
        );
        return (
          <div className="grid grid-cols-1 lg:grid-cols-7 gap-4 mb-4">
            {/* Yesterday */}
            <div className={accuracy_in_yesterday ? 'lg:col-span-3' : 'lg:col-span-2'}>
              <KpiSectionHeader
                label="Yesterday"
                date={kpis ? kpis.yesterday_date : null}
              />
              <div className={`grid ${accuracy_in_yesterday ? 'grid-cols-3' : 'grid-cols-2'} gap-3 mt-2`}>
                <DetailKpi
                  label="Sales"
                  value={kpis ? Math.round(kpis.yesterday_date_sales).toLocaleString() : '—'}
                  accent="slate"
                />
                <DetailKpi
                  label="Requisition"
                  value={kpis ? Math.round(kpis.yesterday_date_req).toLocaleString() : '—'}
                  accent="violet"
                />
                {accuracy_in_yesterday && accuracyKpi}
              </div>
            </div>

            {/* Today */}
            <div className={`${accuracy_in_today ? 'lg:col-span-5' : 'lg:col-span-4'} lg:border-l lg:border-slate-200 lg:pl-4`}>
              <KpiSectionHeader label="Today" date={run_date} />
              <div className={`grid grid-cols-2 ${accuracy_in_today ? 'lg:grid-cols-5' : 'lg:grid-cols-4'} gap-3 mt-2`}>
                <DetailKpi
                  label="Sales"
                  value={kpis ? Math.round(kpis.run_date_sales).toLocaleString() : '—'}
                  accent="green"
                />
                <DetailKpi
                  label="Stock"
                  value={kpis ? (kpis.run_date_stock == null ? '—' : Math.round(kpis.run_date_stock).toLocaleString()) : '—'}
                  accent="amber"
                />
                <DetailKpi
                  label="Next-Day Forecast"
                  value={kpis?.run_date_pred != null ? Math.round(kpis.run_date_pred).toLocaleString() : '—'}
                  accent="blue"
                />
                <DetailKpi
                  label="Next-Day 80% Band"
                  value={
                    kpis?.run_date_lower_80 != null && kpis?.run_date_upper_80 != null
                      ? `${Math.round(kpis.run_date_lower_80).toLocaleString()} – ${Math.round(kpis.run_date_upper_80).toLocaleString()}`
                      : kpis?.run_date_upper_80 != null
                        ? `↑ ${Math.round(kpis.run_date_upper_80).toLocaleString()}`
                        : kpis?.run_date_lower_80 != null
                          ? `↓ ${Math.round(kpis.run_date_lower_80).toLocaleString()}`
                          : '—'
                  }
                  accent="blue"
                />
                {accuracy_in_today && accuracyKpi}
              </div>
            </div>
          </div>
        );
      })()}

      {/* Left column (filter + chart, col-span-7) | Right column (festivals +
          order qty, col-span-3). Filter bar lives inside the left column so
          its width matches the chart. */}
      <div className="grid grid-cols-1 lg:grid-cols-10 gap-5">
        <div className="lg:col-span-7 space-y-4">
          {/* Filters — date / series / confidence (Order Qty + CSV moved out) */}
          <div className="bg-white border border-slate-200 rounded-lg p-3">
            <div className="flex flex-wrap items-end gap-3">
              <DateRange start={dateRange.start} end={dateRange.end} onChange={setDateRange} />
              <SeriesToggles series={series} onChange={setSeries} />
              <ConfidenceControls
                bandLevels={bandLevels}
                selection={bandSelection}
                onChange={setBandSelection}
                disabled={!series.forecast}
              />
            </div>
          </div>

          <div className="bg-white border border-slate-200 rounded-lg overflow-hidden">
            <div className="px-5 py-4 border-b border-slate-100 flex items-start justify-between gap-3">
              <div className="min-w-0">
                <h2 className="text-base font-semibold text-slate-900">Daily Forecast vs Actuals</h2>
                <p className="text-xs text-slate-500 mt-0.5">
                  {new Date(dateRange.start).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}
                  {' - '}
                  {new Date(dateRange.end).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}
                  <span className="mx-1.5 text-slate-300">·</span>
                  <span className="text-slate-700 font-medium">{product.name}</span>
                  <span className="mx-1.5 text-slate-300">·</span>
                  {branch}
                </p>
              </div>
              <button
                onClick={downloadCSV}
                disabled={!detail || isEmpty}
                className="shrink-0 inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-slate-700 bg-white border border-slate-200 hover:bg-slate-50 disabled:opacity-50 disabled:cursor-not-allowed rounded-md transition"
              >
                <svg width="13" height="13" viewBox="0 0 14 14" fill="none"><path d="M7 1v8m0 0L4 6m3 3l3-3M2 11v1a1 1 0 001 1h8a1 1 0 001-1v-1" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/></svg>
                CSV
              </button>
            </div>
            <div className="p-5">
              {loading ? (
                <div className="py-16 text-center text-sm text-slate-500">Loading…</div>
              ) : error ? (
                <div className="py-16 text-center">
                  <p className="text-sm font-medium text-red-700">Failed to load</p>
                  <p className="text-xs text-slate-500 mt-1 break-all">{error}</p>
                </div>
              ) : isEmpty ? (
                <div className="py-16 text-center">
                  <div className="inline-flex items-center justify-center w-12 h-12 rounded-full bg-slate-50 text-slate-300 mb-3">
                    <svg width="24" height="24" viewBox="0 0 24 24" fill="none"><path d="M4 18l5-5 4 4 7-8" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/></svg>
                  </div>
                  <p className="text-sm font-medium text-slate-900">No data for this product / branch / range</p>
                  <p className="text-xs text-slate-500 mt-1">Try widening the date range.</p>
                </div>
              ) : (
                <Chart
                  data={chartData}
                  series={series}
                  bandSelection={series.forecast ? bandSelection : []}
                  festivals={chartFestivals}
                />
              )}
            </div>
          </div>
        </div>

        {/* Right sidebar — Festivals expands (flex-1) to fill the column,
            Order Qty sits at the bottom. Grid stretch makes the column match
            the left column's total height (filter + chart), so Order Qty's
            bottom lines up with the chart's bottom. */}
        <div className="lg:col-span-3 flex flex-col gap-4">
          <div className="flex-1 min-h-0">
            <FestivalsPanel festivals={upcomingFestivals} run_date={run_date} />
          </div>

          <div className="bg-white border border-slate-200 rounded-lg p-5">
            <div className="text-[11px] font-semibold uppercase tracking-wider text-slate-500 mb-3">
              Order Quantity
            </div>
            {submitted ? (
              <div className="inline-flex items-center gap-2 px-4 py-2.5 rounded-md text-sm font-medium bg-emerald-50 text-emerald-700 border border-emerald-200">
                <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
                  <path d="M3 7l3 3 5-5" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/>
                </svg>
                Sent
              </div>
            ) : (
              <>
                <div className="flex items-stretch gap-2 bg-white border border-slate-200 rounded-lg p-1.5 hover:border-slate-300 focus-within:ring-2 focus-within:ring-red-500/20 focus-within:border-red-500 transition">
                  <input
                    type="number"
                    min="0"
                    value={qtyInput}
                    onChange={(e) => setQtyInput(e.target.value)}
                    onKeyDown={(e) => { if (e.key === 'Enter') { handleSet(); e.target.blur(); } }}
                    placeholder="0"
                    className="flex-1 min-w-0 px-3 py-2 text-2xl font-semibold text-right tabular-nums text-slate-900 border-0 bg-transparent focus:outline-none"
                  />
                  <button
                    onClick={handleSet}
                    disabled={!dirty}
                    className="shrink-0 px-4 text-sm font-semibold text-white bg-red-600 hover:bg-red-700 disabled:bg-slate-200 disabled:text-slate-400 disabled:cursor-not-allowed rounded-md transition"
                  >
                    {pendingQty > 0 ? 'Update' : 'Set'}
                  </button>
                </div>
                {moq > 1 && inputAsNum > 0 && (
                  <div className="mt-2.5 text-xs text-slate-500 tabular-nums">
                    MOQ rounded →{' '}
                    <span className={`font-semibold ${inputActual !== inputAsNum ? 'text-amber-700' : 'text-slate-700'}`}>
                      {inputActual.toLocaleString()}
                    </span>
                  </div>
                )}
                {pendingQty > 0 && (
                  <button
                    onClick={handleRemove}
                    className="mt-2 inline-flex items-center gap-1 text-xs text-slate-400 hover:text-red-600 hover:bg-red-50 px-2 py-1 -ml-2 rounded transition"
                  >
                    <svg width="12" height="12" viewBox="0 0 14 14" fill="none">
                      <path d="M3 3l8 8M11 3l-8 8" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round"/>
                    </svg>
                    Remove
                  </button>
                )}
              </>
            )}
          </div>
        </div>
      </div>
    </main>
  );
};

const KPI_ACCENTS = {
  slate:  { dot: 'bg-slate-400',   text: 'text-slate-700' },
  green:  { dot: 'bg-emerald-500', text: 'text-emerald-700' },
  amber:  { dot: 'bg-amber-500',   text: 'text-amber-700' },
  violet: { dot: 'bg-violet-500',  text: 'text-violet-700' },
  blue:   { dot: 'bg-blue-500',    text: 'text-blue-700' },
  red:    { dot: 'bg-red-500',     text: 'text-red-700' },
};

const DetailKpi = ({ label, sub, value, accent = 'slate' }) => {
  const a = KPI_ACCENTS[accent] || KPI_ACCENTS.slate;
  return (
    <div className="bg-white border border-slate-200 rounded-lg px-4 py-3">
      <div className="flex items-center gap-1.5 text-[11px] font-medium text-slate-500 uppercase tracking-wide">
        <span className={`inline-block w-1.5 h-1.5 rounded-full ${a.dot}`} />
        {label}
      </div>
      <div className="mt-2 flex items-baseline gap-2">
        <span className="text-2xl font-semibold tabular-nums text-slate-900">{value}</span>
        {sub && <span className={`text-xs font-medium ${a.text}`}>{sub}</span>}
      </div>
    </div>
  );
};

// Section header shown above each KPI group. The date is optional — Model
// section omits it when no accuracy row was found at all.
const KpiSectionHeader = ({ label, date }) => (
  <div className="flex items-baseline gap-2">
    <span className="text-[11px] font-semibold uppercase tracking-wider text-slate-700">{label}</span>
    {date && (
      <span className="text-[11px] font-medium text-slate-400 tabular-nums">
        {new Date(date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}
      </span>
    )}
  </div>
);

const MasterChip = ({ label, value, unit, icon }) => (
  <div className="bg-white border border-slate-200 rounded-lg px-3 py-2 flex items-center gap-2.5">
    <div className="text-slate-400 shrink-0">{icon}</div>
    <div className="min-w-0 flex-1 flex items-baseline justify-between gap-2">
      <span className="text-[11px] font-medium text-slate-500 uppercase tracking-wide truncate">{label}</span>
      <span className="text-sm font-semibold tabular-nums text-slate-900 whitespace-nowrap">
        {value == null ? '—' : `${Math.max(0, value).toLocaleString()} ${unit}`}
      </span>
    </div>
  </div>
);

window.ProductDetailView = ProductDetailView;
