/* Transaction Ledger — filter, search, sort, categorize inline, add new */

const Ledger = ({ goTo, initialAddOpen, txState, setTxState }) => {
  const [txns, setTxns] = [txState, setTxState];
  const [query, setQuery] = React.useState("");
  const [typeFilter, setTypeFilter] = React.useState("all"); // all | income | expense
  const [catFilter, setCatFilter] = React.useState(null);
  const [monthFilter, setMonthFilter] = React.useState("all");
  const [sort, setSort] = React.useState({ key: "date", dir: "desc" });
  const [addOpen, setAddOpen] = React.useState(!!initialAddOpen);
  const [importOpen, setImportOpen] = React.useState(false);
  const [editingCat, setEditingCat] = React.useState(null); // tx id
  const [selected, setSelected] = React.useState(null);
  const [editTx, setEditTx] = React.useState(null);
  // ── Bulk selection state ──
  const [selectedIds, setSelectedIds] = React.useState(new Set());
  const [bulkCatOpen, setBulkCatOpen] = React.useState(false);

  // If another page navigated here with a specific transaction to open
  // (e.g. "Open transaction" from the Receipt vault), pick it up and
  // open the detail modal immediately.
  React.useEffect(() => {
    if (window.__openTxId) {
      const target = txState.find(t => t.id === window.__openTxId);
      window.__openTxId = null;
      if (target) setSelected(target);
    }
  }, []);

  const months = Object.keys(window.CB_DATA.monthly).sort().reverse();

  const filtered = txns.filter(t => {
    if (typeFilter !== "all" && t.type !== typeFilter) return false;
    if (catFilter && t.category !== catFilter) return false;
    if (monthFilter !== "all" && !t.date.startsWith(monthFilter)) return false;
    if (query) {
      const q = query.toLowerCase();
      const cat = CB.catById(t.category)?.name?.toLowerCase() || "";
      if (!(t.vendor || "").toLowerCase().includes(q) && !(t.note || "").toLowerCase().includes(q) && !cat.includes(q)) return false;
    }
    return true;
  });

  filtered.sort((a, b) => {
    let av, bv;
    if (sort.key === "date") { av = a.date + a.id; bv = b.date + b.id; }
    else if (sort.key === "amount") { av = a.amount; bv = b.amount; }
    else if (sort.key === "vendor") { av = a.vendor; bv = b.vendor; }
    else if (sort.key === "category") { av = CB.catById(a.category)?.name || ""; bv = CB.catById(b.category)?.name || ""; }
    if (av < bv) return sort.dir === "asc" ? -1 : 1;
    if (av > bv) return sort.dir === "asc" ? 1 : -1;
    return 0;
  });

  const totals = filtered.reduce((acc, t) => {
    const usd = CB.txToUSD(t);
    acc[t.type] += usd;
    return acc;
  }, { income: 0, expense: 0 });

  const toggleSort = (key) => {
    setSort(s => s.key === key ? { key, dir: s.dir === "asc" ? "desc" : "asc" } : { key, dir: "desc" });
  };

  const recategorize = async (txId, newCat) => {
    try {
      if (window.CB_API) await window.CB_API.updateTransaction(txId, { categoryId: newCat });
      setTxns(ts => ts.map(t => t.id === txId ? { ...t, category: newCat } : t));
    } catch(e) {
      window.toast?.({ type: "error", message: "Failed to save" });
    }
    setEditingCat(null);
  };

  // ── Bulk actions ──
  const toggleSelect = (id) => setSelectedIds(s => {
    const n = new Set(s);
    if (n.has(id)) n.delete(id); else n.add(id);
    return n;
  });
  const selectAllVisible = () => setSelectedIds(new Set(filtered.map(t => t.id)));
  const clearSelection = () => setSelectedIds(new Set());
  const bulkRecategorize = async (catId) => {
    const ids = [...selectedIds];
    try {
      if (window.CB_API) await window.CB_API.bulkTransactions({ ids, action: "recategorize", payload: { categoryId: catId } });
      setTxns(ts => ts.map(t => ids.includes(t.id) ? { ...t, category: catId, needsReview: false } : t));
      const cat = CB.catById(catId);
      window.toast?.({ message: `Recategorized ${ids.length} transaction${ids.length === 1 ? "" : "s"} as ${cat?.name}` });
      clearSelection();
      setBulkCatOpen(false);
    } catch(e) {
      window.toast?.({ type: "error", message: "Failed to recategorize" });
    }
  };
  const bulkDelete = async () => {
    if (!confirm(`Delete ${selectedIds.size} transaction${selectedIds.size === 1 ? "" : "s"}?`)) return;
    const ids = [...selectedIds];
    try {
      if (window.CB_API) await window.CB_API.bulkTransactions({ ids, action: "delete" });
      setTxns(ts => ts.filter(t => !ids.includes(t.id)));
      window.toast?.({ type: "info", message: `Deleted ${ids.length} transaction${ids.length === 1 ? "" : "s"}` });
      clearSelection();
    } catch(e) {
      window.toast?.({ type: "error", message: "Failed to delete" });
    }
  };
  const bulkMarkReviewed = async () => {
    const ids = [...selectedIds];
    try {
      if (window.CB_API) await window.CB_API.bulkTransactions({ ids, action: "mark-reviewed" });
      setTxns(ts => ts.map(t => ids.includes(t.id) ? { ...t, needsReview: false } : t));
      window.toast?.({ message: `Marked ${ids.length} as reviewed` });
      clearSelection();
    } catch(e) {
      window.toast?.({ type: "error", message: "Failed to save" });
    }
  };

  const allCats = [...window.CB_DATA.categories.income, ...window.CB_DATA.categories.expense];

  return (
    <div className="page-fade">
      <div className="page-head">
        <div>
          <h1 className="page-title">Transactions</h1>
          <div className="page-sub">
            {filtered.length} of {txns.length} · Income {CB.fmt(totals.income)} · Expenses {CB.fmt(totals.expense)}
          </div>
        </div>
        <div className="page-actions">
          <button className="btn" onClick={() => setImportOpen(true)}><Icon name="upload" size={14} /> Import</button>
          <button className="btn" onClick={() => goTo("export")}><Icon name="download" size={14} /> Export</button>
          <button className="btn btn-primary" onClick={() => setAddOpen(true)}>
            <Icon name="plus" size={14} /> Add transaction
          </button>
        </div>
      </div>

      {/* Filters */}
      <div className="filters">
        <div className="search">
          <Icon name="search" size={15} color="var(--muted)" />
          <input
            placeholder="Search vendor, category, note…"
            value={query}
            onChange={(e) => setQuery(e.target.value)}
          />
          {query && (
            <button onClick={() => setQuery("")} style={{ background: "none", border: "none", cursor: "pointer", padding: "4px 6px", display: "flex", alignItems: "center", color: "var(--ink)" }}>
              <Icon name="close" size={14} />
            </button>
          )}
        </div>
        <div className="segmented">
          <button className={typeFilter === "all" ? "on" : ""} onClick={() => setTypeFilter("all")}>All</button>
          <button className={typeFilter === "income" ? "on" : ""} onClick={() => setTypeFilter("income")}>Income</button>
          <button className={typeFilter === "expense" ? "on" : ""} onClick={() => setTypeFilter("expense")}>Expenses</button>
        </div>
        <select className="input" style={{ width: "auto", padding: "7px 12px" }} value={monthFilter} onChange={(e) => setMonthFilter(e.target.value)}>
          <option value="all">All time</option>
          {months.map(m => <option key={m} value={m}>{CB.monthLabel(m)}</option>)}
        </select>
        {catFilter ? (
          <button className="chip on" onClick={() => setCatFilter(null)}>
            <span className="cat-swatch" style={{ background: CB.catById(catFilter)?.color }} />
            {CB.catById(catFilter)?.name}
            <Icon name="close" size={12} />
          </button>
        ) : (
          <div style={{ position: "relative" }}>
            <CategoryFilter onPick={setCatFilter} />
          </div>
        )}
        {(query || catFilter || typeFilter !== "all" || monthFilter !== "all") && (
          <button className="btn btn-ghost btn-sm" onClick={() => { setQuery(""); setCatFilter(null); setTypeFilter("all"); setMonthFilter("all"); }}>
            Clear filters
          </button>
        )}
      </div>

      <div className="card" style={{ padding: 0, overflow: "hidden", position: "relative" }}>
        {selectedIds.size > 0 && (
          <div style={{
            display: "flex", alignItems: "center", gap: 10,
            padding: "10px 16px",
            background: "var(--ink)", color: "var(--bg)",
            position: "sticky", top: 0, zIndex: 5,
          }}>
            <Icon name="check" size={14} />
            <span style={{ fontSize: 13, fontWeight: 500 }}>
              {selectedIds.size} selected
            </span>
            <button className="btn btn-sm" style={{ marginLeft: 12 }} onClick={() => setBulkCatOpen(o => !o)}>
              <Icon name="tag" size={11} /> Recategorize
            </button>
            <button className="btn btn-sm" onClick={bulkMarkReviewed}>
              <Icon name="check" size={11} /> Mark reviewed
            </button>
            <button className="btn btn-sm" onClick={bulkDelete} style={{ color: "var(--expense)" }}>
              <Icon name="close" size={11} /> Delete
            </button>
            <button className="btn btn-ghost btn-sm" onClick={clearSelection} style={{ marginLeft: "auto", color: "var(--bg)" }}>
              Clear
            </button>
            {bulkCatOpen && (
              <div style={{
                position: "absolute", top: "100%", left: 16,
                background: "var(--surface)", border: "1px solid var(--hairline)",
                borderRadius: 12, padding: 6, marginTop: 4,
                boxShadow: "var(--shadow-2)", zIndex: 10,
                maxHeight: 360, overflowY: "auto",
              }}>
                <div style={{ fontSize: 11, color: "var(--muted)", padding: "8px 10px 4px", textTransform: "uppercase", letterSpacing: "0.05em" }}>Pick a category</div>
                {[...allCats].map(c => (
                  <button key={c.id} className="nav-item" style={{ padding: "7px 10px", width: 220 }}
                    onClick={() => bulkRecategorize(c.id)}>
                    <span className="cat-swatch" style={{ background: c.color }} />
                    <span style={{ fontSize: 13 }}>{c.name}</span>
                  </button>
                ))}
              </div>
            )}
          </div>
        )}
        <table className="tbl">
          <thead>
            <tr>
              <th style={{ width: 30 }}>
                <input type="checkbox"
                  checked={filtered.length > 0 && filtered.every(t => selectedIds.has(t.id))}
                  onChange={(e) => e.target.checked ? selectAllVisible() : clearSelection()}
                  aria-label="Select all visible" />
              </th>
              <th style={{ width: 28 }}></th>
              <th onClick={() => toggleSort("date")} style={{ cursor: "pointer", width: 100 }}>
                Date <SortIcon active={sort.key === "date"} dir={sort.dir} />
              </th>
              <th onClick={() => toggleSort("vendor")} style={{ cursor: "pointer" }}>
                Vendor / Description <SortIcon active={sort.key === "vendor"} dir={sort.dir} />
              </th>
              <th onClick={() => toggleSort("category")} style={{ cursor: "pointer" }}>
                Category <SortIcon active={sort.key === "category"} dir={sort.dir} />
              </th>
              <th style={{ width: 130 }}>Account</th>
              <th style={{ width: 80 }}>Status</th>
              <th style={{ width: 36, textAlign: "center" }}>Receipt</th>
              <th onClick={() => toggleSort("amount")} style={{ cursor: "pointer", textAlign: "right", width: 130 }}>
                Amount <SortIcon active={sort.key === "amount"} dir={sort.dir} />
              </th>
            </tr>
          </thead>
          <tbody>
            {filtered.length === 0 && txState.length > 0 && (
              <tr><td colSpan="9" className="empty">
                No transactions match your filters.
              </td></tr>
            )}
            {filtered.length === 0 && txState.length === 0 && (
              <tr><td colSpan="9" style={{ padding: 0, border: 0 }}>
                <EmptyState
                  icon="ledger"
                  title="No transactions yet"
                  subtitle="Connect a bank or YouTube account, import a CSV/XLSX statement, or add one manually."
                  action={{ label: "Add transaction", icon: "plus", onClick: () => setAddOpen(true) }}
                  secondary={{ label: "Import statement", icon: "upload", onClick: () => setImportOpen(true) }}
                />
              </td></tr>
            )}
            {filtered.map(tx => {
              const cat = CB.catById(tx.category);
              const acc = CB.accountById(tx.account);
              const isSelected = selectedIds.has(tx.id);
              return (
                <tr key={tx.id} onClick={() => setSelected(tx)} className="row-clickable"
                  style={{ background: isSelected ? "var(--primary-soft)" : undefined }}>
                  <td onClick={(e) => e.stopPropagation()}>
                    <input type="checkbox" checked={isSelected} onChange={() => toggleSelect(tx.id)} />
                  </td>
                  <td>
                    <span style={{
                      display: "inline-flex", width: 26, height: 26, borderRadius: 8,
                      background: tx.type === "income" ? "var(--income-soft)" : "var(--surface-2)",
                      alignItems: "center", justifyContent: "center",
                      color: tx.type === "income" ? "oklch(0.4 0.13 155)" : "var(--ink-soft)",
                    }}>
                      <Icon name={tx.type === "income" ? "arrowDown" : "arrowUp"} size={12} />
                    </span>
                  </td>
                  <td style={{ color: "var(--ink-soft)", fontFamily: "var(--font-mono)", fontSize: 12 }}>
                    {new Date(tx.date).toLocaleDateString("en-US", { month: "short", day: "2-digit" })}
                  </td>
                  <td>
                    <div style={{ fontWeight: 500 }}>
                      {tx.vendor}
                    </div>
                    {tx.note && <div style={{ fontSize: 11.5, color: "var(--muted)" }}>{tx.note}</div>}
                  </td>
                  <td onClick={(e) => { e.stopPropagation(); setEditingCat(tx.id); }}>
                    {editingCat === tx.id ? (
                      <CategoryPicker
                        type={tx.type}
                        current={tx.category}
                        onPick={(newCat) => recategorize(tx.id, newCat)}
                        onClose={() => setEditingCat(null)}
                      />
                    ) : (
                      <span className="pill" style={{ cursor: "pointer", background: "transparent", border: "1px dashed var(--hairline-strong)" }}>
                        <span className="cat-swatch" style={{ background: cat?.color, width: 8, height: 8 }} />
                        {cat?.name}
                      </span>
                    )}
                  </td>
                  <td style={{ fontSize: 12, color: "var(--ink-soft)" }}>
                    {acc?.name?.split(" ")[0]}{acc?.last4 && acc.last4 !== "—" ? ` ··${acc.last4}` : ""}
                  </td>
                  <td>
                    {tx.status === "pending"
                      ? <Pill kind="">Pending</Pill>
                      : <span style={{ fontSize: 12, color: "var(--muted)" }}>Cleared</span>
                    }
                  </td>
                  <td style={{ textAlign: "center" }} onClick={(e) => {
                    if (!tx.receipt) return;
                    e.stopPropagation();
                    window.__openReceiptTxId = tx.id;
                    goTo("receipts");
                  }}>
                    {tx.receipt && (
                      <span title="View receipt" style={{ cursor: "pointer", display: "inline-flex", color: "var(--income-text)" }}>
                        <Icon name="receipt" size={14} color="var(--income-text)" />
                      </span>
                    )}
                  </td>
                  <td className="num">
                    {(() => {
                      const baseCur = window.CB_DATA?.profile?.baseCurrency || "USD";
                      // Use stored historical rate (fxRateToUsd) when available,
                      // then convert USD → baseCur. Matches dashboard/P&L behaviour.
                      const usdAmt = CB.txToUSD(tx);
                      const displayAmt = baseCur !== "USD" ? CB.convert(usdAmt, "USD", baseCur) : usdAmt;
                      const sign = tx.type === "income" ? "+" : "−";
                      return (
                        <span className={tx.type === "income" ? "amt-income" : "amt-expense"}>
                          {sign}{CB.fmt(displayAmt)}
                        </span>
                      );
                    })()}
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>

      <AddTransactionModal
        open={addOpen}
        onClose={() => setAddOpen(false)}
        onAdd={async (t) => {
          try {
            if (window.CB_API) {
              const saved = await window.CB_API.createTransaction({
                date: t.date, type: t.type, categoryId: t.category,
                vendor: t.vendor, amount: t.amount, currency: t.currency || "USD",
                note: t.note, status: t.status || "cleared",
              });
              setTxns(ts => [saved, ...ts]);
            } else {
              setTxns(ts => [t, ...ts]);
            }
            setAddOpen(false);
            window.toast?.({ message: `Added "${t.vendor}" — ${CB.fmt(t.amount)}` });
          } catch(e) {
            window.toast?.({ type: "error", message: "Failed to save transaction" });
          }
        }}
      />

      <ImportTransactionsModal
        open={importOpen}
        onClose={() => setImportOpen(false)}
        existingTx={txState}
        onImport={async (rows) => {
          try {
            const baseCur = window.CB_DATA?.profile?.baseCurrency || "USD";
            if (window.CB_API) {
              // Send original currency + amount — backend handles FX rate lookup
              const saved = await Promise.all(rows.map(t => {
                const txCur = t.currency || baseCur;
                return window.CB_API.createTransaction({
                  date: t.date, type: t.type, categoryId: t.category,
                  vendor: t.vendor, amount: t.amount, note: t.note || "",
                  status: t.status || "cleared",
                  currency: txCur,
                });
              }));
              setTxns(ts => [...saved, ...ts]);
            } else {
              // Offline fallback — convert client-side (no backend for FX)
              const converted = rows.map(t => {
                const txCur = t.currency || baseCur;
                return {
                  ...t,
                  amount: txCur !== baseCur ? CB.convert(t.amount, txCur, baseCur) : t.amount,
                  currency: baseCur,
                };
              });
              setTxns(ts => [...converted, ...ts]);
            }
            setImportOpen(false);
            window.toast?.({ message: `Imported ${rows.length} transaction${rows.length === 1 ? "" : "s"}` });
          } catch(e) {
            console.error("[Import error]", e);
            window.toast?.({ type: "error", message: e?.message || "Failed to import" });
          }
        }}
      />

      <TransactionDetailModal
        tx={selected}
        onClose={() => setSelected(null)}
        onEdit={() => { setEditTx(selected); setSelected(null); }}
        onRecategorize={recategorize}
        goTo={goTo}
        onAttachReceipt={(updated) => {
          setTxns(ts => ts.map(t => t.id === updated.id ? { ...t, ...updated, receipt: true } : t));
          setSelected(prev => prev?.id === updated.id ? { ...prev, ...updated, receipt: true } : prev);
        }}
      />

      {editTx && (
        <EditTransactionModal
          tx={editTx}
          onClose={() => setEditTx(null)}
          onSave={async (data) => {
            try {
              if (window.CB_API) {
                const saved = await window.CB_API.updateTransaction(editTx.id, {
                  date: data.date,
                  type: data.type,
                  categoryId: data.categoryId,
                  vendor: data.vendor,
                  amount: data.amount,
                  currency: data.currency,
                  accountId: data.accountId,
                  status: data.status,
                  note: data.note,
                });
                setTxns(ts => ts.map(t => t.id === editTx.id ? saved : t));
              } else {
                setTxns(ts => ts.map(t => t.id === editTx.id ? {
                  ...t, ...data, category: data.categoryId
                } : t));
              }
              setEditTx(null);
              window.toast?.({ message: "Transaction updated" });
            } catch(e) {
              window.toast?.({ type: "error", message: "Failed to save changes" });
            }
          }}
        />
      )}
    </div>
  );
};

const SortIcon = ({ active, dir }) => (
  <svg width="10" height="10" viewBox="0 0 10 10" style={{ marginLeft: 4, verticalAlign: "middle", opacity: active ? 1 : 0.3 }}>
    <path d={dir === "asc" ? "M2 6l3-3 3 3" : "M2 4l3 3 3-3"} stroke="currentColor" strokeWidth="1.5" fill="none" />
  </svg>
);

const CategoryFilter = ({ onPick }) => {
  const [open, setOpen] = React.useState(false);
  const ref = React.useRef();
  React.useEffect(() => {
    const handler = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener("mousedown", handler);
    return () => document.removeEventListener("mousedown", handler);
  }, []);
  return (
    <div ref={ref} style={{ position: "relative" }}>
      <button className="chip" onClick={() => setOpen(o => !o)}>
        <Icon name="filter" size={12} /> Category
        <Icon name="chevronDown" size={12} />
      </button>
      {open && (
        <div style={{
          position: "absolute", top: "100%", left: 0, marginTop: 6,
          background: "var(--surface)", border: "1px solid var(--hairline)",
          borderRadius: 12, padding: 6, boxShadow: "var(--shadow-2)",
          zIndex: 20, minWidth: 200,
        }}>
          <div style={{ fontSize: 11, color: "var(--muted)", padding: "8px 10px 4px", textTransform: "uppercase", letterSpacing: "0.05em" }}>Income</div>
          {CB_DATA.categories.income.map(c => (
            <button key={c.id} className="nav-item" onClick={() => { onPick(c.id); setOpen(false); }} style={{ padding: "7px 10px" }}>
              <span className="cat-swatch" style={{ background: c.color }} />
              <span style={{ fontSize: 13 }}>{c.name}</span>
            </button>
          ))}
          <div style={{ fontSize: 11, color: "var(--muted)", padding: "8px 10px 4px", textTransform: "uppercase", letterSpacing: "0.05em" }}>Expense</div>
          {CB_DATA.categories.expense.map(c => (
            <button key={c.id} className="nav-item" onClick={() => { onPick(c.id); setOpen(false); }} style={{ padding: "7px 10px" }}>
              <span className="cat-swatch" style={{ background: c.color }} />
              <span style={{ fontSize: 13 }}>{c.name}</span>
            </button>
          ))}
        </div>
      )}
    </div>
  );
};

const CategoryPicker = ({ type, current, onPick, onClose }) => {
  const ref = React.useRef();
  React.useEffect(() => {
    const handler = (e) => { if (ref.current && !ref.current.contains(e.target)) onClose(); };
    document.addEventListener("mousedown", handler);
    return () => document.removeEventListener("mousedown", handler);
  }, [onClose]);
  const cats = type === "income" ? CB_DATA.categories.income : CB_DATA.categories.expense;
  return (
    <div ref={ref} style={{
      position: "absolute", marginTop: 4, zIndex: 30,
      background: "var(--surface)", border: "1px solid var(--hairline)",
      borderRadius: 12, padding: 6, boxShadow: "var(--shadow-2)",
      minWidth: 200,
    }}>
      {cats.map(c => (
        <button key={c.id} className="nav-item" onClick={() => onPick(c.id)} style={{ padding: "7px 10px" }}>
          <span className="cat-swatch" style={{ background: c.color }} />
          <span style={{ fontSize: 13, flex: 1 }}>{c.name}</span>
          {current === c.id && <Icon name="check" size={14} color="var(--primary)" />}
        </button>
      ))}
    </div>
  );
};

window.Ledger = Ledger;
window.SortIcon = SortIcon;
