import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useMemo, useState } from "react";
import { accounting } from "@/lib/accountingClient";
import { useCompanyId } from "./lib/useCompanyId";
import { fetchAllJournalEntries } from "./lib/fetchJournalEntries";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Card, CardContent } from "@/components/ui/card";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Table, TableHeader, TableRow, TableHead, TableBody, TableCell } from "@/components/ui/table";
import { Loader2, RefreshCw } from "lucide-react";
import { money, fmtDate, startOfYearET, todayET } from "./lib/format";

// Account type → display label, ordering, and normal balance side.
const TYPE_META: Record<string, { label: string; order: number; side: "debit" | "credit" }> = {
  asset:     { label: "Assets",      order: 1, side: "debit"  },
  liability: { label: "Liabilities", order: 2, side: "credit" },
  equity:    { label: "Equity",      order: 3, side: "credit" },
  income:    { label: "Income",      order: 4, side: "credit" },
  expense:   { label: "Expenses",    order: 5, side: "debit"  },
};

type FlatLine = {
  account_id: string;
  code: string | null;
  name: string;
  type: string;
  date: string;
  description: string;
  reference: string | null;
  debit: number;
  credit: number;
};

export default function AccountingGeneralLedgerPage() {
  const { companyId, loading: companyLoading, error: companyError, associationId } = useCompanyId();
  const cid = companyId ?? "";
  const cur = "USD";
  const qc = useQueryClient();

  const [from, setFrom] = useState(startOfYearET());
  const [to, setTo] = useState(todayET());
  const [accountFilter, setAccountFilter] = useState<string>("all");
  const [activityOnly, setActivityOnly] = useState(true);
  const [refreshing, setRefreshing] = useState(false);

  const { data: entries = [], isFetching, error: entriesError } = useQuery({
    queryKey: ["gl-journal-entries", cid],
    enabled: !!cid,
    queryFn: () =>
      // Running balances below assume date-ascending order.
      fetchAllJournalEntries(cid, "id, date, description, reference, journal_entry_lines(debit, credit, account_id, accounts(code, name, type))", { ascending: true }),
  });

  const { data: accounts = [] } = useQuery({
    queryKey: ["accounts", cid, "gl"],
    enabled: !!cid,
    queryFn: async () =>
      (await accounting.from("accounts").select("id, code, name, type").eq("company_id", cid).order("code")).data ?? [],
  });

  // Flatten every JE line into a single list, carrying its entry's date/desc/ref.
  const lines = useMemo<FlatLine[]>(() => {
    const out: FlatLine[] = [];
    for (const e of entries as any[]) {
      for (const l of e.journal_entry_lines ?? []) {
        if (!l.account_id) continue;
        out.push({
          account_id: l.account_id,
          code: l.accounts?.code ?? null,
          name: l.accounts?.name ?? "(unknown account)",
          type: l.accounts?.type ?? "asset",
          date: e.date,
          description: e.description ?? "",
          reference: e.reference ?? null,
          debit: Number(l.debit) || 0,
          credit: Number(l.credit) || 0,
        });
      }
    }
    return out;
  }, [entries]);

  // Build per-account ledgers: beginning balance (everything before `from`),
  // the in-range rows with a running balance, and the ending balance.
  const ledger = useMemo(() => {
    const byAccount = new Map<string, FlatLine[]>();
    for (const l of lines) {
      if (accountFilter !== "all" && l.account_id !== accountFilter) continue;
      (byAccount.get(l.account_id) ?? byAccount.set(l.account_id, []).get(l.account_id)!).push(l);
    }

    const sideOf = (type: string) => TYPE_META[type]?.side ?? "debit";
    // Render running balance on the account's natural side (credit accounts
    // show a credit balance as positive, mirroring Buildium).
    const natural = (signed: number, type: string) => (sideOf(type) === "debit" ? signed : -signed);

    const accountsOut = Array.from(byAccount.entries()).map(([accId, rows]) => {
      const meta = rows[0];
      let beginSigned = 0; // debit − credit
      const inRange: (FlatLine & { running: number })[] = [];
      let runSigned = 0;
      let periodDebit = 0;
      let periodCredit = 0;

      // rows already arrive date-ascending from the query order
      for (const r of rows) {
        const signed = r.debit - r.credit;
        if (r.date < from) {
          beginSigned += signed;
        } else if (r.date <= to) {
          runSigned += signed;
          periodDebit += r.debit;
          periodCredit += r.credit;
          inRange.push({ ...r, running: natural(beginSigned + runSigned, meta.type) });
        }
      }
      const endSigned = beginSigned + runSigned;
      return {
        account_id: accId,
        code: meta.code,
        name: meta.name,
        type: meta.type,
        beginning: natural(beginSigned, meta.type),
        ending: natural(endSigned, meta.type),
        periodDebit,
        periodCredit,
        rows: inRange,
      };
    });

    // Drop untouched accounts when "activity only" is on (no in-range rows AND no opening balance).
    const filtered = activityOnly
      ? accountsOut.filter((a) => a.rows.length > 0 || Math.abs(a.beginning) > 0.005)
      : accountsOut;

    filtered.sort((a, b) => {
      const oa = TYPE_META[a.type]?.order ?? 99;
      const ob = TYPE_META[b.type]?.order ?? 99;
      if (oa !== ob) return oa - ob;
      return (a.code ?? "").localeCompare(b.code ?? "") || a.name.localeCompare(b.name);
    });

    // Group by type for section headers
    const groups: { type: string; label: string; accounts: typeof filtered }[] = [];
    for (const a of filtered) {
      const label = TYPE_META[a.type]?.label ?? a.type;
      let g = groups.find((x) => x.type === a.type);
      if (!g) { g = { type: a.type, label, accounts: [] }; groups.push(g); }
      g.accounts.push(a);
    }
    const totalDebit = filtered.reduce((s, a) => s + a.periodDebit, 0);
    const totalCredit = filtered.reduce((s, a) => s + a.periodCredit, 0);
    return { groups, totalDebit, totalCredit, accountCount: filtered.length };
  }, [lines, accountFilter, activityOnly, from, to]);

  const refresh = async () => {
    setRefreshing(true);
    await qc.invalidateQueries({ queryKey: ["gl-journal-entries", cid] });
    setTimeout(() => setRefreshing(false), 500);
  };

  if (!associationId) return <p className="text-sm text-muted-foreground">Select an association.</p>;
  if (companyLoading) return <div className="flex justify-center py-12"><Loader2 className="h-6 w-6 animate-spin text-muted-foreground" /></div>;
  if (companyError || !companyId) return <p className="text-sm text-muted-foreground text-center py-12">{companyError || "Accounting setup is not ready."}</p>;
  if (entriesError) {
    return (
      <p className="text-sm text-destructive text-center py-12">
        Failed to load the general ledger: {(entriesError as any)?.message || String(entriesError)}
      </p>
    );
  }

  return (
    <div className="space-y-6">
      <div className="flex items-center justify-between">
        <div>
          <h1 className="text-2xl font-semibold">General Ledger</h1>
          <p className="text-sm text-muted-foreground mt-0.5">Every account's activity with running balances</p>
        </div>
        <Button variant="outline" size="sm" onClick={refresh} disabled={refreshing || isFetching}>
          <RefreshCw className={`h-4 w-4 mr-1 ${refreshing || isFetching ? "animate-spin" : ""}`} /> Refresh
        </Button>
      </div>

      {/* Filters */}
      <Card>
        <CardContent className="flex flex-wrap items-end gap-4 py-4">
          <div>
            <Label className="text-xs">From</Label>
            <Input type="date" value={from} onChange={(e) => setFrom(e.target.value)} className="h-9 w-[150px]" />
          </div>
          <div>
            <Label className="text-xs">To</Label>
            <Input type="date" value={to} onChange={(e) => setTo(e.target.value)} className="h-9 w-[150px]" />
          </div>
          <div>
            <Label className="text-xs">Account</Label>
            <Select value={accountFilter} onValueChange={setAccountFilter}>
              <SelectTrigger className="h-9 w-[260px]"><SelectValue /></SelectTrigger>
              <SelectContent>
                <SelectItem value="all">All accounts</SelectItem>
                {(accounts as any[]).map((a) => (
                  <SelectItem key={a.id} value={a.id}>{a.code ? `${a.code} · ` : ""}{a.name}</SelectItem>
                ))}
              </SelectContent>
            </Select>
          </div>
          <label className="flex items-center gap-2 text-sm text-muted-foreground pb-1.5 cursor-pointer select-none">
            <input type="checkbox" checked={activityOnly} onChange={(e) => setActivityOnly(e.target.checked)} className="h-4 w-4" />
            Accounts with activity only
          </label>
        </CardContent>
      </Card>

      {ledger.groups.length === 0 ? (
        <Card><CardContent className="py-16 text-center text-muted-foreground text-sm">
          No general ledger activity for this period.
        </CardContent></Card>
      ) : (
        <div className="space-y-8">
          {ledger.groups.map((g) => (
            <div key={g.type} className="space-y-4">
              <h2 className="text-sm font-semibold uppercase tracking-wide text-muted-foreground">{g.label}</h2>
              {g.accounts.map((a) => (
                <Card key={a.account_id}>
                  <CardContent className="p-0">
                    <div className="flex items-center justify-between px-4 py-2.5 border-b bg-muted/40">
                      <div className="font-medium text-sm">{a.code ? `${a.code} · ` : ""}{a.name}</div>
                      <div className="text-xs text-muted-foreground">
                        Beginning balance <span className="font-medium text-foreground tabular-nums ml-1">{money(a.beginning, cur)}</span>
                      </div>
                    </div>
                    <Table>
                      <TableHeader>
                        <TableRow>
                          <TableHead className="w-[110px]">Date</TableHead>
                          <TableHead>Description</TableHead>
                          <TableHead className="w-[110px]">Ref #</TableHead>
                          <TableHead className="text-right w-[120px]">Debit</TableHead>
                          <TableHead className="text-right w-[120px]">Credit</TableHead>
                          <TableHead className="text-right w-[130px]">Balance</TableHead>
                        </TableRow>
                      </TableHeader>
                      <TableBody>
                        {a.rows.length === 0 ? (
                          <TableRow>
                            <TableCell colSpan={6} className="text-center text-muted-foreground text-sm py-4">
                              No activity in this period.
                            </TableCell>
                          </TableRow>
                        ) : (
                          a.rows.map((r, i) => (
                            <TableRow key={i}>
                              <TableCell className="text-sm">{fmtDate(r.date)}</TableCell>
                              <TableCell className="text-sm">{r.description || "—"}</TableCell>
                              <TableCell className="text-sm text-muted-foreground">{r.reference ?? "—"}</TableCell>
                              <TableCell className="text-right text-sm tabular-nums">{r.debit > 0 ? money(r.debit, cur) : ""}</TableCell>
                              <TableCell className="text-right text-sm tabular-nums">{r.credit > 0 ? money(r.credit, cur) : ""}</TableCell>
                              <TableCell className="text-right text-sm tabular-nums">{money(r.running, cur)}</TableCell>
                            </TableRow>
                          ))
                        )}
                        <TableRow className="bg-muted/60 font-semibold">
                          <TableCell colSpan={3} className="text-sm">Ending balance</TableCell>
                          <TableCell className="text-right text-sm tabular-nums">{money(a.periodDebit, cur)}</TableCell>
                          <TableCell className="text-right text-sm tabular-nums">{money(a.periodCredit, cur)}</TableCell>
                          <TableCell className="text-right text-sm tabular-nums">{money(a.ending, cur)}</TableCell>
                        </TableRow>
                      </TableBody>
                    </Table>
                  </CardContent>
                </Card>
              ))}
            </div>
          ))}

          {/* Grand totals */}
          <Card>
            <CardContent className="flex items-center justify-between py-3 text-sm">
              <span className="font-semibold">Total — {ledger.accountCount} account{ledger.accountCount === 1 ? "" : "s"}</span>
              <span className="text-muted-foreground">
                Debits <span className="font-semibold text-foreground tabular-nums ml-1">{money(ledger.totalDebit, cur)}</span>
                <span className="mx-3">·</span>
                Credits <span className="font-semibold text-foreground tabular-nums ml-1">{money(ledger.totalCredit, cur)}</span>
              </span>
            </CardContent>
          </Card>
        </div>
      )}
    </div>
  );
}
