/* Lager: Leveransrisk (redesign 2026-09).
 *
 * Datakällor (bara läsande GET):
 *   /v1/admin/reports/atp?days=N          leveransrisk per artikel (src/routes/admin.reports.ts)
 *   /v1/admin/reports/inventory           Shopify + Ongoing-saldo och nästa inleverans
 *   /v1/admin/reports/sku-performance     artikelstatus: saldo, dagar kvar
 *   /v1/admin/ongoing/diagnostics         när Ongoing-data senast uppdaterades
 *
 * Inköpsutkast skapas bara i webbläsaren (text och CSV). Ingenting skickas.
 */

const RX_STK_HORIZONS = [30, 60, 90, 180];
const RX_STK_PAGE = 50;

/** Lägsta löpande saldo under horisonten (negativt = brist). */
function rxStkMinBalance(s) {
  let min = Number(s.currentStock) || 0;
  (s.dailyEvents || []).forEach(e => { if (e.runningBalance < min) min = e.runningBalance; });
  return min;
}

function rxStkCoverage(s) {
  const need = Number(s.committedOrders) || 0;
  if (need <= 0) return null;
  const have = Math.max(0, Number(s.currentStock) || 0) + (Number(s.inboundExpected) || 0);
  return Math.round((have / need) * 100);
}

function rxStkSigned(n) {
  if (n == null) return '';
  return n > 0 ? `+${fmtInt(n)}` : fmtInt(n);
}

/**
 * Datakvalitetsflaggor, grupperade per artikelnummer.
 * Returnerar [{ sku, title, items: [{ short, text, target }] }].
 */
function rxStkBuildFlags(atpSkus, inv, perf) {
  const bySku = new Map();
  const seen = new Set();
  const today = rxToday();
  const add = (sku, title, short, text, target) => {
    const k = `${sku}|${short}`;
    if (seen.has(k)) return;
    seen.add(k);
    let g = bySku.get(sku);
    if (!g) { g = { sku, title: title || sku, items: [] }; bySku.set(sku, g); }
    if (!g.title || g.title === sku) g.title = title || sku;
    g.items.push({ short, text, target });
  };
  const atpBySku = new Map((atpSkus || []).map(s => [s.sku, s]));

  (atpSkus || []).forEach(s => {
    if (s.currentStock < 0) {
      add(s.sku, s.title, 'Negativt saldo i Ongoing',
        `Disponibelt saldo i Ongoing är ${fmtInt(s.currentStock)} st. Negativt saldo är troligen ett datafel, så bristen kan vara fel.`,
        'reports/inventory');
    }
  });

  (inv?.variants || []).forEach(v => {
    if (!v.sku) return;
    const title = v.productTitle + (v.variantTitle && v.variantTitle !== 'Default Title' ? ` ${v.variantTitle}` : '');
    if (v.shopifyQty != null && v.shopifyQty < 0) {
      add(v.sku, title, 'Negativt Shopify-saldo',
        `Shopify-saldo är ${fmtInt(v.shopifyQty)} st.`, 'reports/inventory');
    }
    if (v.wmsQtyReserved != null && v.wmsQtyReserved < 0) {
      add(v.sku, title, 'Negativt reserverat',
        `Reserverat i Ongoing är ${fmtInt(v.wmsQtyReserved)} st, vilket inte kan stämma.`, 'reports/inventory');
    }
    if (v.wmsQtyPhysical != null && v.wmsQtyPhysical < 0) {
      add(v.sku, title, 'Negativt lagersaldo',
        `Fysiskt lagersaldo i Ongoing är ${fmtInt(v.wmsQtyPhysical)} st.`, 'reports/inventory');
    }
    const nb = v.nextInbound;
    if (nb && nb.expectedDate) {
      const diff = rxDayDiff(today, nb.expectedDate);
      if (diff != null && diff < 0) {
        add(v.sku, title, 'Passerat inleveransdatum',
          `Inleverans ${rxStkSigned(nb.qtyIncoming)} st med datum ${rxDay(nb.expectedDate)} har passerat utan att vara inlevererad. Den räknas inte i leveransrisk.`,
          'reports/inventory');
      }
    }
    const a = atpBySku.get(v.sku);
    if (a && a.inboundExpected > 0) {
      const rest = nb ? Number(nb.qtyIncoming) || 0 : 0;
      if (a.inboundExpected > rest) {
        add(v.sku, title, 'Inleverans kan vara dubbelräknad',
          `Leveransrisk räknar med inleverans ${fmtInt(a.inboundExpected)} st, men Saldo visar ${fmtInt(rest)} st kvar att leverera. Redan mottagna rader kan räknas igen.`,
          'reports/inventory');
      }
    }
  });

  (perf?.skus || []).forEach(p => {
    if (p.daysOfStock != null && p.daysOfStock < 0) {
      const a = atpBySku.get(p.sku);
      const cmp = a && a.currentStock !== p.currentStock ? ` Leveransrisk räknar med ${fmtInt(a.currentStock)} st.` : '';
      add(p.sku, p.productTitle, 'Negativa dagar kvar',
        `Artikelstatus visar ${fmtInt(p.currentStock)} st och ${fmtInt(p.daysOfStock)} dagar kvar.${cmp}`,
        'reports/skus');
    }
  });

  return [...bySku.values()].sort((x, y) => y.items.length - x.items.length || String(x.title).localeCompare(String(y.title), 'sv'));
}

function rxStkCsvCell(v) {
  const s = String(v ?? '');
  return /[";\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
}

function rxStkSlug(s) {
  return String(s || 'leverantor').toLowerCase().replace(/[åä]/g, 'a').replace(/ö/g, 'o').replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '') || 'leverantor';
}

function rxStkDraftText(group, asOf, days) {
  const lines = [
    `Utkast till inköpsorder: ${group.name}`,
    `Underlag: leveransrisk ${asOf || ''}, horisont ${days} dagar. Antal = brist som inte täcks av registrerad inleverans.`,
    'Utkastet är inte skickat.',
    '',
  ];
  group.rows.forEach(r => { lines.push(`${r.sku}  ${fmtInt(r.qty)} st  ${r.title}`); });
  lines.push('', `Totalt ${fmtInt(group.units)} st`);
  return lines.join('\n');
}

function rxStkDraftCsv(group) {
  const rows = [['Artikelnummer', 'Benämning', 'Antal']];
  group.rows.forEach(r => rows.push([r.sku, r.title, r.qty]));
  return '﻿' + rows.map(r => r.map(rxStkCsvCell).join(';')).join('\r\n');
}

function rxStkDownload(filename, content) {
  const blob = new Blob([content], { type: 'text/csv;charset=utf-8' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = filename;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  setTimeout(() => URL.revokeObjectURL(url), 1000);
}

/* ── Dagligt förlopp för en artikel ─────────────────────── */
function RxStkDaily({ row, id }) {
  const today = rxToday();
  const events = row.dailyEvents || [];
  const shown = events.filter(e => { const d = rxDayDiff(today, e.date); return d != null && d <= 30; });
  const hidden = events.length - shown.length;
  return <div id={id} style={{ padding: '4px 0 8px 0' }}>
    <div style={{ fontSize: 12.5, color: 'var(--rx-ink-3)', marginBottom: 8 }}>
      Dagligt förlopp, 30 dagar framåt. Startar på disponibelt saldo {fmtInt(row.currentStock)} st.
    </div>
    {shown.length === 0
      ? <div className="rx-muted" style={{ fontSize: 13 }}>Inga utleveranser eller inleveranser de närmaste 30 dagarna.</div>
      : <table className="rx-table" style={{ maxWidth: 520, background: '#fff', border: '1px solid var(--rx-border)', borderRadius: 8 }}>
        <thead><tr>
          <th scope="col">Datum</th>
          <th scope="col" className="rx-num">Utgående</th>
          <th scope="col" className="rx-num">Inleverans</th>
          <th scope="col" className="rx-num">Löpande saldo</th>
        </tr></thead>
        <tbody>
          {shown.map(e => <tr key={e.date}>
            <td>{rxDay(e.date, { relative: true })}</td>
            <td className="rx-num">{e.ordersOut ? fmtInt(e.ordersOut) : <span className="rx-faint">0</span>}</td>
            <td className="rx-num" style={{ color: e.inboundIn ? 'var(--rx-ok)' : undefined }}>{e.inboundIn ? rxStkSigned(e.inboundIn) : <span className="rx-faint">0</span>}</td>
            <td className="rx-num" style={{ fontWeight: 600, color: e.runningBalance < 0 ? 'var(--rx-danger)' : 'var(--rx-ink)' }}>{fmtInt(e.runningBalance)}</td>
          </tr>)}
        </tbody>
      </table>}
    {hidden > 0 && <div className="rx-muted" style={{ fontSize: 12.5, marginTop: 6 }}>{rxPlural(hidden, 'händelse', 'händelser')} efter 30 dagar visas inte.</div>}
  </div>;
}

/* ── Utkast per leverantör ──────────────────────────────── */
function RxStkDraft({ group, asOf, days }) {
  const [copied, setCopied] = React.useState(false);
  const text = rxStkDraftText(group, asOf, days);
  const copy = () => {
    if (!navigator.clipboard) return;
    navigator.clipboard.writeText(text).then(() => { setCopied(true); setTimeout(() => setCopied(false), 2000); }).catch(() => {});
  };
  return <div style={{ display: 'flex', flexDirection: 'column', gap: 8, marginTop: 8 }}>
    <label style={{ fontSize: 12, color: 'var(--rx-ink-3)' }}>
      Utkast för {group.name}
      <textarea readOnly value={text} rows={Math.min(12, group.rows.length + 6)}
        style={{ display: 'block', width: '100%', marginTop: 4, boxSizing: 'border-box', fontFamily: 'var(--font-mono)', fontSize: 12, padding: 8, border: '1px solid var(--rx-border-strong)', borderRadius: 8, background: 'var(--rx-surface-alt)', color: 'var(--rx-ink)', resize: 'vertical' }} />
    </label>
    <div style={{ display: 'flex', gap: 8 }}>
      <button type="button" className="btn" onClick={copy} disabled={!navigator.clipboard}>{I.copy}<span>{copied ? 'Kopierat' : 'Kopiera text'}</span></button>
      <button type="button" className="btn" onClick={() => rxStkDownload(`inkopsutkast-${rxStkSlug(group.name)}-${asOf || 'idag'}.csv`, rxStkDraftCsv(group))}>{I.download}<span>Ladda ner CSV</span></button>
    </div>
  </div>;
}

/* ── Sidan ──────────────────────────────────────────────── */
function RxStockPage({ onNavigate }) {
  const [days, setDays] = React.useState(90);
  const [scope, setScope] = React.useState('all');      // all | week
  const [sortBy, setSortBy] = React.useState('size');   // size | date
  const [vendor, setVendor] = React.useState('');
  const [source, setSource] = React.useState('');       // '' | own | drop
  const [q, setQ] = React.useState('');
  const [open, setOpen] = React.useState(null);
  const [limit, setLimit] = React.useState(RX_STK_PAGE);
  const [draftVendor, setDraftVendor] = React.useState(null);

  const atp = useRxFetch(`/v1/admin/reports/atp?days=${days}`);
  const inv = useRxFetch('/v1/admin/reports/inventory');
  const perf = useRxFetch('/v1/admin/reports/sku-performance?days=90');
  const diag = useRxFetch('/v1/admin/ongoing/diagnostics');

  const go = route => { if (onNavigate) onNavigate(route); };

  // Artikelnummer -> variant i Saldo (för källa och benämning)
  const invBySku = React.useMemo(() => {
    const m = new Map();
    (inv.data?.variants || []).forEach(v => { if (v.sku && !m.has(v.sku)) m.set(v.sku, v); });
    return m;
  }, [inv.data]);

  const flags = React.useMemo(
    () => rxStkBuildFlags(atp.data?.skus || [], inv.data, perf.data),
    [atp.data, inv.data, perf.data]);
  const flagsBySku = React.useMemo(() => new Map(flags.map(f => [f.sku, f])), [flags]);
  const flagCount = flags.reduce((n, f) => n + f.items.length, 0);

  const today = rxToday();
  const rows = React.useMemo(() => {
    return (atp.data?.skus || []).filter(s => s.atRisk).map(s => {
      const v = invBySku.get(s.sku);
      const src = v ? (v.wmsQtySellable != null ? 'own' : 'drop') : null;
      const title = s.title || (v ? v.productTitle : null) || s.sku;
      const riskIn = s.riskDate ? rxDayDiff(today, s.riskDate) : null;
      return { ...s, title, src, minBal: rxStkMinBalance(s), coverage: rxStkCoverage(s), riskIn };
    });
  }, [atp.data, invBySku]);

  const vendors = React.useMemo(() => [...new Set(rows.map(r => r.vendor || ''))].sort((a, b) => a.localeCompare(b, 'sv')), [rows]);

  const filtered = React.useMemo(() => {
    const needle = q.trim().toLowerCase();
    const list = rows.filter(r => {
      if (scope === 'week' && !(r.riskIn != null && r.riskIn <= 7)) return false;
      if (vendor && (r.vendor || '') !== vendor.slice(1)) return false;
      if (source && r.src !== source) return false;
      if (needle && !(`${r.title} ${r.sku}`.toLowerCase().includes(needle))) return false;
      return true;
    });
    list.sort((a, b) => {
      if (sortBy === 'date') return String(a.riskDate || '').localeCompare(String(b.riskDate || '')) || a.minBal - b.minBal;
      return a.minBal - b.minBal || String(a.riskDate || '').localeCompare(String(b.riskDate || ''));
    });
    return list;
  }, [rows, scope, vendor, source, q, sortBy]);

  React.useEffect(() => { setLimit(RX_STK_PAGE); }, [scope, vendor, source, q, sortBy, days]);

  // Inköpsförslag: brist som inte täcks av registrerad inleverans (netAvailable < 0)
  const purchase = React.useMemo(() => {
    const m = new Map();
    rows.forEach(r => {
      const name = r.vendor || 'Leverantör saknas';
      let g = m.get(name);
      if (!g) { g = { name, rows: [], units: 0, lateOnly: 0 }; m.set(name, g); }
      if (r.netAvailable < 0) {
        const qty = -r.netAvailable;
        g.rows.push({ sku: r.sku, title: r.title, qty });
        g.units += qty;
      } else {
        g.lateOnly += 1;
      }
    });
    return [...m.values()].sort((a, b) => b.units - a.units || a.name.localeCompare(b.name, 'sv'));
  }, [rows]);

  const total = atp.data?.skus?.length || 0;
  const weekCount = rows.filter(r => r.riskIn != null && r.riskIn <= 7).length;
  const withInbound = rows.filter(r => r.inboundExpected > 0).length;
  const dropCount = rows.filter(r => r.src === 'drop').length;
  const flagsLoading = inv.loading || perf.loading;

  const selectStyle = { height: 34, padding: '0 10px', border: '1px solid var(--rx-border-strong)', borderRadius: 9, background: '#fff', font: 'inherit', fontSize: 13, color: 'var(--rx-ink-2)' };
  const hiddenLabel = { position: 'absolute', width: 1, height: 1, overflow: 'hidden', clip: 'rect(0 0 0 0)' };

  if (atp.loading && !atp.data) return <RxLoading label="Beräknar leveransrisk…" />;
  if (atp.error) return <RxError error={atp.error} onRetry={atp.reload} />;
  if (!atp.data) return null;

  const snapAt = diag.data?.local?.latestSnapshotUpdate;
  const snapAge = snapAt ? (Date.now() - new Date(snapAt).getTime()) / 36e5 : null;

  return <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
    <section aria-label="Nyckeltal" className="rx-grid rx-grid--4">
      <RxKpi label="Artiklar med brist" tone={rows.length ? 'warn' : 'ok'}
        value={<>{fmtInt(rows.length)} <span style={{ fontSize: 14, fontWeight: 500, color: 'var(--rx-ink-3)' }}>av {fmtInt(total)}</span></>}
        sub={dropCount ? `varav ${fmtInt(dropCount)} direktleverans` : `inom ${days} dagar`} />
      <RxKpi label="Brist inom 7 dagar" value={fmtInt(weekCount)} tone={weekCount ? 'warn' : undefined} sub={`till och med ${rxDay(new Date(today.getTime() + 7 * 864e5))}`} />
      <RxKpi label="Med registrerad inleverans"
        value={<>{fmtInt(withInbound)} <span style={{ fontSize: 14, fontWeight: 500, color: 'var(--rx-ink-3)' }}>av {fmtInt(rows.length)}</span></>}
        sub="bland artiklar med brist" />
      <RxKpi label="Datakvalitetsflaggor" value={flagsLoading ? '…' : fmtInt(flagCount)}
        sub={flagsLoading ? 'jämför med Saldo och Artikelstatus' : `på ${rxPlural(flags.length, 'artikel', 'artiklar')}`} />
    </section>

    <div className="rx-split">
      <div className="rx-split__main">
        <RxFilterRow value={scope} onChange={setScope}
          filters={[{ key: 'all', label: 'Alla med brist', count: rows.length }, { key: 'week', label: 'Brist inom 7 dagar', count: weekCount, tone: weekCount ? 'warn' : undefined }]}>
          <label style={{ position: 'relative' }}>
            <span style={hiddenLabel}>Leverantör</span>
            <select value={vendor} onChange={e => setVendor(e.target.value)} style={selectStyle}>
              <option value="">Alla leverantörer</option>
              {vendors.map(v => <option key={v || '-'} value={`=${v}`}>{v || 'Leverantör saknas'}</option>)}
            </select>
          </label>
          <label style={{ position: 'relative' }}>
            <span style={hiddenLabel}>Lagerställe</span>
            <select value={source} onChange={e => setSource(e.target.value)} style={selectStyle} disabled={!inv.data}>
              <option value="">Eget lager och direktleverans</option>
              <option value="own">Eget lager (Ongoing)</option>
              <option value="drop">Direktleverans</option>
            </select>
          </label>
          <label style={{ position: 'relative' }}>
            <span style={hiddenLabel}>Horisont</span>
            <select value={days} onChange={e => { setDays(Number(e.target.value)); setOpen(null); }} style={selectStyle}>
              {RX_STK_HORIZONS.map(d => <option key={d} value={d}>{d} dagar framåt</option>)}
            </select>
          </label>
          <div style={{ marginLeft: 'auto' }}>
            <RxSearch value={q} onChange={setQ} placeholder="Sök artikel eller artikelnummer" />
          </div>
        </RxFilterRow>

        <RxPanel flush title="Artiklar med brist"
          sub={atp.loading ? 'Uppdaterar…' : `${rxPlural(filtered.length, 'artikel', 'artiklar')}, ${days} dagar framåt`}
          actions={<div className="rx-filters" role="group" aria-label="Sortering">
            <button type="button" aria-pressed={sortBy === 'size'} className={`rx-filter${sortBy === 'size' ? ' rx-filter--on' : ''}`} onClick={() => setSortBy('size')}>Störst brist</button>
            <button type="button" aria-pressed={sortBy === 'date'} className={`rx-filter${sortBy === 'date' ? ' rx-filter--on' : ''}`} onClick={() => setSortBy('date')}>Tidigast brist</button>
          </div>}>
          {rows.length === 0
            ? <RxEmpty title="Ingen brist">Alla lagda ordrar med leveransdatum inom {days} dagar täcks av disponibelt saldo och registrerade inleveranser.</RxEmpty>
            : filtered.length === 0
              ? <RxEmpty title="Inga artiklar matchar filtret." action={<button type="button" className="rx-link" onClick={() => { setScope('all'); setVendor(''); setSource(''); setQ(''); }}>Rensa filter</button>} />
              : <div style={{ overflowX: 'auto' }}>
                <table className="rx-table">
                  <thead><tr>
                    <th scope="col">Artikel</th>
                    <th scope="col" className="rx-num">Disponibelt saldo</th>
                    <th scope="col" className="rx-num">Reserverat i order</th>
                    <th scope="col" className="rx-num">Inleverans</th>
                    <th scope="col" className="rx-num">Brist</th>
                    <th scope="col">Brist från</th>
                    <th scope="col" style={{ width: 140 }}>Täckningsgrad</th>
                  </tr></thead>
                  <tbody>
                    {filtered.slice(0, limit).map(r => {
                      const isOpen = open === r.sku;
                      const flag = flagsBySku.get(r.sku);
                      const rel = rxDay(r.riskDate, { relative: true });
                      const detailId = `rx-stk-daily-${rxStkSlug(r.sku)}`;
                      return <React.Fragment key={r.sku}>
                        <tr className={`rx-row--click${isOpen ? ' rx-row--selected' : ''}`} onClick={() => setOpen(isOpen ? null : r.sku)}>
                          <td>
                            <button type="button" aria-expanded={isOpen} aria-controls={isOpen ? detailId : undefined}
                              onClick={e => { e.stopPropagation(); setOpen(isOpen ? null : r.sku); }}
                              style={{ all: 'unset', cursor: 'pointer', display: 'flex', alignItems: 'flex-start', gap: 6 }}>
                              <span aria-hidden="true" style={{ display: 'inline-flex', marginTop: 2, color: 'var(--rx-ink-4)', transform: isOpen ? 'rotate(90deg)' : 'none', transition: 'transform .15s' }}>{I.chev}</span>
                              <span>
                                <span style={{ display: 'block', fontWeight: 500, color: 'var(--rx-ink)' }}>{r.title}</span>
                                <span className="rx-mono rx-muted" style={{ fontSize: 11.5 }}>{r.sku} · {r.vendor || 'leverantör saknas'}</span>
                              </span>
                            </button>
                            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginTop: 4, marginLeft: 22 }}>
                              {r.src === 'drop' && <RxPill tone="info">Direktleverans</RxPill>}
                              {flag && <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5, fontSize: 12, color: 'var(--rx-warn)' }} title={flag.items.map(i => i.text).join(' ')}>
                                {I.warning}{flag.items[0].short}{flag.items.length > 1 ? ` (+${flag.items.length - 1})` : ''}
                              </span>}
                            </div>
                          </td>
                          <td className="rx-num" style={{ color: r.currentStock < 0 ? 'var(--rx-warn)' : 'var(--rx-ink-2)' }}>
                            {r.src === 'drop' ? <span className="rx-muted" title="Artikeln finns inte i Ongoing. Leveransrisk räknar 0 st.">ej i Ongoing</span> : fmtInt(r.currentStock)}
                          </td>
                          <td className="rx-num" style={{ color: 'var(--rx-ink-2)' }}>{fmtInt(r.committedOrders)}</td>
                          <td className="rx-num" style={{ color: r.inboundExpected ? 'var(--rx-ok)' : 'var(--rx-ink-3)' }}>{r.inboundExpected ? rxStkSigned(r.inboundExpected) : '0'}</td>
                          <td className="rx-num">
                            <span style={{ fontWeight: 650, color: 'var(--rx-danger)' }}>{fmtInt(r.minBal)}</span>
                            {r.netAvailable !== r.minBal && <div className="rx-muted" style={{ fontSize: 11.5 }}>{r.netAvailable < 0 ? `${fmtInt(r.netAvailable)} efter inleverans` : 'täcks av inleverans'}</div>}
                          </td>
                          <td style={rel === 'idag' ? { fontWeight: 600, color: 'var(--rx-danger)' } : { color: 'var(--rx-ink-2)' }}>{rel}</td>
                          <td>
                            {r.coverage == null ? <span className="rx-faint">okänd</span> : <>
                              <RxBar pct={r.coverage} tone={r.coverage >= 100 ? 'ok' : 'warn'} />
                              <div className="rx-muted" style={{ fontSize: 11.5, marginTop: 4, fontVariantNumeric: 'tabular-nums' }}>{fmtInt(r.coverage)} % täckt</div>
                            </>}
                          </td>
                        </tr>
                        {isOpen && <tr>
                          <td colSpan={7} style={{ background: 'var(--rx-surface-alt)', paddingLeft: 34 }}>
                            <RxStkDaily row={r} id={detailId} />
                          </td>
                        </tr>}
                      </React.Fragment>;
                    })}
                    {filtered.length > limit && <tr><td colSpan={7}>
                      <button type="button" className="rx-link" onClick={() => setLimit(l => l + RX_STK_PAGE)}>Visa {fmtInt(Math.min(RX_STK_PAGE, filtered.length - limit))} till</button>
                      <span className="rx-muted" style={{ marginLeft: 8, fontSize: 12.5 }}>av {fmtInt(filtered.length - limit)} dolda</span>
                    </td></tr>}
                  </tbody>
                </table>
              </div>}
          <div className="rx-muted" style={{ fontSize: 12, lineHeight: 1.5, padding: '10px 18px 14px', borderTop: '1px solid var(--rx-divider)' }}>
            Disponibelt saldo är säljbart i Ongoing (fysiskt minus det lagret redan reserverat). Reserverat i order är ej levererade Shopify-ordrar med leveransdatum inom {days} dagar. Brist är lägsta löpande saldo under perioden. Täckningsgrad är (saldo plus inleverans) delat med reserverat i order. Inleveranser med passerat datum räknas inte.
          </div>
        </RxPanel>
      </div>

      <aside className="rx-split__side" aria-label="Inköp och datakvalitet">
        <RxPanel title="Inköpsförslag per leverantör">
          {purchase.length === 0
            ? <div className="rx-muted" style={{ fontSize: 13 }}>Ingen brist, inget att beställa.</div>
            : purchase.map(g => <div key={g.name} style={{ paddingTop: 10, borderTop: '1px solid var(--rx-divider)', fontSize: 13.5 }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontWeight: 600 }}>{g.name}</div>
                  <div className="rx-muted" style={{ fontSize: 12.5 }}>
                    {g.rows.length ? rxPlural(g.rows.length, 'artikel', 'artiklar') : 'Inget att beställa'}
                    {g.lateOnly ? `, ${fmtInt(g.lateOnly)} täcks av inleverans men för sent` : ''}
                  </div>
                </div>
                <span style={{ fontWeight: 650, fontVariantNumeric: 'tabular-nums' }}>{fmtInt(g.units)} st</span>
              </div>
              {g.rows.length > 0 && <div style={{ marginTop: 6 }}>
                <button type="button" className="rx-link" aria-expanded={draftVendor === g.name}
                  onClick={() => setDraftVendor(draftVendor === g.name ? null : g.name)}>
                  {draftVendor === g.name ? 'Dölj utkast' : 'Skapa utkast'}
                </button>
                {draftVendor === g.name && <RxStkDraft group={g} asOf={atp.data.asOf} days={days} />}
              </div>}
            </div>)}
          <p style={{ margin: 0, fontSize: 12, color: 'var(--rx-ink-3)', lineHeight: 1.5 }}>
            Antal är brist som inte täcks av registrerad inleverans. Utkast skapas bara här i webbläsaren och skickas aldrig automatiskt. Ingen inköpsorder läggs.
          </p>
        </RxPanel>

        <RxPanel title="Datakvalitet" tone={flags.length ? 'warn' : undefined}
          sub={!flagsLoading && flags.length ? rxPlural(flagCount, 'flagga', 'flaggor') : undefined}>
          {(inv.error || perf.error) && <RxCallout tone="warn" title="Jämförelsen är ofullständig.">
            {inv.error && <div>Saldo: {String(inv.error.message || inv.error)} <button type="button" className="rx-link" onClick={inv.reload}>Försök igen</button></div>}
            {perf.error && <div>Artikelstatus: {String(perf.error.message || perf.error)} <button type="button" className="rx-link" onClick={perf.reload}>Försök igen</button></div>}
          </RxCallout>}
          {flagsLoading && <div className="rx-muted" style={{ fontSize: 13 }}><span className="spinner" /> Jämför med Saldo och Artikelstatus…</div>}
          {!flagsLoading && flags.length === 0 && !inv.error && !perf.error && <div className="rx-muted" style={{ fontSize: 13 }}>Inga negativa saldon, passerade inleveransdatum eller avvikande inleveranser hittades.</div>}
          {flags.length > 0 && <div style={{ display: 'flex', flexDirection: 'column', gap: 10, maxHeight: 420, overflowY: 'auto' }}>
            {flags.map(f => <div key={f.sku} style={{ fontSize: 13, lineHeight: 1.5, color: 'var(--rx-ink-2)' }}>
              <strong style={{ color: 'var(--rx-ink)', fontWeight: 600 }}>{f.title}</strong> <span className="rx-mono rx-muted" style={{ fontSize: 11.5 }}>{f.sku}</span>
              {f.items.map((it, i) => <div key={i}>
                {it.text}{' '}
                <button type="button" className="rx-link" style={{ fontSize: 12.5 }} onClick={() => go(it.target)}>
                  {it.target === 'reports/skus' ? 'Artikelstatus' : 'Saldo'}
                </button>
              </div>)}
            </div>)}
          </div>}
          <div style={{ display: 'flex', gap: 14, fontSize: 13 }}>
            <button type="button" className="rx-link" onClick={() => go('reports/inventory')}>Öppna Saldo</button>
            <button type="button" className="rx-link" onClick={() => go('reports/skus')}>Öppna Artikelstatus</button>
          </div>
        </RxPanel>

        <RxPanel title="Senast uppdaterat">
          <dl style={{ margin: 0, display: 'grid', gridTemplateColumns: 'auto 1fr', gap: '6px 12px', fontSize: 13 }}>
            <dt className="rx-muted">Leveransrisk</dt>
            <dd style={{ margin: 0 }}>beräknad {rxDay(atp.data.asOf, { relative: true })}</dd>
            <dt className="rx-muted">Saldo</dt>
            <dd style={{ margin: 0 }}>{inv.data?.fetchedAt ? `hämtat ${rxDayTime(inv.data.fetchedAt)}` : inv.loading ? 'hämtar…' : 'okänt'}</dd>
            {diag.data && <>
              <dt className="rx-muted">Ongoing</dt>
              <dd style={{ margin: 0 }}>
                {diag.data.connected ? <RxPill tone="ok">Ansluten</RxPill> : <RxPill tone="danger">{diag.data.configured ? 'Svarar inte' : 'Inte konfigurerad'}</RxPill>}
              </dd>
              <dt className="rx-muted">Lagersaldo</dt>
              <dd style={{ margin: 0 }}>
                {snapAt ? rxDayTime(snapAt) : 'okänt'}
                {snapAge != null && snapAge > 24 && <> <RxPill tone="warn">äldre än ett dygn</RxPill></>}
              </dd>
              <dt className="rx-muted">Inleveranser</dt>
              <dd style={{ margin: 0 }}>{diag.data.local?.latestInboundUpdate ? rxDayTime(diag.data.local.latestInboundUpdate) : 'okänt'}</dd>
              <dt className="rx-muted">Senaste webhook</dt>
              <dd style={{ margin: 0 }}>{diag.data.local?.lastWebhookAt ? rxDayTime(String(diag.data.local.lastWebhookAt).replace(' ', 'T') + (/Z|[+-]\d\d:?\d\d$/.test(diag.data.local.lastWebhookAt) ? '' : 'Z')) : 'ingen'}</dd>
            </>}
          </dl>
          {diag.loading && <div className="rx-muted" style={{ fontSize: 12.5 }}><span className="spinner" /> Kontrollerar Ongoing…</div>}
          {diag.error && <div className="rx-muted" style={{ fontSize: 12.5 }}>Kunde inte hämta Ongoing-status: {String(diag.error.message || diag.error)}</div>}
          {diag.data?.error && <div style={{ fontSize: 12.5, color: 'var(--rx-warn)' }}>{diag.data.error}</div>}
        </RxPanel>
      </aside>
    </div>
  </div>;
}
