// Phase 6a. Script/CSS wiring lives in docs/phase6a-wiring.md.
function TransactionsPage({ review = false }) {
  const t = window.useTables(["transactions", "categories"]);
  const Tx = window.AreteTransactions;
  const preview = window.isPreview();
  const [search, setSearch] = React.useState("");
  const [account, setAccount] = React.useState("");
  const [category, setCategory] = React.useState("");
  const [patches, setPatches] = React.useState({});
  const [pending, setPending] = React.useState([]);
  const busy = React.useRef(new Set());
  const [error, setError] = React.useState("");
  const [notice, setNotice] = React.useState("");
  const status = React.useRef(null);
  const prefix = React.useId();

  if (t.status === "loading") return <Loading />;
  if (t.status === "error") return <LoadError error={t.error} onRetry={t.reload} />;
  const source = t.data.transactions || [];
  const categories = Tx.categoryOptions(t.data.categories || []);
  const rows = source.map(row => ({ ...row, ...patches[row.txn_id] }));
  const accounts = [...new Set(source.map(row => row.account).filter(Boolean))].sort();
  const filtered = Tx.filterRows(rows, { review, search, account, category, pending });
  const groups = Tx.groupMonths(filtered);
  const displayCategory = value => value.replace(/-/g, " ");
  const monthLabel = value => /^\d{4}-\d{2}$/.test(value)
    ? new Intl.DateTimeFormat("en-US", { month: "long", year: "numeric", timeZone: "UTC" }).format(new Date(value + "-01T12:00:00Z")) : value;
  const dateLabel = value => /^\d{4}-\d{2}-\d{2}$/.test(value || "")
    ? new Intl.DateTimeFormat("en-US", { month: "short", day: "numeric", timeZone: "UTC" }).format(new Date(value + "T12:00:00Z")) : "—";

  async function save(row, next) {
    if (preview || busy.current.has(row.txn_id)) return;
    busy.current.add(row.txn_id);
    setPending(ids => [...ids, row.txn_id]);
    setError(""); setNotice("");
    const previous = patches[row.txn_id];
    try {
      await Tx.saveCategory((name, args) => window.supaClient.rpc(name, args), row, next,
        patch => setPatches(old => ({ ...old, [row.txn_id]: patch })),
        () => setPatches(old => {
          const restored = { ...old };
          if (previous) restored[row.txn_id] = previous;
          else delete restored[row.txn_id];
          return restored;
        }));
      setNotice("Category saved. The ledger on the Mac will update at its next sync.");
      if (review) status.current?.focus();
    } catch (_) {
      setError("Could not save the category. The row has been restored. Reload to check the saved value, or try again.");
    } finally {
      busy.current.delete(row.txn_id);
      setPending(ids => ids.filter(id => id !== row.txn_id));
    }
  }

  return <section className="phase6a">
    <p className="sub">Pick a category. Changes go back to the ledger file on the Mac at its next sync.</p>
    {preview && <NoData>Preview only. Category changes are off.</NoData>}
    {!categories.length && <NoData>Categories have not arrived yet. The Mac must send the chart of accounts before you can pick a category.</NoData>}
    <div className="txn-filters" role="search" aria-label="Filter transactions">
      <label htmlFor={prefix + "search"}>Search
        <input id={prefix + "search"} type="search" placeholder="Merchant or description" value={search} onChange={e => setSearch(e.target.value)} />
      </label>
      <label htmlFor={prefix + "account"}>Account
        <select id={prefix + "account"} value={account} onChange={e => setAccount(e.target.value)}>
          <option value="">All accounts</option>{accounts.map(name => <option key={name} value={name}>{name}</option>)}
        </select>
      </label>
      <label htmlFor={prefix + "category"}>Category
        <select id={prefix + "category"} value={category} onChange={e => setCategory(e.target.value)}>
          <option value="">All categories</option><option value="__empty__">No category</option>
          {[...new Set([...categories.map(c => c.category), ...source.map(r => r.category).filter(Boolean)])].map(name =>
            <option key={name} value={name}>{displayCategory(name)}</option>)}
        </select>
      </label>
      {(search || account || category) && <button className="linkbtn" onClick={() => { setSearch(""); setAccount(""); setCategory(""); }}>Clear filters</button>}
    </div>
    {error && <div className="notice" role="alert">{error}</div>}
    <p ref={status} tabIndex={-1} className="txn-status" role="status" aria-live="polite">{notice || `${filtered.length} transaction${filtered.length === 1 ? "" : "s"}`}</p>
    {!source.length ? <NoData>No transactions yet. They arrive with the Mac ledger sync.</NoData>
      : !filtered.length ? <NoData>{search || account || category ? "No transactions match these filters." : review ? "All caught up. No transactions need review." : "No transactions to show."}</NoData>
      : <div className="tcard"><div className="scrollx" tabIndex={0} role="region" aria-label={review ? "Transactions needing review" : "All transactions"}>
        <table className="tbl stack txn-table">
          <thead><tr><th scope="col">Date</th><th scope="col">Merchant</th><th scope="col">Account</th><th scope="col" className="r">Amount</th><th scope="col">Category</th></tr></thead>
          {groups.map(group => <tbody key={group.month}>
            <tr className="grp"><th scope="rowgroup" colSpan={5}>{monthLabel(group.month)}</th></tr>
            {group.rows.map(row => {
              const saving = pending.includes(row.txn_id);
              const currentKnown = categories.some(c => c.category === row.category);
              const pickerId = prefix + "pick-" + row.txn_id;
              return <tr key={row.txn_id} aria-busy={saving}>
                <td data-l="Date" className="txn-date">{dateLabel(row.date)}</td>
                <td data-l="Merchant"><div className="txn-merchant">{row.merchant || row.description || "No description"}
                  {row.merchant && row.description && row.description !== row.merchant && <span className="txn-description">{row.description}</span>}
                </div></td>
                <td data-l="Account"><span className="txn-account">{row.account || "—"}</span></td>
                <td data-l="Amount" className="r num"><span className="txn-amount">{Number(row.amount) > 0 ? "+" : ""}{money(row.amount, 2)}<span className="sr">{Number(row.amount) < 0 ? " money out" : Number(row.amount) > 0 ? " money in" : ""}</span></span></td>
                <td data-l="Category"><div className="txn-picker">
                  <label className="sr" htmlFor={pickerId}>Category for {row.merchant || row.description || "transaction"}, {row.date}, {row.account || "no account"}</label>
                  <select id={pickerId} className={"select" + (Tx.needsReview(row) ? " need" : "")} value={row.category || ""}
                    disabled={preview || saving || !categories.length} onChange={e => save(row, e.target.value)}>
                    <option value="" disabled>Pick category</option>
                    {row.category && !currentKnown && <option value={row.category} disabled>{displayCategory(row.category)} (not in chart)</option>}
                    {categories.map(c => <option key={c.category} value={c.category}>{displayCategory(c.category)}</option>)}
                  </select>
                  <span className="txn-review-state">{saving ? "Saving…" : Tx.needsReview(row) ? "Needs review" : "Reviewed"}</span>
                  {Tx.canMarkReviewed(row) && <button className="linkbtn" disabled={preview || saving}
                    onClick={() => save(row, row.category)}>Mark reviewed</button>}
                </div></td>
              </tr>;
            })}
          </tbody>)}
        </table>
      </div></div>}
  </section>;
}

function TransactionsReviewPage() { return <TransactionsPage review />; }
function TransactionsAllPage() { return <TransactionsPage />; }
window.ARETE_PAGES = Object.assign(window.ARETE_PAGES || {}, {
  "transactions/review": TransactionsReviewPage,
  "transactions/all": TransactionsAllPage,
});
