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 } from "lucide-react";
import { EmptyState } from "./components/EmptyState";
import { ensureUndepositedFunds } from "./lib/undeposited";

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 [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 ?? [],
  });

  // 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 selectedTotal = useMemo(
    () => pending.filter((r) => selected.has(r.key)).reduce((s, r) => s + r.amount, 0),
    [pending, selected]
  );

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

  const submitDeposit = async () => {
    if (!bankAccountId) return toast.error("Choose a bank account");
    if (selected.size === 0) return toast.error("Select at least one payment");
    setSaving(true);
    try {
      const bank = (bankAccounts as any[]).find((a) => a.id === bankAccountId);
      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);
      const txTotal = chosen.filter((r) => r.kind === "tx").reduce((s, r) => s + r.amount, 0);
      const count = chosen.length;

      // 1) Create deposit record
      const { data: dep, error: depErr } = await accounting
        .from("deposits")
        .insert({ company_id: cid, bank_account_id: bankAccountId, date: depositDate, amount: selectedTotal, memo: memo || null })
        .select()
        .single();
      if (depErr || !dep) throw new Error(depErr?.message ?? "Failed to create deposit");
      const ref = `DEP-${dep.id.slice(0, 8).toUpperCase()}`;

      // 2) Single debit on bank account for the full deposit
      await accounting.from("transactions").insert({
        company_id: cid,
        account_id: bankAccountId,
        date: depositDate,
        type: "debit",
        amount: selectedTotal,
        description: `Deposit · ${count} payment${count > 1 ? "s" : ""}${memo ? " · " + memo : ""}`,
        category: "Deposit",
        reference: ref,
        deposit_id: dep.id,
      });

      // 3) Offsetting credit on Undeposited Funds — only for the portion actually
      //    held there as transactions (payments_received aren't posted to it).
      if (txTotal > 0) {
        await accounting.from("transactions").insert({
          company_id: cid,
          account_id: undepositedId,
          date: depositDate,
          type: "credit",
          amount: txTotal,
          description: `Deposit cleared · ${txIds.length} payment${txIds.length > 1 ? "s" : ""}`,
          category: "Deposit",
          reference: ref,
          deposit_id: dep.id,
        });
        await accounting.from("transactions").update({ deposit_id: dep.id }).in("id", txIds);
        const { data: und } = await accounting.from("accounts").select("balance").eq("id", undepositedId).single();
        if (und) {
          await accounting.from("accounts").update({ balance: Number(und.balance) - txTotal }).eq("id", undepositedId);
        }
      }

      // 4) Mark selected payments_received as deposited so they leave the queue
      if (pmtIds.length) {
        await accounting.from("payments_received")
          .update({ deposited: true, deposit_id: dep.id, bank_account_id: bankAccountId })
          .in("id", pmtIds);
      }

      // 5) Bank balance reflects the full deposit
      if (bank) {
        await accounting.from("accounts").update({ balance: Number(bank.balance) + selectedTotal }).eq("id", bank.id);
      }

      toast.success(`Deposit of ${money(selectedTotal, cur)} recorded`);
      setSelected(new Set());
      setMemo("");
      qc.invalidateQueries({ queryKey: ["undeposited-tx", cid] });
      qc.invalidateQueries({ queryKey: ["undeposited-pmt", cid] });
      qc.invalidateQueries({ queryKey: ["bank-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">
          Select customer payments held in Undeposited Funds and deposit them as a single bank transaction.
        </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(selectedTotal, 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>

      <div className="flex justify-end">
        <Button onClick={submitDeposit} disabled={saving || selected.size === 0 || !bankAccountId}>
          {saving ? "Recording…" : `Deposit ${money(selectedTotal, cur)}`}
        </Button>
      </div>
    </div>
  );
}
