import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useEffect, useMemo, useState } from "react";
import { accounting } from "@/lib/accountingClient";
import { useCompanyId } from "./lib/useCompanyId";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { Textarea } from "@/components/ui/textarea";
import { toast } from "sonner";
import { money, fmtDate } from "./lib/format";
import { Landmark, Loader2, Plus, Trash2 } from "lucide-react";
import { EmptyState } from "./components/EmptyState";
import { ensureUndepositedFunds } from "./lib/undeposited";

type ManualLine = { account_id: string; amount: string; memo: string };
const EMPTY_LINE: ManualLine = { account_id: "", amount: "", memo: "" };

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

  const [undepositedId, setUndepositedId] = useState<string>("");
  const [bankAccountId, setBankAccountId] = useState("");
  const [depositDate, setDepositDate] = useState(new Date().toLocaleDateString("en-CA", { timeZone: "America/New_York" }));
  const [memo, setMemo] = useState("");
  const [selected, setSelected] = useState<Set<string>>(new Set());
  const [lines, setLines] = useState<ManualLine[]>([]);
  const [saving, setSaving] = useState(false);

  useEffect(() => {
    if (!cid) return;
    ensureUndepositedFunds(cid).then((id) => setUndepositedId(id)).catch(() => {});
  }, [cid]);

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

  // All accounts — for the source-account picker on deposit lines (income, A/R,
  // reserve, clearing, etc.), so a deposit isn't forced through Undeposited Funds.
  const { data: allAccounts = [] } = useQuery({
    queryKey: ["all-accounts", cid],
    enabled: !!cid,
    queryFn: async () =>
      (await accounting.from("accounts").select("id,name,code,type,balance").eq("company_id", cid).order("type").order("code")).data ?? [],
  });

  // Two sources of "awaiting deposit": transactions parked on the Undeposited
  // Funds account (banking flow) and payments_received not yet deposited (incl.
  // payments synced from the main app's owner ledger). Both are unified below.
  const { data: pendingTx = [] } = useQuery({
    queryKey: ["undeposited-tx", cid, undepositedId],
    enabled: !!cid && !!undepositedId,
    queryFn: async () => {
      const { data } = await accounting
        .from("transactions")
        .select("*")
        .eq("company_id", cid)
        .eq("account_id", undepositedId)
        .eq("type", "debit")
        .is("deposit_id", null)
        .order("date", { ascending: false });
      return data ?? [];
    },
  });

  const { data: pendingPmt = [] } = useQuery({
    queryKey: ["undeposited-pmt", cid],
    enabled: !!cid,
    queryFn: async () => {
      const { data } = await accounting
        .from("payments_received")
        .select("id,payment_date,amount,method,reference,memo,customer_id")
        .eq("company_id", cid)
        .eq("deposited", false)
        .order("payment_date", { ascending: false });
      return data ?? [];
    },
  });

  type PendingRow = { key: string; kind: "tx" | "pmt"; id: string; date: string; description: string; reference: string | null; amount: number };

  const pending = useMemo<PendingRow[]>(() => {
    const rows: PendingRow[] = [
      ...(pendingTx as any[]).map((t) => ({
        key: `tx:${t.id}`, kind: "tx" as const, id: t.id, date: t.date,
        description: t.description, reference: t.reference ?? null, amount: Number(t.amount),
      })),
      ...(pendingPmt as any[]).map((p) => ({
        key: `pmt:${p.id}`, kind: "pmt" as const, id: p.id, date: p.payment_date,
        description: [p.method, p.memo].filter(Boolean).join(" · ") || "Customer payment",
        reference: p.reference ?? null, amount: Number(p.amount),
      })),
    ];
    return rows.sort((a, b) => b.date.localeCompare(a.date));
  }, [pendingTx, pendingPmt]);

  const undepositedTotal = useMemo(
    () => pending.filter((r) => selected.has(r.key)).reduce((s, r) => s + r.amount, 0),
    [pending, selected]
  );
  const manualTotal = useMemo(
    () => lines.reduce((s, l) => s + (Number(l.amount) || 0), 0),
    [lines]
  );
  const grandTotal = undepositedTotal + manualTotal;

  const toggleAll = () => {
    if (selected.size === pending.length) setSelected(new Set());
    else setSelected(new Set(pending.map((r) => r.key)));
  };

  const addLine = () => setLines((ls) => [...ls, { ...EMPTY_LINE }]);
  const updateLine = (i: number, patch: Partial<ManualLine>) =>
    setLines((ls) => ls.map((l, idx) => (idx === i ? { ...l, ...patch } : l)));
  const removeLine = (i: number) => setLines((ls) => ls.filter((_, idx) => idx !== i));

  const submitDeposit = async () => {
    if (!bankAccountId) return toast.error("Choose a bank account");
    if (grandTotal <= 0) return toast.error("Add at least one payment or deposit line");

    // Validate manual lines: each must have an account and a positive amount.
    const cleanLines = lines.filter((l) => l.account_id || Number(l.amount));
    for (const l of cleanLines) {
      if (!l.account_id) return toast.error("Every deposit line needs a source account");
      if (!(Number(l.amount) > 0)) return toast.error("Every deposit line needs a positive amount");
    }

    // Optional guard: warn (non-blocking) if crediting Undeposited Funds would exceed
    // what is currently held there — i.e. depositing more than is sitting in it.
    const manualToUndeposited = cleanLines
      .filter((l) => l.account_id === undepositedId)
      .reduce((s, l) => s + Number(l.amount), 0);
    const toUndeposited = undepositedTotal + manualToUndeposited;
    if (toUndeposited > 0 && undepositedId) {
      const held = Number((allAccounts as any[]).find((a) => a.id === undepositedId)?.balance ?? 0);
      if (toUndeposited > held + 0.005) {
        toast.warning(`Crediting ${money(toUndeposited, cur)} to Undeposited Funds, which holds ${money(held, cur)}.`);
      }
    }

    setSaving(true);
    try {
      const chosen = pending.filter((r) => selected.has(r.key));
      const txIds = chosen.filter((r) => r.kind === "tx").map((r) => r.id);
      const pmtIds = chosen.filter((r) => r.kind === "pmt").map((r) => r.id);

      // 1) Deposit header — amount is the sum of all credit lines.
      const { data: dep, error: depErr } = await accounting
        .from("deposits")
        .insert({ company_id: cid, bank_account_id: bankAccountId, date: depositDate, amount: grandTotal, memo: memo || null })
        .select()
        .single();
      if (depErr || !dep) throw new Error(depErr?.message ?? "Failed to create deposit");

      // 2) Deposit lines (credit side). The selected payments collapse into one
      //    Undeposited Funds line; manual lines book to their chosen accounts.
      //    accounting.post_deposit_gl posts Dr Bank (total) / Cr each line.
      const lineRows: any[] = [];
      if (undepositedTotal > 0 && undepositedId) {
        lineRows.push({
          deposit_id: dep.id, company_id: cid, account_id: undepositedId,
          amount: undepositedTotal, memo: `Cleared ${chosen.length} payment${chosen.length !== 1 ? "s" : ""}`,
        });
      }
      for (const l of cleanLines) {
        lineRows.push({ deposit_id: dep.id, company_id: cid, account_id: l.account_id, amount: Number(l.amount), memo: l.memo || null });
      }
      if (lineRows.length) {
        const { error: lineErr } = await accounting.from("deposit_lines").insert(lineRows);
        if (lineErr) throw new Error(lineErr.message);
      }

      // 3) Clear the deposited items from the awaiting-deposit queue.
      if (txIds.length) {
        await accounting.from("transactions").update({ deposit_id: dep.id }).in("id", txIds);
      }
      if (pmtIds.length) {
        await accounting.from("payments_received")
          .update({ deposited: true, deposit_id: dep.id, bank_account_id: bankAccountId })
          .in("id", pmtIds);
      }

      toast.success(`Deposit of ${money(grandTotal, cur)} recorded`);
      setSelected(new Set());
      setLines([]);
      setMemo("");
      qc.invalidateQueries({ queryKey: ["undeposited-tx", cid] });
      qc.invalidateQueries({ queryKey: ["undeposited-pmt", cid] });
      qc.invalidateQueries({ queryKey: ["bank-accounts", cid] });
      qc.invalidateQueries({ queryKey: ["all-accounts", cid] });
      qc.invalidateQueries({ queryKey: ["accounts", cid] });
      qc.invalidateQueries({ queryKey: ["transactions", cid] });
    } catch (e: any) {
      toast.error(e?.message ?? "Failed");
    } finally {
      setSaving(false);
    }
  };

  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>
        <h1 className="text-2xl font-semibold">Make Deposit</h1>
        <p className="text-sm text-muted-foreground">
          Deposit customer payments held in Undeposited Funds, or record a deposit straight to an
          income, A/R, or other account by adding deposit lines.
        </p>
      </div>

      <Card>
        <CardHeader><CardTitle className="text-base">Deposit details</CardTitle></CardHeader>
        <CardContent>
          <div className="grid gap-3 md:grid-cols-4">
            <div>
              <Label>Deposit to</Label>
              <Select value={bankAccountId} onValueChange={setBankAccountId}>
                <SelectTrigger><SelectValue placeholder="Bank account" /></SelectTrigger>
                <SelectContent>
                  {(bankAccounts as any[]).map((a) => (
                    <SelectItem key={a.id} value={a.id}>{a.code ? `${a.code} · ` : ""}{a.name}</SelectItem>
                  ))}
                </SelectContent>
              </Select>
            </div>
            <div>
              <Label>Date</Label>
              <Input type="date" value={depositDate} onChange={(e) => setDepositDate(e.target.value)} />
            </div>
            <div className="md:col-span-2">
              <Label>Memo</Label>
              <Textarea rows={1} value={memo} onChange={(e) => setMemo(e.target.value)} maxLength={200} />
            </div>
          </div>
        </CardContent>
      </Card>

      <Card>
        <CardHeader className="flex flex-row items-center justify-between">
          <CardTitle className="text-base">Payments awaiting deposit</CardTitle>
          <div className="text-sm">
            Selected: <b>{money(undepositedTotal, cur)}</b> ({selected.size} of {pending.length})
          </div>
        </CardHeader>
        <CardContent>
          <Table>
            <TableHeader>
              <TableRow>
                <TableHead className="w-10">
                  <input
                    type="checkbox"
                    checked={selected.size > 0 && selected.size === pending.length}
                    onChange={toggleAll}
                  />
                </TableHead>
                <TableHead>Date</TableHead>
                <TableHead>Description</TableHead>
                <TableHead>Reference</TableHead>
                <TableHead className="text-right">Amount</TableHead>
              </TableRow>
            </TableHeader>
            <TableBody>
              {pending.map((r) => (
                <TableRow key={r.key} className="cursor-pointer" onClick={() => {
                  const s = new Set(selected);
                  s.has(r.key) ? s.delete(r.key) : s.add(r.key);
                  setSelected(s);
                }}>
                  <TableCell><input type="checkbox" checked={selected.has(r.key)} readOnly /></TableCell>
                  <TableCell>{fmtDate(r.date)}</TableCell>
                  <TableCell>{r.description}</TableCell>
                  <TableCell className="text-muted-foreground">{r.reference ?? "—"}</TableCell>
                  <TableCell className="text-right tabular-nums">{money(r.amount, cur)}</TableCell>
                </TableRow>
              ))}
              {pending.length === 0 && (
                <TableRow className="hover:bg-transparent">
                  <TableCell colSpan={5} className="p-0">
                    <EmptyState
                      icon={Landmark}
                      title="No payments awaiting deposit"
                      description="Customer payments received to Undeposited Funds will appear here."
                    />
                  </TableCell>
                </TableRow>
              )}
            </TableBody>
          </Table>
        </CardContent>
      </Card>

      <Card>
        <CardHeader className="flex flex-row items-center justify-between">
          <div>
            <CardTitle className="text-base">Other deposit lines</CardTitle>
            <p className="text-xs text-muted-foreground mt-1">
              Book a deposit directly to an account — interest income, a refund, a reimbursement, etc.
            </p>
          </div>
          <Button size="sm" variant="outline" onClick={addLine}><Plus className="h-4 w-4 mr-1" />Add line</Button>
        </CardHeader>
        <CardContent>
          {lines.length === 0 ? (
            <p className="text-sm text-muted-foreground">No additional lines. Add one to deposit to a specific account.</p>
          ) : (
            <div className="space-y-2">
              {lines.map((l, i) => (
                <div key={i} className="grid gap-2 md:grid-cols-12 items-center">
                  <div className="md:col-span-5">
                    <Select value={l.account_id} onValueChange={(v) => updateLine(i, { account_id: v })}>
                      <SelectTrigger><SelectValue placeholder="Source account" /></SelectTrigger>
                      <SelectContent>
                        {(allAccounts as any[]).map((a) => (
                          <SelectItem key={a.id} value={a.id}>
                            {a.code ? `${a.code} · ` : ""}{a.name}<span className="text-muted-foreground"> · {a.type}</span>
                          </SelectItem>
                        ))}
                      </SelectContent>
                    </Select>
                  </div>
                  <div className="md:col-span-4">
                    <Input value={l.memo} placeholder="Memo (optional)" onChange={(e) => updateLine(i, { memo: e.target.value })} />
                  </div>
                  <div className="md:col-span-2">
                    <Input
                      type="number" inputMode="decimal" step="0.01" placeholder="0.00"
                      className="text-right tabular-nums"
                      value={l.amount} onChange={(e) => updateLine(i, { amount: e.target.value })}
                    />
                  </div>
                  <div className="md:col-span-1 flex justify-end">
                    <Button size="icon" variant="ghost" onClick={() => removeLine(i)} aria-label="Remove line">
                      <Trash2 className="h-4 w-4" />
                    </Button>
                  </div>
                </div>
              ))}
            </div>
          )}
        </CardContent>
      </Card>

      <div className="flex items-center justify-end gap-4">
        <div className="text-sm text-muted-foreground">
          {money(undepositedTotal, cur)} from Undeposited + {money(manualTotal, cur)} direct
        </div>
        <Button onClick={submitDeposit} disabled={saving || grandTotal <= 0 || !bankAccountId}>
          {saving ? "Recording…" : `Deposit ${money(grandTotal, cur)}`}
        </Button>
      </div>
    </div>
  );
}
