"use client";

import { useMemo, useState } from "react";
import { Card, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { useLocalStorage } from "@/lib/hooks/use-local-storage";
import { formatCurrency } from "@/lib/utils";
import type { Bill, BillPayment } from "@/lib/finance/bill-types";

interface PlaidAccountLocal {
  accountId: string;
  itemId: string;
  accessToken?: string;
}

interface MatchCandidate {
  transactionId: string;
  accountId: string;
  accountLabel: string;
  date: string;
  amount: number;
  merchantName: string;
  category: string;
  score: number;
  reasons: string[];
}

interface BillMatches {
  billId: string;
  candidates: MatchCandidate[];
}

interface BillReconcileProps {
  /** Outstanding bills (status != Paid && != Cancelled). */
  outstanding: Bill[];
  /** Called when Glenn approves a match — flips bill to Paid with the BillPayment. */
  onMatch: (billId: string, payment: BillPayment) => void;
}

export function BillReconcile({ outstanding, onMatch }: BillReconcileProps) {
  const [accounts] = useLocalStorage<PlaidAccountLocal[]>("sag.plaid.accounts", []);
  const [accessTokens] = useLocalStorage<Record<string, string>>("sag.plaid.accessTokens", {});
  const [matches, setMatches] = useState<BillMatches[] | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string>("");
  const [dismissed, setDismissed] = useState<Set<string>>(new Set()); // bill ids
  const [summary, setSummary] = useState<{
    billsScanned: number;
    transactionsScanned: number;
    billsWithMatches: number;
  } | null>(null);
  const [open, setOpen] = useState(false);

  const tokens = useMemo(() => {
    return Array.from(
      new Set(
        accounts
          .map((a) => accessTokens[a.itemId] ?? a.accessToken)
          .filter((t): t is string => !!t)
      )
    );
  }, [accounts, accessTokens]);

  async function reconcile() {
    setError("");
    setLoading(true);
    setMatches(null);
    setSummary(null);
    try {
      const resp = await fetch("/api/bills/reconcile-plaid", {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({
          accessTokens: tokens,
          bills: outstanding.map((b) => ({
            id: b.id,
            vendor: b.vendor,
            amount: b.amount,
            dueDate: b.dueDate,
          })),
        }),
      });
      const data = await resp.json();
      if (!resp.ok) throw new Error(data.error || "Reconcile failed");
      setMatches(data.matches ?? []);
      setSummary(data.summary ?? null);
    } catch (e) {
      setError(e instanceof Error ? e.message : "Failed");
    } finally {
      setLoading(false);
    }
  }

  function approveMatch(bill: Bill, candidate: MatchCandidate) {
    const payment: BillPayment = {
      method: "Plaid ACH",
      paidDate: candidate.date,
      amount: candidate.amount,
      confirmationNumber: candidate.transactionId,
      fromAccount: candidate.accountLabel,
      notes: `Auto-matched via Plaid reconciliation (score ${candidate.score}/100). Merchant: ${candidate.merchantName}.`,
    };
    onMatch(bill.id, payment);
    setDismissed((prev) => {
      const next = new Set(prev);
      next.add(bill.id);
      return next;
    });
  }

  function dismiss(billId: string) {
    setDismissed((prev) => {
      const next = new Set(prev);
      next.add(billId);
      return next;
    });
  }

  const visible = useMemo(() => {
    if (!matches) return [];
    return matches.filter((m) => m.candidates.length > 0 && !dismissed.has(m.billId));
  }, [matches, dismissed]);

  const noTokens = tokens.length === 0;
  const noOutstanding = outstanding.length === 0;

  return (
    <Card>
      <CardContent className="p-4 flex items-center flex-wrap gap-2">
        <span className="text-sm font-medium">Reconcile with bank:</span>
        <Button
          variant={open ? "brand" : "outline"}
          size="sm"
          onClick={() => setOpen((v) => !v)}
        >
          🔍 Match Plaid debits to bills
        </Button>
        <span className="text-[11px] text-muted-foreground ml-auto">
          Auto-finds the Plaid debit that paid each outstanding bill — you approve each match.
        </span>
      </CardContent>
      {open && (
        <div className="p-4 border-t space-y-3">
          <div className="flex items-center justify-between flex-wrap gap-2">
            <div>
              <h3 className="text-sm font-semibold">Bill ↔ Plaid debit reconciliation</h3>
              <p className="text-[11px] text-muted-foreground">
                Scores each open bill against your recent Plaid debits on amount + vendor + date.
                Surfaces top 3 candidates per bill. One-click approve flips status to Paid with
                method &ldquo;Plaid ACH&rdquo;, payment date from the debit, confirmation # set
                to the Plaid transaction id.
              </p>
            </div>
            <Button
              variant="brand"
              size="sm"
              disabled={loading || noTokens || noOutstanding}
              onClick={reconcile}
            >
              {loading
                ? "Scanning…"
                : matches
                  ? "Re-scan"
                  : "Run reconciliation"}
            </Button>
          </div>

          {noTokens && (
            <Card className="border-dashed">
              <CardContent className="p-3 text-xs text-muted-foreground">
                No Plaid accounts linked. Visit <code>/app/finance</code> → Connect bank to link
                Truist (or any Plaid-supported institution) first.
              </CardContent>
            </Card>
          )}

          {noOutstanding && !noTokens && (
            <Card className="border-dashed">
              <CardContent className="p-3 text-xs text-muted-foreground">
                No outstanding bills to reconcile. Add bills first, or load sample data on the
                empty-state.
              </CardContent>
            </Card>
          )}

          {error && <p className="text-xs text-destructive">{error}</p>}

          {summary && (
            <p className="text-[11px] text-muted-foreground">
              Scanned {summary.billsScanned} outstanding bill
              {summary.billsScanned === 1 ? "" : "s"} against{" "}
              {summary.transactionsScanned} Plaid debits (last 90 days). Found candidates for{" "}
              <strong>{summary.billsWithMatches}</strong> bill
              {summary.billsWithMatches === 1 ? "" : "s"}.
            </p>
          )}

          {visible.length === 0 && matches && summary && summary.billsWithMatches === 0 && (
            <Card className="border-dashed">
              <CardContent className="p-3 text-xs text-muted-foreground">
                No matches found. Sandbox transactions are synthetic and rarely match SAG-flavored
                sample bills by vendor name. With real Plaid + real bills, expect 60–80% of
                routine bills (utilities, SaaS, rent) to auto-match.
              </CardContent>
            </Card>
          )}

          {visible.length > 0 && (
            <div className="space-y-3">
              {visible.map((match) => {
                const bill = outstanding.find((b) => b.id === match.billId);
                if (!bill) return null;
                return (
                  <Card key={match.billId} className="bg-muted/20">
                    <CardContent className="p-4">
                      <div className="flex items-start justify-between gap-3 flex-wrap mb-3">
                        <div>
                          <div className="text-sm font-semibold">{bill.vendor}</div>
                          <div className="text-[11px] text-muted-foreground">
                            Due {bill.dueDate} · {formatCurrency(bill.amount)}
                          </div>
                        </div>
                        <Button
                          variant="ghost"
                          size="sm"
                          onClick={() => dismiss(match.billId)}
                        >
                          Skip
                        </Button>
                      </div>
                      <div className="space-y-2">
                        {match.candidates.map((c) => (
                          <div
                            key={c.transactionId}
                            className="flex items-start justify-between gap-3 flex-wrap rounded-md border bg-background p-3"
                          >
                            <div className="min-w-0 flex-1">
                              <div className="flex items-center gap-2 flex-wrap mb-1">
                                <span className="text-sm font-medium">{c.merchantName}</span>
                                <Badge
                                  variant={
                                    c.score >= 80
                                      ? "success"
                                      : c.score >= 60
                                        ? "info"
                                        : "warning"
                                  }
                                  className="text-[10px]"
                                >
                                  Score {c.score}/100
                                </Badge>
                              </div>
                              <div className="text-[11px] text-muted-foreground">
                                {c.date} · {formatCurrency(c.amount)} · {c.accountLabel}
                                {c.category ? ` · ${c.category}` : ""}
                              </div>
                              {c.reasons.length > 0 && (
                                <ul className="mt-1 text-[10px] text-muted-foreground space-y-0.5">
                                  {c.reasons.map((r, i) => (
                                    <li key={i}>· {r}</li>
                                  ))}
                                </ul>
                              )}
                            </div>
                            <Button
                              variant="brand"
                              size="sm"
                              onClick={() => approveMatch(bill, c)}
                            >
                              ✓ Mark paid
                            </Button>
                          </div>
                        ))}
                      </div>
                    </CardContent>
                  </Card>
                );
              })}
            </div>
          )}
        </div>
      )}
    </Card>
  );
}
