import { useQuery, useQueryClient } from "@tanstack/react-query";
import { Fragment, useEffect, useMemo, useRef, useState } from "react";
import { format } from "date-fns";
import { CalendarIcon, Lock, Pencil, RotateCcw, Save, Info, Loader2, Upload, FileDown } from "lucide-react";
import { cn } from "@/lib/utils";
import { accounting } from "@/lib/accountingClient";
import { useCompanyId } from "./lib/useCompanyId";
import { parseCsv, pick } from "./lib/csv";
import { money } from "./lib/format";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Card, CardContent } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Calendar } from "@/components/ui/calendar";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Table, TableHeader, TableRow, TableHead, TableBody, TableCell } from "@/components/ui/table";
import {
  AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent,
  AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { toast } from "sonner";

const TYPES = [
  { value: "asset", label: "Assets", side: "debit" as const },
  { value: "expense", label: "Expenses", side: "debit" as const },
  { value: "liability", label: "Liabilities", side: "credit" as const },
  { value: "equity", label: "Equity", side: "credit" as const },
  { value: "income", label: "Income", side: "credit" as const },
];

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

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

  const { data: setup } = useQuery({
    queryKey: ["ob-setup", cid],
    enabled: !!cid,
    queryFn: async () => (await accounting.from("opening_balances_setup").select("*").eq("company_id", cid).maybeSingle()).data,
  });

  const { data: balances = [] } = useQuery({
    queryKey: ["ob", cid],
    enabled: !!cid,
    queryFn: async () => (await accounting.from("opening_balances").select("*").eq("company_id", cid)).data ?? [],
  });

  const [rows, setRows] = useState<Record<string, { debit: string; credit: string }>>({});
  const [asOfDate, setAsOfDate] = useState<Date>(new Date());
  const [dateLocked, setDateLocked] = useState(false);

  useEffect(() => {
    if (setup) {
      setAsOfDate(new Date(setup.as_of_date));
      setDateLocked(setup.confirmed);
    }
  }, [setup]);

  useEffect(() => {
    const map: Record<string, { debit: string; credit: string }> = {};
    for (const b of balances as any[]) {
      map[b.account_id] = {
        debit: Number(b.debit) ? String(b.debit) : "",
        credit: Number(b.credit) ? String(b.credit) : "",
      };
    }
    setRows(map);
  }, [balances]);

  const update = (id: string, side: "debit" | "credit", v: string) => {
    setRows((r) => ({ ...r, [id]: { debit: side === "debit" ? v : "", credit: side === "credit" ? v : "" } }));
  };

  const grouped = useMemo(() => {
    const out: Record<string, any[]> = {};
    for (const t of TYPES) out[t.value] = [];
    for (const a of accounts as any[]) (out[a.type] ??= []).push(a);
    return out;
  }, [accounts]);

  const totalDebit = useMemo(() => Object.values(rows).reduce((s, r) => s + (parseFloat(r.debit) || 0), 0), [rows]);
  const totalCredit = useMemo(() => Object.values(rows).reduce((s, r) => s + (parseFloat(r.credit) || 0), 0), [rows]);
  const diff = totalDebit - totalCredit;
  const balanced = Math.abs(diff) < 0.005;

  const save = async () => {
    if (!balanced) return toast.error("Debits and Credits must match");
    const setupPayload = {
      company_id: cid,
      as_of_date: format(asOfDate, "yyyy-MM-dd"),
      confirmed: true,
    };
    const { error: e1 } = await accounting.from("opening_balances_setup").upsert(setupPayload, { onConflict: "company_id" });
    if (e1) return toast.error(e1.message);

    const rowsPayload = (accounts as any[])
      .map((a: any) => {
        const r = rows[a.id];
        const d = parseFloat(r?.debit || "0") || 0;
        const c = parseFloat(r?.credit || "0") || 0;
        return { company_id: cid, account_id: a.id, debit: d, credit: c };
      })
      .filter((r) => r.debit !== 0 || r.credit !== 0);

    await accounting.from("opening_balances").delete().eq("company_id", cid);
    if (rowsPayload.length) {
      const { error: e2 } = await accounting.from("opening_balances").insert(rowsPayload);
      if (e2) return toast.error(e2.message);
    }
    toast.success("Opening balances saved");
    setDateLocked(true);
    qc.invalidateQueries({ queryKey: ["ob-setup", cid] });
    qc.invalidateQueries({ queryKey: ["ob", cid] });
  };

  const resetAll = () => {
    setRows({});
    toast.info("All balances cleared. Click Save to persist.");
  };

  // ── CSV import: a trial balance per account (Account Code/Name, Debit, Credit) ──
  const fileRef = useRef<HTMLInputElement>(null);

  const importCsv = async (file: File) => {
    let parsed: Record<string, string>[];
    try {
      parsed = parseCsv(await file.text());
    } catch {
      toast.error("Could not read the CSV file.");
      return;
    }
    if (!parsed.length) { toast.error("No rows found in the file."); return; }

    const byCode = new Map<string, any>();
    const byName = new Map<string, any>();
    for (const a of accounts as any[]) {
      if (a.code) byCode.set(String(a.code).trim().toLowerCase(), a);
      if (a.name) byName.set(String(a.name).trim().toLowerCase(), a);
    }
    // Signed parse: handles "$", commas, and parentheses/leading-minus as negative,
    // so a negative balance carries through to reporting (e.g. reserves).
    const numFrom = (s: string) => {
      const t = (s || "").trim();
      const mag = Math.abs(parseFloat(t.replace(/[$,()]/g, "")) || 0);
      return /^\s*[-(]/.test(t) ? -mag : mag;
    };

    const next: Record<string, { debit: string; credit: string }> = {};
    let matched = 0;
    let unmatched = 0;
    for (const r of parsed) {
      const code = pick(r["account code"], r["code"], r["account number"], r["account #"], r["acct"], r["number"]);
      const name = pick(r["account name"], r["account"], r["name"]);
      const acct =
        (code && byCode.get(code.trim().toLowerCase())) ||
        (name && byName.get(name.trim().toLowerCase())) || null;
      if (!acct) { if (code || name) unmatched++; continue; }

      let debit = numFrom(pick(r["debit"], r["debits"]));
      let credit = numFrom(pick(r["credit"], r["credits"]));
      if (!debit && !credit) {
        // Single balance column → place on the account's natural side (sign preserved).
        const signed = numFrom(pick(r["balance"], r["amount"]));
        const debitNatural = acct.type === "asset" || acct.type === "expense";
        if (debitNatural) debit = signed; else credit = signed;
      }
      if (debit || credit) {
        next[acct.id] = { debit: debit ? String(debit) : "", credit: credit ? String(credit) : "" };
        matched++;
      }
    }

    if (!matched) {
      toast.error("No rows matched an account by code or name. Check your CSV headers (Account Code / Account Name, Debit, Credit).");
      return;
    }
    setRows(next);
    toast.success(
      `Imported ${matched} account balance${matched === 1 ? "" : "s"}.${unmatched ? ` ${unmatched} row(s) didn't match an account.` : ""} Review and click Save.`,
    );
  };

  const downloadTemplate = () => {
    const header = "Account Code,Account Name,Debit,Credit";
    const sample = (accounts as any[]).slice(0, 5).map((a) => `${a.code ?? ""},"${(a.name ?? "").replace(/"/g, '""')}",,`);
    const blob = new Blob([[header, ...sample].join("\n")], { type: "text/csv" });
    const url = URL.createObjectURL(blob);
    const link = document.createElement("a");
    link.href = url; link.download = "trial-balance-template.csv"; link.click();
    URL.revokeObjectURL(url);
  };

  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>;

  return (
    <div className="space-y-6">
      <div className="flex flex-wrap items-end justify-between gap-3">
        <div>
          <h1 className="text-2xl font-semibold">Opening Balances</h1>
          <p className="text-sm text-muted-foreground">Set starting balances for each account from your previous accounting system.</p>
        </div>
        <div className="flex items-end gap-2">
          <input
            ref={fileRef}
            type="file"
            accept=".csv,text/csv"
            className="hidden"
            onChange={(e) => { const f = e.target.files?.[0]; if (f) importCsv(f); e.target.value = ""; }}
          />
          <Button variant="outline" onClick={downloadTemplate} title="Download a CSV template">
            <FileDown className="mr-1 h-4 w-4" /> Template
          </Button>
          <Button variant="outline" onClick={() => fileRef.current?.click()} title="Import a trial balance CSV">
            <Upload className="mr-1 h-4 w-4" /> Import CSV
          </Button>
          <div className="flex flex-col gap-1">
            <span className="text-xs text-muted-foreground">As of date</span>
            <div className="flex items-center gap-1">
              <Popover>
                <PopoverTrigger asChild>
                  <Button variant="outline" disabled={dateLocked} className={cn("w-[200px] justify-start text-left font-normal", dateLocked && "opacity-100")}>
                    <CalendarIcon className="mr-2 h-4 w-4" />
                    {format(asOfDate, "PPP")}
                    {dateLocked && <Lock className="ml-auto h-3.5 w-3.5 text-muted-foreground" />}
                  </Button>
                </PopoverTrigger>
                <PopoverContent className="w-auto p-0" align="end">
                  <Calendar mode="single" selected={asOfDate} onSelect={(d) => d && setAsOfDate(d)} initialFocus className={cn("p-3 pointer-events-auto")} />
                </PopoverContent>
              </Popover>
              {dateLocked && (
                <Button variant="ghost" size="icon" onClick={() => setDateLocked(false)} title="Edit date">
                  <Pencil className="h-4 w-4" />
                </Button>
              )}
            </div>
          </div>
        </div>
      </div>

      <div className="flex items-start gap-3 rounded-md border border-primary/20 bg-primary/5 px-4 py-3 text-sm">
        <Info className="h-4 w-4 mt-0.5 text-primary shrink-0" />
        <p>Enter the balances from your previous accounting system. <strong>Debits must equal Credits to save.</strong> Accounts with zero balances can remain blank.</p>
      </div>

      <Card>
        <CardContent className="p-0">
          <Table>
            <TableHeader>
              <TableRow>
                <TableHead className="w-28">Code</TableHead>
                <TableHead>Account name</TableHead>
                <TableHead className="w-32">Type</TableHead>
                <TableHead className="text-right w-44">Debit</TableHead>
                <TableHead className="text-right w-44">Credit</TableHead>
              </TableRow>
            </TableHeader>
            <TableBody>
              {TYPES.map((t) => {
                const rs = grouped[t.value] ?? [];
                if (!rs.length) return null;
                return (
                  <Fragment key={t.value}>
                    <TableRow key={`g-${t.value}`} className="bg-muted/40 hover:bg-muted/40">
                      <TableCell colSpan={5} className="font-semibold text-xs uppercase tracking-wide text-muted-foreground py-2">
                        {t.label}
                      </TableCell>
                    </TableRow>
                    {rs.map((a: any) => {
                      const r = rows[a.id] ?? { debit: "", credit: "" };
                      const isDebit = t.side === "debit";
                      return (
                        <TableRow key={a.id}>
                          <TableCell className="font-mono text-xs text-muted-foreground">{a.code ?? "—"}</TableCell>
                          <TableCell className="font-medium">{a.name}</TableCell>
                          <TableCell className="capitalize text-sm text-muted-foreground">{a.type}</TableCell>
                          <TableCell className="text-right">
                            {isDebit ? (
                              <Input type="number" step="0.01" inputMode="decimal" value={r.debit}
                                onChange={(e) => update(a.id, "debit", e.target.value)}
                                placeholder="0.00" className="text-right ml-auto max-w-[160px]" />
                            ) : <span className="text-muted-foreground">—</span>}
                          </TableCell>
                          <TableCell className="text-right">
                            {!isDebit ? (
                              <Input type="number" step="0.01" inputMode="decimal" value={r.credit}
                                onChange={(e) => update(a.id, "credit", e.target.value)}
                                placeholder="0.00" className="text-right ml-auto max-w-[160px]" />
                            ) : <span className="text-muted-foreground">—</span>}
                          </TableCell>
                        </TableRow>
                      );
                    })}
                  </Fragment>
                );
              })}
              {(accounts as any[]).length === 0 && (
                <TableRow><TableCell colSpan={5} className="text-center text-sm text-muted-foreground py-8">No accounts yet. Create some in Chart of Accounts first.</TableCell></TableRow>
              )}
            </TableBody>
          </Table>
          <div className="border-t bg-muted/30 px-4 py-3">
            <div className="grid grid-cols-[1fr_auto_auto_auto] items-center gap-6 text-sm">
              <div className="font-semibold">Totals</div>
              <div className="text-right"><span className="text-muted-foreground mr-2">Debits</span><span className="font-semibold tabular-nums">{money(totalDebit, cur)}</span></div>
              <div className="text-right"><span className="text-muted-foreground mr-2">Credits</span><span className="font-semibold tabular-nums">{money(totalCredit, cur)}</span></div>
              <div className="text-right">
                <span className="text-muted-foreground mr-2">Difference</span>
                <span className={cn("font-semibold tabular-nums", balanced ? "text-emerald-600" : "text-red-600")}>
                  {money(diff, cur)}
                </span>
              </div>
            </div>
          </div>
        </CardContent>
      </Card>

      <div className="flex items-center justify-between gap-3">
        <div className="flex items-center gap-2">
          {balanced ? (
            <Badge className="bg-emerald-100 text-emerald-700 border-0">Balanced</Badge>
          ) : (
            <Badge className="bg-red-100 text-red-700 border-0">Out of balance by {money(Math.abs(diff), cur)}</Badge>
          )}
          {setup?.confirmed && <Badge variant="outline">Last saved {format(new Date(setup.updated_at), "PP")}</Badge>}
        </div>
        <div className="flex items-center gap-2">
          <AlertDialog>
            <AlertDialogTrigger asChild>
              <Button variant="outline"><RotateCcw className="mr-2 h-4 w-4" /> Reset All</Button>
            </AlertDialogTrigger>
            <AlertDialogContent>
              <AlertDialogHeader>
                <AlertDialogTitle>Reset all opening balances?</AlertDialogTitle>
                <AlertDialogDescription>This clears every entered debit and credit on this page. Saved balances remain until you click Save.</AlertDialogDescription>
              </AlertDialogHeader>
              <AlertDialogFooter>
                <AlertDialogCancel>Cancel</AlertDialogCancel>
                <AlertDialogAction onClick={resetAll}>Reset</AlertDialogAction>
              </AlertDialogFooter>
            </AlertDialogContent>
          </AlertDialog>
          <Button onClick={save} disabled={!balanced || (accounts as any[]).length === 0}>
            <Save className="mr-2 h-4 w-4" /> Save Opening Balances
          </Button>
        </div>
      </div>
    </div>
  );
}
