// transactions.jsx — sortable/filterable transactions table

const { useState: txState, useMemo } = React;

function TransactionsScreen({ db, setDb, navTo, push }) {
  const all = db.transactions;
  const months = useMemo(() => {
    const set = new Set(all.map(t => monthOf(t.date)));
    return Array.from(set).sort().reverse();
  }, [all]);
  const cats = db.categories;

  const [filterMonth, setFilterMonth] = txState("all");
  const [filterCat, setFilterCat] = txState("all");
  const [filterPair, setFilterPair] = txState("all"); // all / paired / unpaired / review
  const [filterSource, setFilterSource] = txState("all");
  const [q, setQ] = txState("");
  const [sort, setSort] = txState({ field: "date", dir: "desc" });
  const [selected, setSelected] = txState(new Set());
  const [showManual, setShowManual] = txState(false);

  // Phase 4 wiring: the inert "Add manual entry" header button calls
  // window.__openManualEntry; register it while this screen is mounted.
  React.useEffect(() => {
    window.__openManualEntry = () => setShowManual(true);
    return () => { if (window.__openManualEntry) delete window.__openManualEntry; };
  }, []);

  const filtered = useMemo(() => {
    let rows = all.slice();
    if (filterMonth !== "all") rows = rows.filter(t => monthOf(t.date) === filterMonth);
    if (filterCat !== "all")   rows = rows.filter(t => t.category === filterCat);
    if (filterSource !== "all")rows = rows.filter(t => t.source === filterSource);
    if (filterPair === "paired")   rows = rows.filter(t => !!t.receiptId);
    if (filterPair === "unpaired") rows = rows.filter(t => !t.receiptId);
    if (filterPair === "review")   rows = rows.filter(t => t.needsReview);
    if (q.trim()) {
      const qs = q.trim().toLowerCase();
      rows = rows.filter(t =>
        t.description.toLowerCase().includes(qs) ||
        t.category.toLowerCase().includes(qs) ||
        (t.note || "").toLowerCase().includes(qs)
      );
    }
    rows.sort((a, b) => {
      const f = sort.field;
      let av = a[f], bv = b[f];
      if (f === "amount") { av = a.amount; bv = b.amount; }
      if (av < bv) return sort.dir === "asc" ? -1 : 1;
      if (av > bv) return sort.dir === "asc" ? 1 : -1;
      return 0;
    });
    return rows;
  }, [all, filterMonth, filterCat, filterPair, filterSource, q, sort]);

  // Totals for filtered view
  const totals = useMemo(() => {
    let inc = 0, exp = 0;
    filtered.forEach(t => { if (t.amount > 0) inc += t.amount; else exp += -t.amount; });
    return { inc, exp, net: inc - exp, count: filtered.length };
  }, [filtered]);

  // Phase 4 wiring: re-categorize a transaction. Optimistic patch, then call
  // the API; reconcile with the server-returned row (authoritative — it flips
  // `edited` to true); roll back + error-toast if the call rejects.
  const updateTxn = (id, patch) => {
    const prevRow = all.find(t => t.id === id);
    setDb(prev => ({
      ...prev,
      transactions: prev.transactions.map(t => t.id === id ? { ...t, ...patch } : t),
    }));
    ViewerAPI.patchTransaction(id, { category_id: patch.category })
      .then(resp => {
        const server = resp && resp.transaction;
        if (!server) return;
        setDb(prev => ({
          ...prev,
          transactions: prev.transactions.map(t => t.id === id ? { ...t, ...server } : t),
        }));
      })
      .catch(err => {
        // roll back to the pre-edit row
        setDb(prev => ({
          ...prev,
          transactions: prev.transactions.map(t => t.id === id ? { ...t, ...prevRow } : t),
        }));
        push("Couldn't save: " + err.message);
      });
  };

  return (
    <div className="stack">
      {/* KPI strip */}
      <div className="grid-4">
        <KPICard label={`Transactions${filterMonth==="all"?" · all":""}`} value={totals.count.toString()} sub={
          filterMonth === "all" ? "Across all months in view" : monthLabel(filterMonth)
        }/>
        <KPICard label="Income (filtered)" value={fmt(totals.inc, false)} sub={`${filtered.filter(t=>t.amount>0).length} deposits`} />
        <KPICard label="Expenses (filtered)" value={"−" + fmt(totals.exp, false)} sub={`${filtered.filter(t=>t.amount<0).length} debits`} />
        <KPICard label="Needs review" value={String(all.filter(t=>t.needsReview).length)} sub="Unresolved across all data" />
      </div>

      {/* Table card */}
      <div className="card flush">
        <div className="filters">
          <div className="search" style={{ flex: "0 1 320px", minWidth: 200 }}>
            <Icon name="search" size={14} />
            <input
              className="input"
              placeholder="Search description, category, notes…"
              value={q}
              onChange={e => setQ(e.target.value)}
            />
          </div>
          <div className="row" style={{ gap: 6 }}>
            <span className="label">Month</span>
            <select className="select" value={filterMonth} onChange={e => setFilterMonth(e.target.value)}>
              <option value="all">All</option>
              {months.map(m => <option key={m} value={m}>{monthLabel(m)}</option>)}
            </select>
          </div>
          <div className="row" style={{ gap: 6 }}>
            <span className="label">Category</span>
            <select className="select" value={filterCat} onChange={e => setFilterCat(e.target.value)}>
              <option value="all">All</option>
              {cats.map(c => <option key={c.id} value={c.id}>{c.label}</option>)}
            </select>
          </div>
          <div className="row" style={{ gap: 6 }}>
            <span className="label">Source</span>
            <Seg value={filterSource} onChange={setFilterSource} options={[
              { value: "all", label: "All" },
              { value: "bank", label: "Bank" },
              { value: "jobber", label: "Jobber" },
              { value: "wix", label: "Wix" },
            ]} />
          </div>
          <div className="row" style={{ gap: 6, marginLeft: "auto" }}>
            <Seg value={filterPair} onChange={setFilterPair} options={[
              { value: "all", label: "All" },
              { value: "paired", label: "Paired" },
              { value: "unpaired", label: "Unpaired" },
              { value: "review", label: "Review" },
            ]} />
          </div>
        </div>

        <div className="table-wrap">
          <table className="t">
            <thead>
              <tr>
                <SortHeader field="date" label="Date" sort={sort} setSort={setSort} />
                <th>Status</th>
                <SortHeader field="description" label="Description" sort={sort} setSort={setSort} />
                <th>Category</th>
                <SortHeader field="amount" label="Amount" sort={sort} setSort={setSort} align="right" />
                <th>Receipt</th>
              </tr>
            </thead>
            <tbody>
              {filtered.length === 0 && (
                <tr><td colSpan={6} style={{ textAlign: "center", padding: 32, color: "var(--ink-3)" }}>
                  No transactions match the current filters.
                </td></tr>
              )}
              {filtered.map(t => {
                const paired = !!t.receiptId;
                const receipt = paired ? db.receipts.find(r => r.id === t.receiptId) : null;
                return (
                  <tr key={t.id} onClick={() => navTo({ screen: "txn", id: t.id })}>
                    <td className="nowrap mono" style={{ color: "var(--ink-2)" }}>{fmtDate(t.date)}</td>
                    <td>
                      <div className="row" style={{ gap: 4 }}>
                        <PairedPill paired={paired} />
                        {t.needsReview && <ReviewPill needsReview />}
                        {t.source !== "bank" && <Pill kind="info">{t.source}</Pill>}
                      </div>
                    </td>
                    <td>
                      <div style={{ fontWeight: 500 }}>{t.description}</div>
                      {t.note && <div className="tiny muted">{t.note}</div>}
                    </td>
                    <td onClick={e => e.stopPropagation()}>
                      <InlineSelect
                        value={t.category}
                        options={db.categories.map(c => ({ value: c.id, label: c.label }))}
                        onChange={(v) => { updateTxn(t.id, { category: v }); push(`Re-categorized to ${db.categories.find(c=>c.id===v).label}`); }}
                      />
                    </td>
                    <td className={"right amt " + (t.amount < 0 ? "neg" : "pos")}>
                      {(t.amount >= 0 ? "+" : "−") + fmt(Math.abs(t.amount))}
                    </td>
                    <td>
                      {paired
                        ? <a className="row tiny" style={{ gap: 4, color: "var(--accent)" }} onClick={e => { e.stopPropagation(); navTo({ screen: "receipt", id: receipt.id }); }}>
                            <Icon name="paperclip" size={12} />
                            {receipt.vendorShort}
                          </a>
                        : <span className="tiny muted">—</span>
                      }
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>

        <footer className="row between" style={{ padding: "10px 16px", borderTop: "1px solid var(--border)", background: "var(--paper)", fontSize: 12, color: "var(--ink-3)" }}>
          <span>{filtered.length} of {all.length} transactions · net {fmtSigned(totals.net, false)}</span>
          <span>Click any row to see the paired receipt inline.</span>
        </footer>
      </div>

      {showManual && (
        <ManualEntryModal
          db={db}
          setDb={setDb}
          push={push}
          onClose={() => setShowManual(false)}
        />
      )}
    </div>
  );
}

// ────────────────────────────────────────────────────────────────────────────
// Manual entry modal (Phase 4 — net-new UI for POST /books/viewer/transaction)
// ────────────────────────────────────────────────────────────────────────────
function ManualEntryModal({ db, setDb, push, onClose }) {
  const [date, setDate] = txState(db.today || "");
  const [description, setDescription] = txState("");
  const [amount, setAmount] = txState("");
  const [category, setCategory] = txState(db.categories[0] ? db.categories[0].id : "");
  const [note, setNote] = txState("");
  const [saving, setSaving] = txState(false);
  const [formErr, setFormErr] = txState(null);

  const submit = () => {
    setFormErr(null);
    if (!date) { setFormErr("Pick a date."); return; }
    if (!description.trim()) { setFormErr("Enter a description."); return; }
    const amt = parseFloat(amount);
    if (isNaN(amt) || amt === 0) { setFormErr("Enter a non-zero amount (negative for an expense)."); return; }
    setSaving(true);
    const fields = {
      date,
      description: description.trim(),
      amount: amt,
      category_id: category,
    };
    if (note.trim()) fields.note = note.trim();
    ViewerAPI.addTransaction(fields)
      .then(resp => {
        const server = resp && resp.transaction;
        if (server) {
          // Insert the server-created row (carries the real DB id + edited/isManual).
          setDb(prev => ({
            ...prev,
            transactions: [server, ...prev.transactions],
          }));
        }
        push("Manual entry added");
        onClose();
      })
      .catch(err => {
        setSaving(false);
        setFormErr(err.message);
      });
  };

  return (
    <Modal
      title="Add manual entry"
      onClose={onClose}
      footer={
        <>
          <button className="btn" onClick={onClose} disabled={saving}>Cancel</button>
          <button className="btn primary" onClick={submit} disabled={saving}>
            {saving ? "Saving…" : "Add transaction"}
          </button>
        </>
      }
    >
      <div className="manual-form">
        <div className="field-row">
          <div className="field">
            <label>Date</label>
            <input className="input" type="date" value={date} onChange={e => setDate(e.target.value)} />
          </div>
          <div className="field">
            <label>Amount (− for expense)</label>
            <input className="input" inputMode="decimal" placeholder="-124.50"
                   value={amount} onChange={e => setAmount(e.target.value)} />
          </div>
        </div>
        <div className="field">
          <label>Description</label>
          <input className="input" placeholder="e.g. Sherwin-Williams — lacquer"
                 value={description} onChange={e => setDescription(e.target.value)} />
        </div>
        <div className="field">
          <label>Category</label>
          <select className="select" value={category} onChange={e => setCategory(e.target.value)}>
            {db.categories.map(c => <option key={c.id} value={c.id}>{c.label}</option>)}
          </select>
        </div>
        <div className="field">
          <label>Note (optional)</label>
          <input className="input" placeholder="Anything worth remembering"
                 value={note} onChange={e => setNote(e.target.value)} />
        </div>
        {formErr && <div className="form-err">{formErr}</div>}
      </div>
    </Modal>
  );
}

window.TransactionsScreen = TransactionsScreen;
