// offer.jsx — Offer Creator screen + Offer History

const { useState: useStateOff, useMemo: useMemoOff, useRef: useRefOff } = React;

// ───── Storage for offers (in-memory + localStorage) ─────
const OFFER_KEY = 'reuseit.offers.v1';
const DRAFT_KEY = 'reuseit.draft.v2';      // legacy single-draft (migrated on load)
const DRAFTS_KEY = 'reuseit.drafts.v1';    // multi-draft store

function loadOffers() {
  try { return JSON.parse(localStorage.getItem(OFFER_KEY) || '[]'); } catch { return []; }
}
function saveOffers(list) {
  try { localStorage.setItem(OFFER_KEY, JSON.stringify(list)); } catch {}
}
function loadDraft() {
  try { return JSON.parse(localStorage.getItem(DRAFT_KEY) || 'null'); } catch { return null; }
}
function saveDraft(draft) {
  try {
    if (!draft || draft.length === 0) localStorage.removeItem(DRAFT_KEY);
    else localStorage.setItem(DRAFT_KEY, JSON.stringify(draft));
  } catch {}
}
function loadDrafts() {
  let drafts = [];
  try { drafts = JSON.parse(localStorage.getItem(DRAFTS_KEY) || '[]'); } catch {}
  // Migrate legacy single draft if present and not yet migrated
  const legacy = loadDraft();
  if (legacy && legacy.length && !drafts.some(d => d.__migrated)) {
    drafts.unshift({
      id: 'DR-' + Date.now().toString(36).toUpperCase(),
      name: 'Draft offer',
      items: legacy,
      updatedAt: new Date().toISOString(),
      __migrated: true,
    });
    saveDrafts(drafts);
    saveDraft([]); // clear legacy
  }
  return drafts;
}
function saveDrafts(list) {
  try { localStorage.setItem(DRAFTS_KEY, JSON.stringify(list)); } catch {}
}

let _LID = 0;
function newLid() { _LID += 1; return 'L' + Date.now().toString(36) + '-' + _LID; }

// Variants matching a partial line spec
function matchedVariants(line) {
  const m = MODELS_BY_ID[line.modelId];
  if (!m) return [];
  return m.variants.filter(v =>
    (line.storage   == null || v.storage === line.storage) &&
    (line.color     == null || v.color === line.color) &&
    (line.condition == null || v.condition === line.condition)
  );
}

// Aggregate stats for one line. With wildcards we average median/low/high
// across matched variants (per-unit), then × qty for the totals.
function lineStats(line) {
  const vs = matchedVariants(line);
  if (vs.length === 0) return { matched: 0, median: 0, low: 0, high: 0, byCond: { A: 0, B: 0, C: 0 } };
  const median = vs.reduce((s, v) => s + v.stats.median, 0) / vs.length;
  const low    = Math.min(...vs.map(v => v.stats.lowest));
  const high   = Math.max(...vs.map(v => v.stats.highest));
  // Condition mix for the LINE: if condition is locked, all qty goes to that.
  // If wildcarded, distribute evenly across matched conditions present.
  const byCond = { A: 0, B: 0, C: 0 };
  if (line.condition) {
    byCond[line.condition] = line.qty;
  } else {
    const conds = [...new Set(vs.map(v => v.condition))];
    const each = line.qty / conds.length;
    conds.forEach(c => { byCond[c] += each; });
  }
  return { matched: vs.length, median, low, high, byCond };
}

function aggregateOffer(items) {
  const lines = items.map((it, i) => ({ ...it, lid: it.lid || it.id || `__pos_${i}`, stats: lineStats(it) }));
  const totalQty = lines.reduce((s, l) => s + l.qty, 0);
  const totalMedian = lines.reduce((s, l) => s + l.stats.median * l.qty, 0);
  const totalLow = lines.reduce((s, l) => s + l.stats.low * l.qty, 0);
  const totalHigh = lines.reduce((s, l) => s + l.stats.high * l.qty, 0);
  const cond = { A: 0, B: 0, C: 0 };
  lines.forEach(l => { cond.A += l.stats.byCond.A; cond.B += l.stats.byCond.B; cond.C += l.stats.byCond.C; });
  const condPct = {
    A: totalQty ? Math.round(100 * cond.A / totalQty) : 0,
    B: totalQty ? Math.round(100 * cond.B / totalQty) : 0,
    C: totalQty ? Math.round(100 * cond.C / totalQty) : 0,
  };
  // Most / least valuable lines (by unit median, only matched ones)
  const matchedLines = lines.filter(l => l.stats.matched > 0);
  let mostValuable = null, leastValuable = null;
  let itemLow = 0, itemHigh = 0;
  if (matchedLines.length) {
    mostValuable = matchedLines.reduce((a, b) => b.stats.median > a.stats.median ? b : a);
    leastValuable = matchedLines.reduce((a, b) => b.stats.median < a.stats.median ? b : a);
    // Per-unit price spread across the offer's line items
    itemLow  = Math.min(...matchedLines.map(l => l.stats.low));
    itemHigh = Math.max(...matchedLines.map(l => l.stats.high));
  }
  return { lines, totalQty, totalMedian, totalLow, totalHigh, itemLow, itemHigh, cond, condPct, mostValuable, leastValuable };
}

// Export an offer to CSV (download in browser)
function exportOfferCSV(offer) {
  const header = 'model,storage,color,condition,qty';
  const rows = (offer.items || []).map(it => {
    const m = MODELS_BY_ID[it.modelId];
    const name = m ? m.name : it.modelId;
    return [name, it.storage || '', it.color || '', it.condition || '', it.qty].map(v => {
      const s = String(v ?? '');
      return /[",\n]/.test(s) ? '"' + s.replace(/"/g, '""') + '"' : s;
    }).join(',');
  });
  const csv = [header, ...rows].join('\n');
  const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = (offer.name || offer.id || 'offer').replace(/[^\w\-]+/g, '_') + '.csv';
  document.body.appendChild(a); a.click(); a.remove();
  URL.revokeObjectURL(url);
}

// CSV parsing — empty cell or "-" / "–" means wildcard
function parseCSV(text) {
  const lines = text.split(/\r?\n/).map(l => l.trim()).filter(Boolean);
  if (lines.length === 0) return [];
  const splitLine = (l) => l.split(',').map(c => c.trim().replace(/^"|"$/g, ''));
  const head = splitLine(lines[0]).map(h => h.toLowerCase());
  const headerIs = head.includes('model') || head.includes('product');
  const idx = {
    model: headerIs ? Math.max(head.indexOf('model'), head.indexOf('product')) : 0,
    storage: headerIs ? head.indexOf('storage') : 1,
    color: headerIs ? head.indexOf('color') : 2,
    condition: headerIs ? head.indexOf('condition') : 3,
    qty: headerIs ? Math.max(head.indexOf('qty'), head.indexOf('quantity'), head.indexOf('amount')) : 4,
  };
  const start = headerIs ? 1 : 0;
  const wildcard = (s) => !s || s === '-' || s === '–' || s.toLowerCase() === 'any';
  const out = [];
  for (let i = start; i < lines.length; i++) {
    const row = splitLine(lines[i]);
    const q = (row[idx.model] || '').toLowerCase().trim();
    if (!q) continue;
    const m = MODELS.find(x => x.name.toLowerCase() === q)
           || MODELS.find(x => x.name.toLowerCase().includes(q))
           || MODELS.find(x => q.includes(x.name.toLowerCase()));
    if (!m) continue;
    const storage   = wildcard(row[idx.storage])   ? null : row[idx.storage];
    const colorRaw  = wildcard(row[idx.color])     ? null : row[idx.color];
    const color     = colorRaw ? (m.colors.find(c => c.toLowerCase() === colorRaw.toLowerCase()) || colorRaw) : null;
    const condRaw   = wildcard(row[idx.condition]) ? null : row[idx.condition].toUpperCase();
    const condition = condRaw && /^[ABC]$/.test(condRaw) ? condRaw : null;
    const qty = Math.max(1, parseInt(row[idx.qty], 10) || 1);
    out.push({ lid: newLid(), modelId: m.id, storage, color, condition, qty });
  }
  return out;
}

// ───── OfferCreator screen — edits a single draft ─────
function OfferCreator({ go, draft, updateDraft, finalizeDraft, discardDraft, offerCount }) {
  const [search, setSearch] = useStateOff('');
  const draftItems = (draft && draft.items) || [];
  const defaultName = (draft && draft.name) || `Offer #${(offerCount || 0) + 1}`;
  const [name, setName] = useStateOff(defaultName);
  const [notes, setNotes] = useStateOff((draft && draft.notes) || '');
  // collapsedLines starts as `null` meaning "all collapsed by default"; once user toggles anything we switch to a per-id map.
  const [collapsedLines, setCollapsedLines] = useStateOff(null);
  const isCollapsed = (lid) => collapsedLines == null ? true : !!collapsedLines[lid];
  const toggleLine = (lid) => setCollapsedLines(c => {
    if (c == null) return { [lid]: false };
    return { ...c, [lid]: !c[lid] };
  });
  const fileRef = useRefOff();
  // Discard the draft on unmount if it was never actually edited
  const draftRef = useRefOff(draft);
  draftRef.current = draft;
  React.useEffect(() => {
    return () => {
      const d = draftRef.current;
      if (d && d.pristine) discardDraft(d.id);
    };
  }, []);

  function setDraftItems(updater) {
    const next = typeof updater === 'function' ? updater(draftItems) : updater;
    updateDraft({ items: next });
  }

  const matches = useMemoOff(() => {
    if (!search.trim()) return [];
    const q = search.toLowerCase();
    return MODELS.filter(m => m.name.toLowerCase().includes(q) || m.brand.toLowerCase().includes(q)).slice(0, 5);
  }, [search]);

  // Add a line for a model (wildcards by default — a clean "any variant" row)
  function addModel(modelId, qty = 1) {
    setDraftItems(prev => [...prev, { lid: newLid(), modelId, storage: null, color: null, condition: null, qty }]);
  }
  // Compatibility entry from catalogue: comes in as a variantId, we lock it to that variant
  function addLineFromVariant(variantId, qty = 1) {
    const v = VARIANT_BY_ID[variantId]; if (!v) return;
    setDraftItems(prev => [...prev, { lid: newLid(), modelId: v.modelId, storage: v.storage, color: v.color, condition: v.condition, qty }]);
  }
  function setLine(lid, patch) {
    setDraftItems(prev => prev.map(l => l.lid === lid ? { ...l, ...patch } : l));
  }
  function removeLine(lid) {
    setDraftItems(prev => prev.filter(l => l.lid !== lid));
  }
  function onUpload(e) {
    const f = e.target.files[0]; if (!f) return;
    const reader = new FileReader();
    reader.onload = () => {
      const items = parseCSV(reader.result);
      setDraftItems(prev => [...prev, ...items]);
    };
    reader.readAsText(f);
    e.target.value = '';
  }

  function saveOffer() {
    if (draftItems.length === 0) return;
    finalizeDraft(draft.id, name || defaultName, notes);
    go({ view: 'offers' });
  }

  const agg = aggregateOffer(draftItems);

  return (
    <div className="screen">
      <div className="offer-titlebar">
        <div className="offer-title-input-wrap">
          <span className="offer-title-eyebrow">Draft offer</span>
          <div className="offer-title-input-row">
            <input
              className="offer-title-input"
              value={name}
              onChange={(e) => { setName(e.target.value); updateDraft({ name: e.target.value }); }}
              placeholder={defaultName}
              maxLength={20}
              size={Math.max((name || defaultName || '').length, 6)}
            />
            <Icon d={ICONS.edit} size={20} className="offer-title-pencil" />
          </div>
          <textarea
            className="offer-notes-input"
            value={notes}
            onChange={(e) => { setNotes(e.target.value); updateDraft({ notes: e.target.value }); }}
            placeholder="Add notes (optional)"
            rows={1}
            maxLength={100}
          />
        </div>
        <div className="offer-titlebar-actions">
          <button className="ghost-btn" onClick={() => { discardDraft(draft.id); go({ view: 'offers' }); }}>
            Discard draft
          </button>
          <button className="primary-btn lg" onClick={saveOffer} disabled={draftItems.length === 0}>
            <Icon d={ICONS.offer} size={14} /> Save offer
          </button>
        </div>
      </div>

      {/* Summary header */}
      <section className="card offer-summary">
        <div className="offer-sum-grid">
          <div>
            <div className="eyebrow">Total median value</div>
            <div className="big-num-val">{fmtPrice(agg.totalMedian)}</div>
            <div className="muted small">{agg.totalQty} item{agg.totalQty === 1 ? '' : 's'} across {agg.lines.length} row{agg.lines.length === 1 ? '' : 's'}</div>
          </div>
          <div>
            <div className="eyebrow">Value distribution</div>
            <PriceRangeViz lines={agg.lines} perUnit />
          </div>
          <div>
            <div className="eyebrow">Condition mix</div>
            <div className="cond-mix-bar">
              <span className="cm-seg cm-A" style={{ width: agg.condPct.A + '%' }} title={`Grade A · ${agg.condPct.A}%`}></span>
              <span className="cm-seg cm-B" style={{ width: agg.condPct.B + '%' }} title={`Grade B · ${agg.condPct.B}%`}></span>
              <span className="cm-seg cm-C" style={{ width: agg.condPct.C + '%' }} title={`Grade C · ${agg.condPct.C}%`}></span>
            </div>
            <div className="cond-mix-legend">
              <span><i className="dot dot-A"></i>A {agg.condPct.A}%</span>
              <span><i className="dot dot-B"></i>B {agg.condPct.B}%</span>
              <span><i className="dot dot-C"></i>C {agg.condPct.C}%</span>
            </div>
          </div>
        </div>
      </section>

      {/* Add inputs */}
      <section className="card searchcard">
        <div className="offer-add-row">
          <div className="search">
            <Icon d={ICONS.search} size={16} />
            <input value={search} onChange={(e) => setSearch(e.target.value)} placeholder="Search a product to add …" />
            {search && <button className="link" onClick={() => setSearch('')}>clear</button>}
          </div>
          <div className="offer-add-actions">
            <input type="file" ref={fileRef} accept=".csv,text/csv" style={{display:'none'}} onChange={onUpload} />
            <button className="ghost-btn" onClick={() => fileRef.current && fileRef.current.click()}>
              <Icon d={ICONS.upload} size={14} /> Upload CSV
            </button>
          </div>
        </div>
        {matches.length > 0 && (
          <div className="search-results">
            {matches.map(m => (
              <button key={m.id} className="search-result" onClick={() => { addModel(m.id); setSearch(''); }}>
                <ProductGlyph product={m} size={32} tone="cream" />
                <div className="search-result-text">
                  <strong>{m.name}</strong>
                  <span>{m.brand} · adds a row with all variants matched — narrow below</span>
                </div>
                <span className="search-result-price"><Icon d={ICONS.plus} size={14} /> Add</span>
              </button>
            ))}
          </div>
        )}
        <div className="muted xs offer-csv-hint">CSV columns: <code>model, storage, color, condition, qty</code> — empty cell or <code>-</code> means any.</div>
      </section>

      {/* Line items */}
      <section className="card">
        <div className="card-head">
          <div>
            <div className="eyebrow">Items</div>
            <h3 className="h3">{agg.totalQty} item{agg.totalQty === 1 ? '' : 's'}</h3>
          </div>
          <div className="card-head-actions">
            {agg.lines.length > 0 && (() => {
              const allCollapsed = agg.lines.every(l => isCollapsed(l.lid));
              return (
                <button className="link" onClick={() => {
                  if (allCollapsed) {
                    const next = {}; agg.lines.forEach(l => { next[l.lid] = false; }); setCollapsedLines(next);
                  } else {
                    setCollapsedLines(null);
                  }
                }}>
                  {allCollapsed ? 'expand all' : 'collapse all'}
                </button>
              );
            })()}
            {agg.lines.length > 0 && <button className="link" onClick={() => setDraftItems([])}>clear all</button>}
          </div>
        </div>

        {agg.lines.length === 0 ? (
          <div className="empty-inline">No items yet — search above or upload a CSV.</div>
        ) : (
          <div className="offer-lines">
            <div className="offer-line-head v2">
              <span>Product</span><span>Variant</span><span>Qty</span><span>Unit median</span><span>Subtotal</span><span></span>
            </div>
            {agg.lines.map(l => (
              <OfferLineRow
                key={l.lid}
                line={l}
                collapsed={isCollapsed(l.lid)}
                onToggleCollapse={() => toggleLine(l.lid)}
                onChange={(patch) => setLine(l.lid, patch)}
                onRemove={() => removeLine(l.lid)}
              />
            ))}
            <div className="offer-line-foot v2">
              <span>Total</span>
              <span className="num"><strong>{agg.totalQty}</strong></span>
              <span className="num muted">{fmtPrice(agg.totalLow)}–{fmtPrice(agg.totalHigh)}</span>
              <span className="num"><strong>{fmtPrice(agg.totalMedian)}</strong></span>
            </div>
          </div>
        )}
      </section>
    </div>
  );
}

function OfferLineRow({ line, collapsed, onToggleCollapse, onChange, onRemove }) {
  const m = MODELS_BY_ID[line.modelId];
  const subtotal = line.stats.median * line.qty;
  // Attribute strip: dot-separated, category-agnostic. Phones today; future categories
  // can extend `attrs` (e.g. ram/cpu/screen) without changing the column layout.
  const attrs = [
    line.storage,
    line.color,
    line.condition && `Grade ${line.condition}`,
  ].filter(Boolean);
  const stripText = attrs.length ? attrs.join(' · ') : 'Any variant';
  const isAny = attrs.length === 0;

  if (collapsed) {
    return (
      <div className="offer-line offer-line-v2 is-collapsed">
        <button className="offer-line-prod ol-prod" onClick={onToggleCollapse}>
          <span className="chev is-closed"><Icon d={ICONS.chevron} size={12} /></span>
          <ProductGlyph product={m} size={28} tone="cream" />
          <span className="ol-name">{m.name}</span>
        </button>
        <button className={`ol-strip${isAny ? ' is-any' : ''}`} onClick={onToggleCollapse} title={stripText}>
          {stripText}
        </button>
        <input
          type="number"
          min="1"
          className="line-qty ol-qty"
          value={line.qty}
          onChange={(e) => onChange({ qty: Math.max(1, parseInt(e.target.value, 10) || 1) })}
          onClick={(e) => e.stopPropagation()}
        />
        <span className="num ol-unit">{line.stats.matched ? fmtPrice(line.stats.median) : '—'}</span>
        <span className="num ol-sub"><strong>{line.stats.matched ? fmtPrice(subtotal) : '—'}</strong></span>
        <button className="line-rm" onClick={(e) => { e.stopPropagation(); onRemove(); }} title="Remove">
          <Icon d={ICONS.trash} size={14} />
        </button>
      </div>
    );
  }

  // Expanded: same top row of identifiers + numeric columns, plus an editor panel underneath.
  return (
    <div className="offer-line offer-line-v2 is-expanded">
      <button className="offer-line-prod ol-prod" onClick={onToggleCollapse}>
        <span className="chev"><Icon d={ICONS.chevron} size={12} /></span>
        <ProductGlyph product={m} size={28} tone="cream" />
        <span className="ol-name">{m.name}</span>
      </button>
      <span className={`ol-strip${isAny ? ' is-any' : ''}`}>{stripText}</span>
      <input
        type="number"
        min="1"
        className="line-qty ol-qty"
        value={line.qty}
        onChange={(e) => onChange({ qty: Math.max(1, parseInt(e.target.value, 10) || 1) })}
      />
      <span className="num ol-unit">{line.stats.matched ? fmtPrice(line.stats.median) : '—'}</span>
      <span className="num ol-sub"><strong>{line.stats.matched ? fmtPrice(subtotal) : '—'}</strong></span>
      <button className="line-rm" onClick={onRemove} title="Remove">
        <Icon d={ICONS.trash} size={14} />
      </button>
      <div className="ol-editor">
        <AttrField label="Storage">
          <select className={`line-sel ${line.storage == null ? 'is-any' : ''}`}
                  value={line.storage == null ? '__any' : line.storage}
                  onChange={(e) => onChange({ storage: e.target.value === '__any' ? null : e.target.value })}>
            <option value="__any">– any –</option>
            {m.storages.map(s => <option key={s} value={s}>{s}</option>)}
          </select>
        </AttrField>
        <AttrField label="Color">
          <select className={`line-sel ${line.color == null ? 'is-any' : ''}`}
                  value={line.color == null ? '__any' : line.color}
                  onChange={(e) => onChange({ color: e.target.value === '__any' ? null : e.target.value })}>
            <option value="__any">– any –</option>
            {m.colors.map(c => <option key={c} value={c}>{c}</option>)}
          </select>
        </AttrField>
        <AttrField label="Condition">
          <select className={`line-sel cond-sel cond-${line.condition || 'any'} ${line.condition == null ? 'is-any' : ''}`}
                  value={line.condition == null ? '__any' : line.condition}
                  onChange={(e) => onChange({ condition: e.target.value === '__any' ? null : e.target.value })}>
            <option value="__any">– any –</option>
            <option value="A">A · Like new</option>
            <option value="B">B · Good</option>
            <option value="C">C · Fair</option>
          </select>
        </AttrField>
      </div>
    </div>
  );
}

function AttrField({ label, children }) {
  return (
    <label className="attr-field">
      <span className="attr-field-l">{label}</span>
      {children}
    </label>
  );
}

function PriceRangeViz({ low, high, median, lines, perUnit }) {
  // Airbnb-style histogram: 0 → rounded ceiling, narrow bars per price bucket.
  // Each unit (line.qty expanded) contributes 1 to its bucket; hover shows model name(s).
  const units = [];
  (lines || []).forEach(l => {
    if (!l || !l.stats || !l.stats.matched) return;
    const p = perUnit ? l.stats.median : l.stats.median * l.qty;
    const m = MODELS_BY_ID[l.modelId];
    const name = m ? m.name : 'Item';
    for (let i = 0; i < l.qty; i++) units.push({ price: p, name });
  });
  if (!units.length) {
    return (
      <div className="range-viz">
        <div className="range-hist-row"></div>
        <div className="range-track"></div>
        <div className="range-row"><span>—</span><span>—</span></div>
      </div>
    );
  }
  const maxPrice = Math.max(...units.map(u => u.price));
  // Round ceiling up to a nice even number above the max
  const ceiling = niceCeiling(maxPrice);
  const BINS = 28;
  const buckets = Array.from({ length: BINS }, () => []);
  units.forEach(u => {
    let idx = Math.floor((u.price / ceiling) * BINS);
    if (idx >= BINS) idx = BINS - 1;
    if (idx < 0) idx = 0;
    buckets[idx].push(u);
  });
  const maxCount = Math.max(...buckets.map(b => b.length), 1);
  return (
    <div className="range-viz">
      <div className="range-hist-row">
        {buckets.map((bucket, i) => {
          const c = bucket.length;
          const h = c === 0 ? 0 : Math.max(8, (c / maxCount) * 100);
          // Group by model name with counts
          const counts = new Map();
          bucket.forEach(u => counts.set(u.name, (counts.get(u.name) || 0) + 1));
          return (
            <span key={i} className="range-hist-cell">
              {c > 0 && <span className="range-hist-bar" style={{ height: h + '%' }}></span>}
              {c > 0 && (
                <span className="range-hist-tip">
                  {[...counts.entries()].map(([name, n], j) => (
                    <span key={j} className="range-tip-line">
                      {n > 1 ? `${n} × ` : ''}{name}
                    </span>
                  ))}
                </span>
              )}
            </span>
          );
        })}
      </div>
      <div className="range-track"></div>
      <div className="range-row">
        <span>{fmtPrice(0)}</span>
        <span>{fmtPrice(ceiling)}</span>
      </div>
    </div>
  );
}

function niceCeiling(max) {
  if (!Number.isFinite(max) || max <= 0) return 100;
  // Round up to a "nice" even number with a 5–20% headroom above max.
  const padded = max * 1.1;
  const mag = Math.pow(10, Math.floor(Math.log10(padded)));
  const n = padded / mag;
  let nice;
  if (n <= 1) nice = 1;
  else if (n <= 2) nice = 2;
  else if (n <= 2.5) nice = 2.5;
  else if (n <= 5) nice = 5;
  else nice = 10;
  return Math.ceil(nice * mag);
}

function weightedMedian(items) {
  const sorted = items.slice().sort((a, b) => a.price - b.price);
  const total = sorted.reduce((s, it) => s + it.qty, 0);
  if (!total) return sorted[0].price;
  let acc = 0;
  for (const it of sorted) {
    acc += it.qty;
    if (acc >= total / 2) return it.price;
  }
  return sorted[sorted.length - 1].price;
}

// ───── OfferHistory screen ─────
function OfferHistory({ go, offers, removeOffer, loadIntoDraft }) {
  return (
    <div className="screen">
      <TopBar
        title="Offer History"
        subtitle={`${offers.length} saved offer${offers.length === 1 ? '' : 's'}`}
      />
      {offers.length === 0 ? (
        <div className="empty">
          <div className="empty-glyph"><Icon d={ICONS.offer} size={36} /></div>
          <h3>No offers yet</h3>
          <p>Build your first offer from the catalogue or by uploading a CSV.</p>
          <button className="primary-btn" onClick={() => go({ view: 'offer-create' })}>Create offer</button>
        </div>
      ) : (
        <div className="offer-history">
          {offers.slice().reverse().map(o => (
            <OfferHistoryRow key={o.id} o={o} go={go} removeOffer={removeOffer} loadIntoDraft={loadIntoDraft} />
          ))}
        </div>
      )}
    </div>
  );
}

function OfferHistoryRow({ o, go, removeOffer, loadIntoDraft }) {
  const [open, setOpen] = useStateOff(false);
  const agg = aggregateOffer(o.items);
  const mv = agg.mostValuable, lv = agg.leastValuable;
  const mvModel = mv ? MODELS_BY_ID[mv.modelId] : null;
  const lvModel = lv ? MODELS_BY_ID[lv.modelId] : null;
  return (
    <div className={`card offer-card ${open ? 'is-open' : 'is-closed'}`}>
      <div className="offer-card-head">
        <div>
          <div className="eyebrow">{o.id}</div>
          <h3 className="h3">{o.name}</h3>
          <div className="muted small">{new Date(o.createdAt).toLocaleString('en-GB')}</div>
        </div>
        <div className="offer-card-actions">
          <button className="ghost-btn" onClick={(e) => { e.stopPropagation(); exportOfferCSV(o); }} title="Export CSV">
            <Icon d={ICONS.download} size={12} /> Export
          </button>
          <button className="ghost-btn" onClick={() => go({ view: 'offer-detail', offerId: o.id })}>
            Edit
          </button>
          <button className="ghost-btn" onClick={() => removeOffer(o.id)}>
            <Icon d={ICONS.trash} size={12} /> Delete
          </button>
          <button
            className={`offer-card-chevron ${open ? 'is-open' : ''}`}
            onClick={() => setOpen(v => !v)}
            aria-expanded={open}
            aria-label={open ? 'Collapse offer' : 'Expand offer'}
          >
            <Icon d="M6 9l6 6 6-6" size={22} />
          </button>
        </div>
      </div>
      {open && (
        <div className="offer-card-body">
          <div className="offer-card-stats">
            <div><span className="lbl">Items</span><span className="val">{agg.totalQty}</span></div>
            <div><span className="lbl">Total median</span><span className="val">{fmtPrice(agg.totalMedian)}</span></div>
            <div><span className="lbl">Range</span><span className="val small">{fmtPrice(agg.totalLow)}–{fmtPrice(agg.totalHigh)}</span></div>
            <div className="cm-cell">
              <span className="lbl">Condition mix</span>
              <div className="cond-mix-bar">
                <span className="cm-seg cm-A" style={{ width: agg.condPct.A + '%' }}></span>
                <span className="cm-seg cm-B" style={{ width: agg.condPct.B + '%' }}></span>
                <span className="cm-seg cm-C" style={{ width: agg.condPct.C + '%' }}></span>
              </div>
              <div className="cm-mini-legend">
                <span>A {agg.condPct.A}%</span>
                <span>B {agg.condPct.B}%</span>
                <span>C {agg.condPct.C}%</span>
              </div>
            </div>
          </div>
          <div className="offer-card-extremes">
            {mv && (
              <div className="extreme-row">
                <span className="extreme-tag tag-high">Most valuable</span>
                <span className="extreme-name">{mvModel.name}</span>
                <span className="extreme-meta muted xs">{mv.storage || 'any'} · {mv.color || 'any'} · {mv.condition || 'any'}</span>
                <span className="extreme-price"><strong>{fmtPrice(mv.stats.median)}</strong> <span className="muted xs">/ unit</span></span>
              </div>
            )}
            {lv && lv !== mv && (
              <div className="extreme-row">
                <span className="extreme-tag tag-low">Least valuable</span>
                <span className="extreme-name">{lvModel.name}</span>
                <span className="extreme-meta muted xs">{lv.storage || 'any'} · {lv.color || 'any'} · {lv.condition || 'any'}</span>
                <span className="extreme-price"><strong>{fmtPrice(lv.stats.median)}</strong> <span className="muted xs">/ unit</span></span>
              </div>
            )}
          </div>
        </div>
      )}
    </div>
  );
}

// ───── OffersIndex screen ─────
function OffersIndex({ go, offers, drafts, createDraft, discardDraft }) {
  const visibleDrafts = drafts.filter(d => !d.pristine);
  const totalItems = offers.reduce((s, o) => s + o.items.reduce((ss, i) => ss + i.qty, 0), 0);
  const recent = offers.slice().reverse().slice(0, 3);
  return (
    <div className="screen">
      <TopBar
        title="Offers"
        subtitle="Build, price and track customer offers against live market data."
      />

      <div className="offers-index-grid">
        <button className="offers-index-card primary" onClick={() => { const id = createDraft(); go({ view: 'offer-create', draftId: id }); }}>
          <div className="oic-glyph"><Icon d={ICONS.offer} size={28} /></div>
          <div className="oic-body">
            <div className="eyebrow">Create</div>
            <h2 className="oic-title">New offer</h2>
          </div>
        </button>

        <button className="offers-index-card" onClick={() => go({ view: 'offer-history' })}>
          <div className="oic-glyph"><Icon d={ICONS.history} size={28} /></div>
          <div className="oic-body">
            <div className="eyebrow">Archive</div>
            <h2 className="oic-title">History</h2>
          </div>
        </button>
      </div>

      {recent.length > 0 && (
        <section className="card">
          <div className="card-head">
            <div>
              <div className="eyebrow">Recent</div>
              <h3 className="h3">Last {recent.length} offer{recent.length === 1 ? '' : 's'}</h3>
            </div>
          </div>
          <div className="offers-recent">
            {recent.map(o => {
              const agg = aggregateOffer(o.items);
              return (
                <div key={o.id} className="offers-recent-row offers-draft-row">
                  <button className="orr-click" onClick={() => go({ view: 'offer-detail', offerId: o.id })}>
                    <div className="orr-main">
                      <div className="eyebrow">{o.id}</div>
                      <div className="orr-name">{o.name}</div>
                      <div className="muted xs">{new Date(o.createdAt).toLocaleString('en-GB')}</div>
                    </div>
                    <div className="orr-stats">
                      <div><span className="lbl">Items</span><span className="val">{agg.totalQty}</span></div>
                      <div><span className="lbl">Lines</span><span className="val">{agg.lines.length}</span></div>
                      <div><span className="lbl">Median</span><span className="val">{fmtPrice(agg.totalMedian)}</span></div>
                    </div>
                  </button>
                </div>
              );
            })}
          </div>
        </section>
      )}

      {visibleDrafts.length > 0 && (
        <section className="card">
          <div className="card-head">
            <div>
              <div className="eyebrow">Drafts</div>
              <h3 className="h3">{visibleDrafts.length} draft{visibleDrafts.length === 1 ? '' : 's'} in progress</h3>
            </div>
          </div>
          <div className="offers-recent">
            {visibleDrafts.slice().reverse().map(d => {
              const agg = aggregateOffer(d.items || []);
              return (
                <div key={d.id} className="offers-recent-row offers-draft-row">
                  <button className="orr-click" onClick={() => go({ view: 'offer-create', draftId: d.id })}>
                    <div className="orr-main">
                      <div className="eyebrow">Draft · {d.id.slice(-6)}</div>
                      <div className="orr-name">{d.name || 'Untitled draft'}</div>
                      <div className="muted xs">Updated {new Date(d.updatedAt).toLocaleString('en-GB')}</div>
                    </div>
                    <div className="orr-stats">
                      <div><span className="lbl">Items</span><span className="val">{agg.totalQty}</span></div>
                      <div><span className="lbl">Rows</span><span className="val">{agg.lines.length}</span></div>
                      <div><span className="lbl">Median</span><span className="val">{fmtPrice(agg.totalMedian)}</span></div>
                    </div>
                  </button>
                  <button className="line-rm draft-rm" onClick={(e) => { e.stopPropagation(); if (confirm('Discard this draft?')) discardDraft(d.id); }} title="Discard draft">
                    <Icon d={ICONS.close} size={14} />
                  </button>
                </div>
              );
            })}
          </div>
        </section>
      )}
    </div>
  );
}

// ───── OfferEditor screen — edit a saved offer in place ─────
function OfferEditor({ go, offers, offerId, updateOffer, removeOffer, loadIntoDraft }) {
  const offer = offers.find(o => o.id === offerId);
  const [name, setName] = useStateOff(offer ? offer.name : '');
  const [notes, setNotes] = useStateOff(offer ? (offer.notes || '') : '');
  const [items, setItems] = useStateOff(offer ? offer.items : []);
  const [collapsedLines, setCollapsedLines] = useStateOff(null);
  const isCollapsed = (lid) => collapsedLines == null ? true : !!collapsedLines[lid];
  const toggleLine = (lid) => setCollapsedLines(c => {
    if (c == null) return { [lid]: false };
    return { ...c, [lid]: !c[lid] };
  });
  const [search, setSearch] = useStateOff('');
  const fileRef = useRefOff();

  React.useEffect(() => {
    if (offer) { setName(offer.name); setItems(offer.items); }
  }, [offerId]);

  const matches = useMemoOff(() => {
    if (!search.trim()) return [];
    const q = search.toLowerCase();
    return MODELS.filter(m => m.name.toLowerCase().includes(q) || m.brand.toLowerCase().includes(q)).slice(0, 5);
  }, [search]);

  if (!offer) {
    return (
      <div className="screen">
        <TopBar title="Offer not found" subtitle="It may have been deleted." right={
          <button className="ghost-btn" onClick={() => go({ view: 'offer-history' })}>Back to history</button>
        } />
      </div>
    );
  }

  function addModel(modelId) {
    setItems(prev => [...prev, { lid: newLid(), modelId, storage: null, color: null, condition: null, qty: 1 }]);
  }
  function setLine(lid, patch) {
    setItems(prev => prev.map(l => l.lid === lid ? { ...l, ...patch } : l));
  }
  function removeLine(lid) {
    setItems(prev => prev.filter(l => l.lid !== lid));
  }
  function onUpload(e) {
    const f = e.target.files[0]; if (!f) return;
    const r = new FileReader();
    r.onload = () => setItems(prev => [...prev, ...parseCSV(r.result)]);
    r.readAsText(f);
    e.target.value = '';
  }
  function save() {
    updateOffer(offer.id, { name: name || offer.name, notes, items });
    go({ view: 'offer-history' });
  }

  const agg = aggregateOffer(items);
  return (
    <div className="screen">
      <div className="offer-titlebar">
        <div className="offer-title-input-wrap">
          <span className="offer-title-eyebrow">Editing · {offer.id}</span>
          <div className="offer-title-input-row">
            <input className="offer-title-input" value={name} onChange={(e) => setName(e.target.value)} placeholder={offer.name} maxLength={20} size={Math.max((name || offer.name || '').length, 6)} />
            <Icon d={ICONS.edit} size={20} className="offer-title-pencil" />
          </div>
          <textarea
            className="offer-notes-input"
            value={notes}
            onChange={(e) => setNotes(e.target.value)}
            placeholder="Add notes (optional)"
            rows={1}
            maxLength={100}
          />
        </div>
        <div className="offer-titlebar-actions">
          <button className="ghost-btn" onClick={() => go({ view: 'offer-history' })}>Cancel</button>
          <button className="ghost-btn" onClick={() => { if (confirm('Delete this offer?')) { removeOffer(offer.id); go({ view: 'offer-history' }); } }}>
            <Icon d={ICONS.trash} size={12} /> Delete
          </button>
          <button className="primary-btn lg" onClick={save}>
            <Icon d={ICONS.offer} size={14} /> Save changes
          </button>
        </div>
      </div>
      <section className="card offer-summary">
        <div className="offer-sum-grid">
          <div>
            <div className="eyebrow">Total median value</div>
            <div className="big-num-val">{fmtPrice(agg.totalMedian)}</div>
            <div className="muted small">{agg.totalQty} item{agg.totalQty === 1 ? '' : 's'} across {agg.lines.length} row{agg.lines.length === 1 ? '' : 's'}</div>
          </div>
          <div>
            <div className="eyebrow">Value distribution</div>
            <PriceRangeViz lines={agg.lines} perUnit />
          </div>
          <div>
            <div className="eyebrow">Condition mix</div>
            <div className="cond-mix-bar">
              <span className="cm-seg cm-A" style={{ width: agg.condPct.A + '%' }}></span>
              <span className="cm-seg cm-B" style={{ width: agg.condPct.B + '%' }}></span>
              <span className="cm-seg cm-C" style={{ width: agg.condPct.C + '%' }}></span>
            </div>
            <div className="cond-mix-legend">
              <span><i className="dot dot-A"></i>A {agg.condPct.A}%</span>
              <span><i className="dot dot-B"></i>B {agg.condPct.B}%</span>
              <span><i className="dot dot-C"></i>C {agg.condPct.C}%</span>
            </div>
          </div>
        </div>
      </section>

      <section className="card searchcard">
        <div className="offer-add-row">
          <div className="search">
            <Icon d={ICONS.search} size={16} />
            <input value={search} onChange={(e) => setSearch(e.target.value)} placeholder="Search a product to add …" />
            {search && <button className="link" onClick={() => setSearch('')}>clear</button>}
          </div>
          <div className="offer-add-actions">
            <input type="file" ref={fileRef} accept=".csv,text/csv" style={{display:'none'}} onChange={onUpload} />
            <button className="ghost-btn" onClick={() => fileRef.current && fileRef.current.click()}>
              <Icon d={ICONS.upload} size={14} /> Upload CSV
            </button>
          </div>
        </div>
        {matches.length > 0 && (
          <div className="search-results">
            {matches.map(m => (
              <button key={m.id} className="search-result" onClick={() => { addModel(m.id); setSearch(''); }}>
                <ProductGlyph product={m} size={32} tone="cream" />
                <div className="search-result-text">
                  <strong>{m.name}</strong>
                  <span>{m.brand} · adds a row with all variants matched — narrow below</span>
                </div>
                <span className="search-result-price"><Icon d={ICONS.plus} size={14} /> Add</span>
              </button>
            ))}
          </div>
        )}
      </section>

      <section className="card">
        <div className="card-head">
          <div>
            <div className="eyebrow">Items</div>
            <h3 className="h3">{agg.totalQty} item{agg.totalQty === 1 ? '' : 's'}</h3>
          </div>
        </div>
        {agg.lines.length === 0 ? (
          <div className="empty-inline">No items — search above to add some.</div>
        ) : (
          <div className="offer-lines">
            <div className="offer-line-head v2">
              <span>Product</span><span>Variant</span><span>Qty</span><span>Unit median</span><span>Subtotal</span><span></span>
            </div>
            {agg.lines.map(l => (
              <OfferLineRow
                key={l.lid}
                line={l}
                collapsed={isCollapsed(l.lid)}
                onToggleCollapse={() => toggleLine(l.lid)}
                onChange={(patch) => setLine(l.lid, patch)}
                onRemove={() => removeLine(l.lid)}
              />
            ))}
            <div className="offer-line-foot v2">
              <span>Total</span>
              <span className="num"><strong>{agg.totalQty}</strong></span>
              <span className="num muted">{fmtPrice(agg.totalLow)}–{fmtPrice(agg.totalHigh)}</span>
              <span className="num"><strong>{fmtPrice(agg.totalMedian)}</strong></span>
            </div>
          </div>
        )}
      </section>
    </div>
  );
}

Object.assign(window, { OfferCreator, OfferHistory, OffersIndex, OfferEditor, loadOffers, saveOffers, loadDraft, saveDraft, loadDrafts, saveDrafts, addLineFromVariant: null });
