"use client";

import Link from "next/link";
import { useEffect, useRef, useState } from "react";
import { AppShell } from "../../components/app-shell";
import { apiFetch } from "@/lib/api";

type TransactionRow = {
  viewRef: string;
  name?: string;
  description?: string;
  amount: string;
  category?: string | null;
  note?: string | null;
  attribution?: "mine" | "yours" | "ours";
  split?: {
    mode: "none" | "equal" | "custom";
    minePercent: number;
    yoursPercent: number;
    mineAmount: number;
    yoursAmount: number;
  };
  status?: string;
  hidden?: boolean;
  commentCount?: number;
  date: string;
};

type TransactionComment = {
  id: string;
  body: string;
  createdAt: string;
  updatedAt: string;
  author: {
    displayName: string;
    email?: string | null;
  };
};

type Account = {
  viewRef: string;
  institutionName: string;
  accountType: string;
  mask?: string | null;
};

type ImportResult = {
  imported: number;
  skipped: number;
  total?: number;
  errors?: string[];
};

type ImportBatchResult = {
  totalFiles: number;
  processedFiles: number;
  failedFiles: number;
  imported: number;
  skipped: number;
  total: number;
  results: Array<{
    fileName: string;
    imported: number;
    skipped: number;
    total: number;
    error?: string;
  }>;
};

type CsvMapping = {
  date: string;
  description: string;
  amount: string;
  category?: string;
  notes?: string;
  amountMultiplier?: 1 | -1;
};

type CsvPreview = {
  fileName: string;
  headerSignature: string;
  headers: string[];
  rowCount: number;
  mapping: Partial<CsvMapping>;
  remembered: boolean;
};

type CashflowRow = {
  month: string;
  income: string;
  expense: string;
  net: string;
};

type MerchantInsight = {
  merchant: string;
  total: string;
  count: number;
};

export default function TransactionsPage() {
  const [rows, setRows] = useState<TransactionRow[]>([]);
  const [status, setStatus] = useState("Loading transactions...");
  const [summary, setSummary] = useState<{
    total: string; count: number; income?: string; expense?: string; net?: string;
  } | null>(null);
  const [cashflow, setCashflow] = useState<CashflowRow[]>([]);
  const [merchants, setMerchants] = useState<MerchantInsight[]>([]);
  const [datePreset, setDatePreset] = useState("this_month");
  const [showFilters, setShowFilters] = useState(false);
  const [accounts, setAccounts] = useState<Account[]>([]);
  const [autoSync, setAutoSync] = useState(true);
  const [isSyncing, setIsSyncing] = useState(false);
  const [showManual, setShowManual] = useState(false);
  const [showImport, setShowImport] = useState(false);
  const [importStatus, setImportStatus] = useState("");
  const [importLoading, setImportLoading] = useState(false);
  const [csvFiles, setCsvFiles] = useState<File[]>([]);
  const [csvPreview, setCsvPreview] = useState<CsvPreview | null>(null);
  const [csvMapping, setCsvMapping] = useState<CsvMapping>({
    date: "",
    description: "",
    amount: "",
    amountMultiplier: 1,
  });
  const fileInputRef = useRef<HTMLInputElement>(null);
  const [manualForm, setManualForm] = useState({
    accountRef: "",
    date: new Date().toISOString().slice(0, 10),
    description: "",
    amount: "",
    category: "",
    note: "",
    attribution: "mine" as "mine" | "yours" | "ours",
    splitMode: "none" as "none" | "equal" | "custom",
    splitMinePercent: "50",
    splitYoursPercent: "50",
  });
  const [editingRef, setEditingRef] = useState<string | null>(null);
  const [commentRef, setCommentRef] = useState<string | null>(null);
  const [comments, setComments] = useState<TransactionComment[]>([]);
  const [commentDraft, setCommentDraft] = useState("");
  const [commentStatus, setCommentStatus] = useState("");
  const [editForm, setEditForm] = useState({
    category: "",
    note: "",
    attribution: "mine" as "mine" | "yours" | "ours",
    splitMode: "none" as "none" | "equal" | "custom",
    splitMinePercent: "50",
    splitYoursPercent: "50",
    hidden: false,
  });
  const [filters, setFilters] = useState({
    startDate: "", endDate: "", minAmount: "", maxAmount: "",
    category: "", source: "", search: "", includeHidden: false,
  });

  const applyPreset = (preset: string) => {
    setDatePreset(preset);
    if (preset === "custom") return;
    const now = new Date();
    const end = new Date(now.getFullYear(), now.getMonth(), now.getDate());
    let start = new Date(end);
    if (preset === "this_month") {
      start = new Date(end.getFullYear(), end.getMonth(), 1);
    } else if (preset === "last_month") {
      start = new Date(end.getFullYear(), end.getMonth() - 1, 1);
      end.setDate(0);
    } else if (preset === "last_6_months") {
      start = new Date(end.getFullYear(), end.getMonth() - 5, 1);
    } else if (preset === "last_year") {
      start = new Date(end.getFullYear() - 1, 0, 1);
      end.setMonth(11, 31);
    }
    const fmt = (d: Date) => d.toISOString().slice(0, 10);
    setFilters((prev) => ({ ...prev, startDate: fmt(start), endDate: fmt(end) }));
  };

  const buildQuery = () => {
    const params = new URLSearchParams();
    if (filters.startDate) params.set("start_date", filters.startDate);
    if (filters.endDate) params.set("end_date", filters.endDate);
    if (filters.minAmount) params.set("min_amount", filters.minAmount);
    if (filters.maxAmount) params.set("max_amount", filters.maxAmount);
    if (filters.category) params.set("category", filters.category);
    if (filters.source) params.set("source", filters.source);
    if (filters.search) params.set("search", filters.search);
    if (filters.includeHidden) params.set("include_hidden", "true");
    return params.toString() ? `?${params.toString()}` : "";
  };

  const load = async () => {
    const query = buildQuery();
    const res = await apiFetch<{ transactions: TransactionRow[]; total: number }>(`/api/transactions${query}`);
    if (res.error) {
      setStatus(res.error.message ?? "Unable to load transactions.");
      return;
    }
    const txs = res.data?.transactions ?? [];
    setRows(txs);
    setStatus(txs.length ? "" : "No transactions yet.");
  };

  const loadAccounts = async () => {
    const res = await apiFetch<{ accounts: Account[]; total: number }>("/api/accounts");
    if (!res.error) setAccounts(res.data?.accounts ?? []);
  };

  const loadSummary = async () => {
    const query = buildQuery();
    const res = await apiFetch<{ total: string; count: number }>(`/api/transactions/summary${query}`);
    if (!res.error) setSummary(res.data);
  };

  const loadInsights = async () => {
    const [cashflowRes, merchantsRes] = await Promise.all([
      apiFetch<CashflowRow[]>("/api/transactions/cashflow?months=6"),
      apiFetch<MerchantInsight[]>("/api/transactions/merchants?limit=6"),
    ]);
    if (!cashflowRes.error) setCashflow(cashflowRes.data ?? []);
    if (!merchantsRes.error) setMerchants(merchantsRes.data ?? []);
  };

  useEffect(() => {
    applyPreset("this_month");
    load();
    loadSummary();
    loadInsights();
    loadAccounts();
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  useEffect(() => {
    if (!autoSync) return;
    const id = setInterval(() => { onSync(); }, 5 * 60 * 1000);
    return () => clearInterval(id);
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [autoSync, filters.startDate, filters.endDate]);

  const onSync = async () => {
    if (isSyncing) return;
    setIsSyncing(true);
    setStatus("Syncing transactions...");
    const res = await apiFetch<unknown>("/api/transactions/sync", {
      method: "POST",
      body: JSON.stringify({ startDate: filters.startDate || undefined, endDate: filters.endDate || undefined }),
    });
    if (res.error) {
      setStatus(res.error.message ?? "Sync failed.");
      setIsSyncing(false);
      return;
    }
    setStatus("Sync complete.");
    await load();
    await loadSummary();
    await loadInsights();
    setIsSyncing(false);
  };

  const onPreviewCsv = async (files: File[]) => {
    const csvFiles = files.filter((file) => file.name.toLowerCase().endsWith(".csv"));
    if (!csvFiles.length) {
      setImportStatus("Select one or more CSV files.");
      return;
    }

    setCsvFiles(csvFiles);
    setCsvPreview(null);
    setImportLoading(true);
    setImportStatus(`Reading ${csvFiles[0].name}...`);

    const formData = new FormData();
    formData.append("file", csvFiles[0]);
    try {
      const payload = await apiFetch<CsvPreview>("/api/transactions/import/preview", {
        method: "POST",
        body: formData,
      });
      if (payload.error) {
        setImportStatus(payload.error?.message ?? "Preview failed.");
        setImportLoading(false);
        return;
      }
      const preview = payload.data;
      setCsvPreview(preview);
      setCsvMapping({
        date: preview.mapping.date ?? "",
        description: preview.mapping.description ?? "",
        amount: preview.mapping.amount ?? "",
        category: preview.mapping.category,
        notes: preview.mapping.notes,
        amountMultiplier: preview.mapping.amountMultiplier === -1 ? -1 : 1,
      });
      setImportStatus(
        `${csvFiles.length} CSV file${csvFiles.length === 1 ? "" : "s"} selected. ${preview.remembered ? "Using remembered mapping." : "Review the column mapping before import."}`
      );
    } catch {
      setImportStatus("Preview failed. Please try again.");
    }
    setImportLoading(false);
  };

  const onImportCsv = async () => {
    const selectedFiles = csvFiles.filter((file) => file.name.toLowerCase().endsWith(".csv"));
    if (!selectedFiles.length) {
      setImportStatus("Select one or more CSV files.");
      return;
    }
    if (!csvMapping.date || !csvMapping.description || !csvMapping.amount) {
      setImportStatus("Map date, description, and amount columns before importing.");
      return;
    }

    setImportLoading(true);
    setImportStatus(`Uploading ${selectedFiles.length} file${selectedFiles.length === 1 ? "" : "s"}...`);
    const formData = new FormData();
    for (const file of selectedFiles) {
      formData.append("files", file);
    }
    formData.append("mapping", JSON.stringify(csvMapping));
    try {
      const payload = await apiFetch<ImportBatchResult>("/api/transactions/import/batch", {
        method: "POST",
        body: formData,
      });
      if (payload.error) {
        setImportStatus(payload.error?.message ?? "Import failed.");
        setImportLoading(false);
        return;
      }
      const r = payload.data;
      const failed = r.failedFiles
        ? ` ${r.failedFiles} file${r.failedFiles === 1 ? "" : "s"} failed.`
        : "";
      const failedNames = r.results
        .filter((result) => result.error)
        .map((result) => `${result.fileName}: ${result.error}`)
        .join(" ");
      setImportStatus(
        `Processed ${r.processedFiles}/${r.totalFiles} file${r.totalFiles === 1 ? "" : "s"}. Imported ${r.imported} transaction${r.imported === 1 ? "" : "s"}, skipped ${r.skipped} duplicate${r.skipped === 1 ? "" : "s"}.${failed}${failedNames ? ` ${failedNames}` : ""}`
      );
      setCsvFiles([]);
      setCsvPreview(null);
      await load();
      await loadSummary();
      await loadInsights();
    } catch {
      setImportStatus("Import failed. Please try again.");
    }
    setImportLoading(false);
  };

  const formatAmount = (value: string) => {
    const numeric = Number.parseFloat(value.replace(/[^0-9.-]/g, ""));
    if (Number.isNaN(numeric)) return { display: value, tone: "text-foreground" };
    return {
      display: numeric < 0 ? `-$${Math.abs(numeric).toFixed(2)}` : `$${numeric.toFixed(2)}`,
      tone: numeric < 0 ? "text-foreground" : "text-primary font-bold",
    };
  };

  const splitPayload = (mode: "none" | "equal" | "custom", mine: string, yours: string) => {
    if (mode === "custom") {
      return {
        splitMode: mode,
        splitMinePercent: Number.parseFloat(mine),
        splitYoursPercent: Number.parseFloat(yours),
      };
    }
    return { splitMode: mode };
  };

  const splitLabel = (row: TransactionRow) => {
    if (!row.split || row.split.mode === "none") return "No split";
    if (row.split.mode === "equal") return "50/50";
    return `${row.split.minePercent}/${row.split.yoursPercent}`;
  };

  const onManualCreate = async (event: React.FormEvent) => {
    event.preventDefault();
    const amount = Number.parseFloat(manualForm.amount);
    if (Number.isNaN(amount)) { setStatus("Invalid amount."); return; }
    setStatus("Saving manual transaction...");
    const res = await apiFetch<unknown>("/api/transactions/manual", {
      method: "POST",
      body: JSON.stringify({
        accountId: manualForm.accountRef || undefined,
        date: manualForm.date,
        description: manualForm.description,
        amount,
        category: manualForm.category || undefined,
        note: manualForm.note || undefined,
        attribution: manualForm.attribution,
        ...splitPayload(manualForm.splitMode, manualForm.splitMinePercent, manualForm.splitYoursPercent),
      }),
    });
    if (res.error) { setStatus(res.error.message ?? "Unable to save transaction."); return; }
    setManualForm((prev) => ({ ...prev, description: "", amount: "", category: "", note: "", attribution: "mine", splitMode: "none", splitMinePercent: "50", splitYoursPercent: "50" }));
    setShowManual(false);
    await load();
    await loadSummary();
    await loadInsights();
    setStatus("Manual transaction saved.");
  };

  const startEdit = (row: TransactionRow) => {
    setEditingRef(row.viewRef);
    setEditForm({
      category: row.category ?? "",
      note: row.note ?? "",
      attribution: row.attribution ?? "mine",
      splitMode: row.split?.mode ?? "none",
      splitMinePercent: String(row.split?.minePercent ?? 50),
      splitYoursPercent: String(row.split?.yoursPercent ?? 50),
      hidden: Boolean(row.hidden),
    });
  };

  const openComments = async (row: TransactionRow) => {
    setCommentRef(row.viewRef);
    setComments([]);
    setCommentDraft("");
    setCommentStatus("Loading comments...");
    const res = await apiFetch<TransactionComment[]>(`/api/transactions/${row.viewRef}/comments`);
    if (res.error) {
      setCommentStatus(res.error.message ?? "Unable to load comments.");
      return;
    }
    setComments(res.data ?? []);
    setCommentStatus("");
  };

  const createComment = async () => {
    if (!commentRef || !commentDraft.trim()) return;
    setCommentStatus("Posting comment...");
    const res = await apiFetch<TransactionComment>(`/api/transactions/${commentRef}/comments`, {
      method: "POST",
      body: JSON.stringify({ body: commentDraft }),
    });
    if (res.error) {
      setCommentStatus(res.error.message ?? "Unable to post comment.");
      return;
    }
    if (res.data) setComments((prev) => [...prev, res.data]);
    setCommentDraft("");
    setCommentStatus("");
    await load();
  };

  const saveEdit = async () => {
    if (!editingRef) return;
    setStatus("Saving edits...");
    const res = await apiFetch<unknown>(`/api/transactions/${editingRef}/derived`, {
      method: "PATCH",
      body: JSON.stringify({
        userCategory: editForm.category || undefined,
        userNotes: editForm.note || undefined,
        attribution: editForm.attribution,
        ...splitPayload(editForm.splitMode, editForm.splitMinePercent, editForm.splitYoursPercent),
        isHidden: editForm.hidden,
      }),
    });
    if (res.error) { setStatus(res.error.message ?? "Unable to save edits."); return; }
    setEditingRef(null);
    await load();
    await loadSummary();
    await loadInsights();
    setStatus("Transaction updated.");
  };

  const inputCls = "mt-2 w-full rounded-md border border-border bg-background/50 px-3 py-2 text-sm text-foreground focus:border-primary focus:ring-primary focus:outline-none";
  const labelCls = "text-xs font-semibold text-muted-foreground uppercase tracking-wider";
  const selectedCommentRow = rows.find((row) => row.viewRef === commentRef) ?? null;

  return (
    <AppShell title="Transactions" subtitle="View, sync, and categorize your transactions.">
      {/* Action bar */}
      <div className="flex flex-wrap items-center gap-2 text-sm mb-6">
        <span className="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-secondary/50 border border-border text-muted-foreground">
          <span className="h-2 w-2 rounded-full bg-primary" />
          {datePreset === "custom" ? "Custom range" : datePreset.replace(/_/g, " ")}
        </span>

        <button onClick={onSync} className="ml-auto px-3 py-2 rounded-lg bg-primary text-primary-foreground text-sm font-bold hover:bg-primary/90 transition-colors">
          {isSyncing ? "Syncing..." : "Sync"}
        </button>
        <button onClick={() => setAutoSync((prev) => !prev)} className={`px-3 py-2 rounded-lg border text-sm font-medium transition-colors ${autoSync ? "bg-primary/10 border-primary/30 text-primary" : "bg-background border-border text-foreground hover:bg-secondary"}`}>
          Auto {autoSync ? "On" : "Off"}
        </button>
        <button onClick={() => setShowManual((prev) => !prev)} className="px-3 py-2 rounded-lg bg-background border border-border text-foreground text-sm font-medium hover:bg-secondary transition-colors">
          {showManual ? "Hide manual" : "Add manual"}
        </button>
        <button onClick={() => { setShowImport((prev) => !prev); setImportStatus(""); }} className="px-3 py-2 rounded-lg bg-background border border-border text-foreground text-sm font-medium hover:bg-secondary transition-colors">
          {showImport ? "Hide import" : "Import CSV"}
        </button>
        <Link href={`/exports${buildQuery()}`} className="px-3 py-2 rounded-lg bg-background border border-border text-foreground text-sm font-medium hover:bg-secondary transition-colors">
          Export
        </Link>
        <button onClick={() => setShowFilters((prev) => !prev)} className="px-3 py-2 rounded-lg bg-background border border-border text-foreground text-sm font-medium hover:bg-secondary transition-colors">
          {showFilters ? "Hide filters" : "Filters"}
        </button>
      </div>

      {/* CSV Import panel */}
      {showImport && (
        <div className="mb-6 glass-panel rounded-xl p-5 shadow-sm">
          <p className="text-sm font-bold text-foreground mb-1">Import CSV</p>
          <p className="text-xs text-muted-foreground mb-4">
            Supports Chase, Bank of America, Wells Fargo, and generic CSV formats. Duplicate transactions are skipped automatically.
          </p>
          <div
            className="border-2 border-dashed border-border rounded-xl p-8 text-center cursor-pointer hover:border-primary/50 transition-colors"
            onClick={() => fileInputRef.current?.click()}
            onDragOver={(e) => e.preventDefault()}
            onDrop={(e) => {
              e.preventDefault();
              onPreviewCsv(Array.from(e.dataTransfer.files));
            }}
          >
            <input
              ref={fileInputRef}
              type="file"
              accept=".csv"
              multiple
              className="hidden"
              onChange={(e) => {
                const files = Array.from(e.target.files ?? []);
                if (files.length) onPreviewCsv(files);
                e.currentTarget.value = "";
              }}
            />
            <svg className="mx-auto h-8 w-8 text-muted-foreground mb-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
              <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" />
            </svg>
            {importLoading ? (
              <p className="text-sm text-muted-foreground">Uploading...</p>
            ) : (
              <p className="text-sm text-muted-foreground">Drop CSV files here or <span className="text-primary font-medium">click to browse</span></p>
            )}
          </div>
          {csvPreview && (
            <div className="mt-4 grid gap-4 lg:grid-cols-[minmax(0,0.9fr)_minmax(0,1.1fr)]">
              <div className="rounded-xl border border-border bg-background/40 p-4">
                <div className="mb-3 flex items-center justify-between gap-3">
                  <p className="text-sm font-semibold text-foreground">Column mapping</p>
                  <span className="rounded-full bg-secondary px-2 py-1 text-[11px] font-medium text-muted-foreground">
                    {csvPreview.remembered ? "Remembered" : "New"}
                  </span>
                </div>
                <div className="grid gap-3 sm:grid-cols-2">
                  {[
                    ["date", "Date"],
                    ["description", "Description"],
                    ["amount", "Amount"],
                    ["category", "Category"],
                    ["notes", "Notes"],
                  ].map(([key, label]) => (
                    <label key={key} className={labelCls}>
                      {label}
                      <select
                        value={(csvMapping[key as keyof CsvMapping] as string | undefined) ?? ""}
                        onChange={(e) => setCsvMapping((prev) => ({ ...prev, [key]: e.target.value || undefined }))}
                        className={inputCls}
                      >
                        <option value="">Not mapped</option>
                        {csvPreview.headers.map((header) => (
                          <option key={header} value={header}>{header}</option>
                        ))}
                      </select>
                    </label>
                  ))}
                  <label className={labelCls}>
                    Amount sign
                    <select
                      value={csvMapping.amountMultiplier ?? 1}
                      onChange={(e) => setCsvMapping((prev) => ({ ...prev, amountMultiplier: e.target.value === "-1" ? -1 : 1 }))}
                      className={inputCls}
                    >
                      <option value={1}>Keep file values</option>
                      <option value={-1}>Flip income/expense signs</option>
                    </select>
                  </label>
                </div>
                <button
                  type="button"
                  onClick={onImportCsv}
                  disabled={importLoading}
                  className="mt-4 w-full rounded-lg bg-primary px-4 py-2 text-sm font-bold text-primary-foreground hover:bg-primary/90 disabled:cursor-not-allowed disabled:opacity-60"
                >
                  {importLoading ? "Importing..." : `Import ${csvFiles.length} file${csvFiles.length === 1 ? "" : "s"}`}
                </button>
              </div>
              <div className="overflow-hidden rounded-xl border border-border bg-background/40">
                <div className="border-b border-border px-4 py-3">
                  <p className="text-sm font-semibold text-foreground">Columns: {csvPreview.fileName}</p>
                  <p className="mt-1 text-xs text-muted-foreground">{csvPreview.rowCount} detected row{csvPreview.rowCount === 1 ? "" : "s"}</p>
                </div>
                <div className="max-h-72 overflow-auto">
                  <table className="w-full text-left text-xs">
                    <thead className="bg-secondary/30 text-muted-foreground">
                      <tr>
                        <th className="whitespace-nowrap px-3 py-2 font-semibold">Column</th>
                        <th className="whitespace-nowrap px-3 py-2 font-semibold">Mapped as</th>
                      </tr>
                    </thead>
                    <tbody>
                      {csvPreview.headers.map((header) => {
                        const mapped = Object.entries(csvMapping).find(([, value]) => value === header)?.[0];
                        return (
                          <tr key={header} className="border-t border-border text-foreground">
                            <td className="max-w-48 truncate px-3 py-2">{header}</td>
                            <td className="px-3 py-2 capitalize text-muted-foreground">{mapped ?? "Not mapped"}</td>
                          </tr>
                        );
                      })}
                    </tbody>
                  </table>
                </div>
              </div>
            </div>
          )}
          {importStatus && (
            <p className="mt-3 text-sm text-muted-foreground">{importStatus}</p>
          )}
        </div>
      )}

      {/* Manual transaction form */}
      {showManual && (
        <div className="mb-6 glass-panel rounded-xl p-5 shadow-sm">
          <p className="text-sm font-bold text-foreground mb-4">Add Manual Transaction</p>
          <form onSubmit={onManualCreate} className="grid gap-3 md:grid-cols-3">
            <div>
              <label className={labelCls}>Date</label>
              <input type="date" value={manualForm.date} onChange={(e) => setManualForm((p) => ({ ...p, date: e.target.value }))} className={inputCls} required />
            </div>
            <div>
              <label className={labelCls}>Description</label>
              <input type="text" value={manualForm.description} onChange={(e) => setManualForm((p) => ({ ...p, description: e.target.value }))} className={inputCls} required />
            </div>
            <div>
              <label className={labelCls}>Amount (negative = expense)</label>
              <input type="number" step="0.01" value={manualForm.amount} onChange={(e) => setManualForm((p) => ({ ...p, amount: e.target.value }))} className={inputCls} required placeholder="-42.50" />
            </div>
            <div>
              <label className={labelCls}>Category</label>
              <input type="text" value={manualForm.category} onChange={(e) => setManualForm((p) => ({ ...p, category: e.target.value }))} className={inputCls} />
            </div>
            <div>
              <label className={labelCls}>Account</label>
              <select value={manualForm.accountRef} onChange={(e) => setManualForm((p) => ({ ...p, accountRef: e.target.value }))} className={inputCls}>
                <option value="">— No account —</option>
                {accounts.map((a) => (
                  <option key={a.viewRef} value={a.viewRef}>{a.institutionName} {a.mask ? `••${a.mask}` : ""}</option>
                ))}
              </select>
            </div>
            <div>
              <label className={labelCls}>Note</label>
              <input type="text" value={manualForm.note} onChange={(e) => setManualForm((p) => ({ ...p, note: e.target.value }))} className={inputCls} />
            </div>
            <div>
              <label className={labelCls}>Attribution</label>
              <select value={manualForm.attribution} onChange={(e) => setManualForm((p) => ({ ...p, attribution: e.target.value as "mine" | "yours" | "ours" }))} className={inputCls}>
                <option value="mine">Mine</option>
                <option value="yours">Yours</option>
                <option value="ours">Ours</option>
              </select>
            </div>
            <div>
              <label className={labelCls}>Split</label>
              <select value={manualForm.splitMode} onChange={(e) => setManualForm((p) => ({ ...p, splitMode: e.target.value as "none" | "equal" | "custom" }))} className={inputCls}>
                <option value="none">No split</option>
                <option value="equal">50/50</option>
                <option value="custom">Custom</option>
              </select>
            </div>
            {manualForm.splitMode === "custom" && (
              <>
                <div>
                  <label className={labelCls}>Mine %</label>
                  <input type="number" min="0" max="100" step="0.01" value={manualForm.splitMinePercent} onChange={(e) => setManualForm((p) => ({ ...p, splitMinePercent: e.target.value }))} className={inputCls} />
                </div>
                <div>
                  <label className={labelCls}>Yours %</label>
                  <input type="number" min="0" max="100" step="0.01" value={manualForm.splitYoursPercent} onChange={(e) => setManualForm((p) => ({ ...p, splitYoursPercent: e.target.value }))} className={inputCls} />
                </div>
              </>
            )}
            <div className="md:col-span-3 flex justify-end gap-2">
              <button type="button" onClick={() => setShowManual(false)} className="px-4 py-2 rounded-lg border border-border text-sm text-foreground hover:bg-secondary">Cancel</button>
              <button type="submit" className="px-4 py-2 rounded-lg bg-primary text-primary-foreground text-sm font-bold hover:bg-primary/90">Save</button>
            </div>
          </form>
        </div>
      )}

      {/* Filters */}
      {showFilters && (
        <div className="mb-6 glass-panel rounded-xl p-5 shadow-sm">
          <div className="grid gap-4 md:grid-cols-3">
            <div>
              <label className={labelCls}>Date range</label>
              <select value={datePreset} onChange={(e) => applyPreset(e.target.value)} className={inputCls}>
                <option value="this_month">This month</option>
                <option value="last_month">Last month</option>
                <option value="last_6_months">Last 6 months</option>
                <option value="last_year">Last year</option>
                <option value="custom">Custom</option>
              </select>
            </div>
            <div>
              <label className={labelCls}>Start date</label>
              <input type="date" value={filters.startDate} onChange={(e) => setFilters((p) => ({ ...p, startDate: e.target.value }))} className={inputCls} disabled={datePreset !== "custom"} />
            </div>
            <div>
              <label className={labelCls}>End date</label>
              <input type="date" value={filters.endDate} onChange={(e) => setFilters((p) => ({ ...p, endDate: e.target.value }))} className={inputCls} disabled={datePreset !== "custom"} />
            </div>
            <div>
              <label className={labelCls}>Search</label>
              <input type="text" value={filters.search} onChange={(e) => setFilters((p) => ({ ...p, search: e.target.value }))} className={inputCls} placeholder="Description..." />
            </div>
            <div>
              <label className={labelCls}>Category</label>
              <input type="text" value={filters.category} onChange={(e) => setFilters((p) => ({ ...p, category: e.target.value }))} className={inputCls} />
            </div>
            <div>
              <label className={labelCls}>Source</label>
              <input type="text" value={filters.source} onChange={(e) => setFilters((p) => ({ ...p, source: e.target.value }))} className={inputCls} placeholder="plaid, manual, csv..." />
            </div>
            <div>
              <label className={labelCls}>Min amount</label>
              <input type="number" step="0.01" value={filters.minAmount} onChange={(e) => setFilters((p) => ({ ...p, minAmount: e.target.value }))} className={inputCls} />
            </div>
            <div>
              <label className={labelCls}>Max amount</label>
              <input type="number" step="0.01" value={filters.maxAmount} onChange={(e) => setFilters((p) => ({ ...p, maxAmount: e.target.value }))} className={inputCls} />
            </div>
            <div className="flex items-end gap-2">
              <label className="flex items-center gap-2 text-sm text-foreground cursor-pointer">
                <input type="checkbox" checked={filters.includeHidden} onChange={(e) => setFilters((p) => ({ ...p, includeHidden: e.target.checked }))} className="rounded border-border text-primary focus:ring-primary" />
                Include hidden
              </label>
            </div>
            <div className="flex items-end">
              <button onClick={() => { load(); loadSummary(); loadInsights(); }} className="w-full px-4 py-2 rounded-lg bg-primary text-primary-foreground text-sm font-bold hover:bg-primary/90">Apply</button>
            </div>
          </div>
        </div>
      )}

      {/* Summary cards */}
      {summary && (
        <div className="mb-6 grid gap-3 md:grid-cols-3">
          {[
            { label: "Total", value: `$${Math.abs(Number.parseFloat(summary.total ?? "0")).toFixed(2)}`, sub: `${summary.count} transactions` },
            { label: "Income", value: `+$${Math.abs(Number.parseFloat(summary.income ?? "0")).toFixed(2)}`, sub: "Credits" },
            { label: "Expenses", value: `-$${Math.abs(Number.parseFloat(summary.expense ?? "0")).toFixed(2)}`, sub: "Debits" },
          ].map((c) => (
            <div key={c.label} className="glass-panel rounded-xl p-4 shadow-sm">
              <p className="text-xs uppercase tracking-[0.2em] text-muted-foreground font-semibold">{c.label}</p>
              <p className="mt-2 text-xl font-bold text-foreground">{c.value}</p>
              <p className="text-xs text-muted-foreground">{c.sub}</p>
            </div>
          ))}
        </div>
      )}

      {(cashflow.length > 0 || merchants.length > 0) && (
        <div className="mb-6 grid gap-4 lg:grid-cols-2">
          {cashflow.length > 0 && (
            <div className="glass-panel rounded-xl p-4 shadow-sm">
              <div className="mb-4 flex items-center justify-between">
                <p className="text-sm font-bold text-foreground">Cashflow</p>
                <span className="text-xs text-muted-foreground">Last 6 months</span>
              </div>
              <div className="space-y-3">
                {cashflow.map((item) => {
                  const income = Number.parseFloat(item.income);
                  const expense = Number.parseFloat(item.expense);
                  const max = Math.max(income, expense, 1);
                  return (
                    <div key={item.month} className="grid grid-cols-[5rem_1fr_4.5rem] items-center gap-3 text-xs">
                      <span className="font-medium text-muted-foreground">{item.month}</span>
                      <div className="space-y-1">
                        <div className="h-2 rounded-full bg-secondary">
                          <div className="h-2 rounded-full bg-primary" style={{ width: `${Math.min((income / max) * 100, 100)}%` }} />
                        </div>
                        <div className="h-2 rounded-full bg-secondary">
                          <div className="h-2 rounded-full bg-foreground/70" style={{ width: `${Math.min((expense / max) * 100, 100)}%` }} />
                        </div>
                      </div>
                      <span className={Number.parseFloat(item.net) >= 0 ? "text-primary font-semibold" : "text-foreground font-semibold"}>
                        ${Number.parseFloat(item.net).toFixed(0)}
                      </span>
                    </div>
                  );
                })}
              </div>
            </div>
          )}

          {merchants.length > 0 && (
            <div className="glass-panel rounded-xl p-4 shadow-sm">
              <div className="mb-4 flex items-center justify-between">
                <p className="text-sm font-bold text-foreground">Top merchants</p>
                <span className="text-xs text-muted-foreground">By spend</span>
              </div>
              <div className="space-y-3">
                {merchants.map((merchant) => (
                  <div key={merchant.merchant} className="flex items-center justify-between gap-3 rounded-lg border border-border bg-background/40 px-3 py-2">
                    <div className="min-w-0">
                      <p className="truncate text-sm font-semibold text-foreground">{merchant.merchant}</p>
                      <p className="text-xs text-muted-foreground">{merchant.count} transactions</p>
                    </div>
                    <p className="text-sm font-bold text-foreground">${Number.parseFloat(merchant.total).toFixed(2)}</p>
                  </div>
                ))}
              </div>
            </div>
          )}
        </div>
      )}

      {/* Transaction table */}
      <div className="glass-panel rounded-2xl shadow-sm overflow-hidden">
        {status && (
          <div className="px-6 py-3 bg-secondary/30 border-b border-border text-sm text-muted-foreground">{status}</div>
        )}
        {selectedCommentRow && (
          <div className="border-b border-border bg-secondary/10 p-5">
            <div className="flex flex-col gap-3 md:flex-row md:items-start md:justify-between">
              <div>
                <p className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">Transaction chat</p>
                <h2 className="mt-1 text-base font-bold text-foreground">{selectedCommentRow.description ?? selectedCommentRow.name ?? "Transaction"}</h2>
                <p className="mt-1 text-xs text-muted-foreground">
                  {new Date(selectedCommentRow.date).toLocaleDateString()} · {formatAmount(selectedCommentRow.amount).display}
                </p>
              </div>
              <button
                type="button"
                onClick={() => {
                  setCommentRef(null);
                  setComments([]);
                  setCommentDraft("");
                  setCommentStatus("");
                }}
                className="rounded border border-border px-3 py-1 text-xs font-semibold text-foreground hover:bg-secondary"
              >
                Close
              </button>
            </div>
            <div className="mt-4 grid gap-4 lg:grid-cols-[1fr_320px]">
              <div className="max-h-64 space-y-3 overflow-y-auto rounded-lg border border-border bg-background/40 p-3">
                {comments.map((comment) => (
                  <div key={comment.id} className="rounded-lg border border-border bg-background p-3">
                    <div className="flex flex-wrap items-center justify-between gap-2">
                      <p className="text-sm font-semibold text-foreground">{comment.author.displayName}</p>
                      <p className="text-xs text-muted-foreground">{new Date(comment.createdAt).toLocaleString()}</p>
                    </div>
                    <p className="mt-2 whitespace-pre-wrap text-sm leading-6 text-muted-foreground">{comment.body}</p>
                  </div>
                ))}
                {!comments.length && !commentStatus && (
                  <p className="text-sm text-muted-foreground">No comments yet.</p>
                )}
                {commentStatus && <p className="text-sm text-muted-foreground">{commentStatus}</p>}
              </div>
              <div>
                <textarea
                  value={commentDraft}
                  onChange={(event) => setCommentDraft(event.target.value)}
                  maxLength={1000}
                  placeholder="Add a note for this transaction"
                  className="h-32 w-full rounded-lg border border-border bg-background px-3 py-2 text-sm text-foreground focus:border-primary focus:outline-none"
                />
                <div className="mt-2 flex items-center justify-between gap-3">
                  <span className="text-xs text-muted-foreground">{commentDraft.length}/1000</span>
                  <button
                    type="button"
                    onClick={createComment}
                    disabled={!commentDraft.trim()}
                    className="rounded bg-primary px-4 py-2 text-xs font-bold text-primary-foreground hover:bg-primary/90 disabled:opacity-50"
                  >
                    Post comment
                  </button>
                </div>
              </div>
            </div>
          </div>
        )}
        <div className="overflow-x-auto">
          <table className="w-full text-left text-xs text-muted-foreground">
            <thead className="text-[0.65rem] uppercase tracking-[0.18em] font-semibold bg-secondary/20">
              <tr>
                <th className="px-4 py-3">Date</th>
                <th className="px-4 py-3">Description</th>
                <th className="px-4 py-3">Category</th>
                <th className="px-4 py-3">Attribution</th>
                <th className="px-4 py-3">Split</th>
                <th className="px-4 py-3 text-right">Amount</th>
                <th className="px-4 py-3 text-right">Actions</th>
              </tr>
            </thead>
            <tbody>
              {rows.map((row) =>
                editingRef === row.viewRef ? (
                  <tr key={row.viewRef} className="border-b border-border bg-secondary/10">
                    <td className="px-4 py-3" colSpan={4}>
                      <div className="flex flex-wrap gap-2">
                        <input
                          type="text"
                          value={editForm.category}
                          onChange={(e) => setEditForm((p) => ({ ...p, category: e.target.value }))}
                          placeholder="Category"
                          className="w-28 rounded border border-border bg-background px-2 py-1 text-xs text-foreground focus:border-primary focus:outline-none"
                        />
                        <input
                          type="text"
                          value={editForm.note}
                          onChange={(e) => setEditForm((p) => ({ ...p, note: e.target.value }))}
                          placeholder="Note"
                          className="flex-1 rounded border border-border bg-background px-2 py-1 text-xs text-foreground focus:border-primary focus:outline-none"
                        />
                        <select
                          value={editForm.attribution}
                          onChange={(e) => setEditForm((p) => ({ ...p, attribution: e.target.value as "mine" | "yours" | "ours" }))}
                          className="rounded border border-border bg-background px-2 py-1 text-xs text-foreground focus:border-primary focus:outline-none"
                        >
                          <option value="mine">Mine</option>
                          <option value="yours">Yours</option>
                          <option value="ours">Ours</option>
                        </select>
                        <select
                          value={editForm.splitMode}
                          onChange={(e) => setEditForm((p) => ({ ...p, splitMode: e.target.value as "none" | "equal" | "custom" }))}
                          className="rounded border border-border bg-background px-2 py-1 text-xs text-foreground focus:border-primary focus:outline-none"
                        >
                          <option value="none">No split</option>
                          <option value="equal">50/50</option>
                          <option value="custom">Custom</option>
                        </select>
                        {editForm.splitMode === "custom" && (
                          <>
                            <input
                              type="number"
                              min="0"
                              max="100"
                              step="0.01"
                              value={editForm.splitMinePercent}
                              onChange={(e) => setEditForm((p) => ({ ...p, splitMinePercent: e.target.value }))}
                              placeholder="Mine %"
                              className="w-20 rounded border border-border bg-background px-2 py-1 text-xs text-foreground focus:border-primary focus:outline-none"
                            />
                            <input
                              type="number"
                              min="0"
                              max="100"
                              step="0.01"
                              value={editForm.splitYoursPercent}
                              onChange={(e) => setEditForm((p) => ({ ...p, splitYoursPercent: e.target.value }))}
                              placeholder="Yours %"
                              className="w-20 rounded border border-border bg-background px-2 py-1 text-xs text-foreground focus:border-primary focus:outline-none"
                            />
                          </>
                        )}
                        <label className="flex items-center gap-1 text-xs text-foreground">
                          <input type="checkbox" checked={editForm.hidden} onChange={(e) => setEditForm((p) => ({ ...p, hidden: e.target.checked }))} />
                          Hide
                        </label>
                      </div>
                    </td>
                    <td className="px-4 py-3" colSpan={3}>
                      <div className="flex gap-2 justify-end">
                        <button onClick={saveEdit} className="rounded bg-primary px-3 py-1 text-[11px] font-bold text-primary-foreground hover:bg-primary/90">Save</button>
                        <button onClick={() => setEditingRef(null)} className="rounded border border-border px-3 py-1 text-[11px] text-foreground hover:bg-secondary">Cancel</button>
                      </div>
                    </td>
                  </tr>
                ) : (
                  <tr key={row.viewRef} className={`border-b border-border hover:bg-secondary/20 transition-colors ${row.hidden ? "opacity-50" : ""}`}>
                    <td className="px-4 py-3 font-medium whitespace-nowrap">
                      {new Date(row.date).toLocaleDateString(undefined, { month: "short", day: "numeric", year: "numeric" })}
                    </td>
                    <td className="px-4 py-3 text-foreground font-medium max-w-[200px] truncate">
                      {row.description ?? row.name ?? "—"}
                    </td>
                    <td className="px-4 py-3">
                      {row.category ? (
                        <span className="inline-flex rounded-full bg-secondary px-2 py-0.5 text-xs font-medium text-foreground">{row.category}</span>
                      ) : (
                        <span className="text-muted-foreground">—</span>
                      )}
                    </td>
                    <td className="px-4 py-3">
                      <span className="inline-flex rounded-full border border-border bg-background px-2 py-0.5 text-xs font-medium capitalize text-foreground">
                        {row.attribution ?? "mine"}
                      </span>
                    </td>
                    <td className="px-4 py-3">
                      <span className="inline-flex rounded-full border border-border bg-background px-2 py-0.5 text-xs font-medium text-foreground">
                        {splitLabel(row)}
                      </span>
                    </td>
                    <td className={`px-4 py-3 text-right font-bold ${formatAmount(row.amount).tone}`}>
                      {formatAmount(row.amount).display}
                    </td>
                    <td className="px-4 py-3 text-right">
                      <div className="flex justify-end gap-3">
                        <button onClick={() => openComments(row)} className="text-xs text-primary hover:underline">
                          Comments{row.commentCount ? ` (${row.commentCount})` : ""}
                        </button>
                        <button onClick={() => startEdit(row)} className="text-xs text-primary hover:underline">Edit</button>
                      </div>
                    </td>
                  </tr>
                )
              )}
              {!rows.length && !status && (
                <tr>
                  <td colSpan={7} className="px-4 py-12 text-center text-sm text-muted-foreground">
                    No transactions found. Try adjusting your filters or sync your accounts.
                  </td>
                </tr>
              )}
            </tbody>
          </table>
        </div>
      </div>
    </AppShell>
  );
}
