import { useCallback, useEffect, useMemo, useState } from "react";
import { createFileRoute, Link, useNavigate } from "@tanstack/react-router";
import {
  CloudDownload,
  EyeOff,
  Landmark,
  RefreshCw,
  Search,
  X,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import { EmptyState, KpiCard, PageHeader, SectionCard } from "@/components/ledger/Primitives";
import { Pill } from "@/components/ledger/StatusBadge";
import { ConfidenceIndicator } from "@/components/ledger/ConfidenceIndicator";
import { useAuthSession } from "@/hooks/useAuthSession";
import {
  fetchBankMatchesApi,
  fetchBankTransactionsApi,
  searchBankMatchesApi,
  type FirmActor,
} from "@/lib/api";
import { eur, useLedger } from "@/lib/ledger-store";
import type { BankMatchCandidate, BankStats, BankStatus, BankTx } from "@/lib/ledger-types";

export const Route = createFileRoute("/bank")({
  validateSearch: (search: Record<string, unknown>) => ({
    client: typeof search.client === "string" ? search.client : undefined,
  }),
  head: () => ({
    meta: [{ title: "Bank matching — Amanah" }],
  }),
  component: BankPage,
});

const STATUS_FILTERS: { value: string; label: string }[] = [
  { value: "all", label: "All" },
  { value: "Unmatched", label: "Unmatched" },
  { value: "Suggested", label: "Suggested" },
  { value: "Needs review", label: "Needs review" },
  { value: "Matched", label: "Matched" },
  { value: "Partial", label: "Partial" },
  { value: "Ignored", label: "Ignored" },
];

const TONE: Record<BankStatus, "neutral" | "green" | "amber" | "danger"> = {
  Unmatched: "neutral",
  Suggested: "amber",
  "Needs review": "amber",
  Matched: "green",
  Partial: "amber",
  Ignored: "neutral",
};

function confidenceLabel(score?: number | null): string {
  if (!score || score <= 0) return "—";
  if (score >= 85) return "High";
  if (score >= 60) return "Medium";
  return "Low";
}

function BankPage() {
  const session = useAuthSession("firm");
  const navigate = useNavigate();
  const { client: clientParam } = Route.useSearch();
  const { clients, clientName, getInvoice, approveBankMatch, rejectBankMatch, ignoreBankTransaction, syncBankTransactions, refreshState } =
    useLedger();

  const actor: FirmActor = useMemo(
    () => ({ platform: "firm", email: session?.email ?? "", actor: session?.name ?? "" }),
    [session?.email, session?.name],
  );

  const [rows, setRows] = useState<BankTx[]>([]);
  const [stats, setStats] = useState<BankStats>({
    unmatched: 0,
    suggested: 0,
    needsReview: 0,
    matched: 0,
    ignored: 0,
    partial: 0,
    total: 0,
  });
  const [snelstartConnected, setSnelstartConnected] = useState(false);
  const [snelstartIntegrationEnabled, setSnelstartIntegrationEnabled] = useState(false);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  const [status, setStatus] = useState("all");
  const [client, setClient] = useState(clientParam ?? "all");
  const [dateFrom, setDateFrom] = useState("");
  const [dateTo, setDateTo] = useState("");
  const [search, setSearch] = useState("");
  const [hideIgnored, setHideIgnored] = useState(false);

  const [reviewTx, setReviewTx] = useState<BankTx | null>(null);
  const [matches, setMatches] = useState<BankMatchCandidate[]>([]);
  const [manualQuery, setManualQuery] = useState("");
  const [manualResults, setManualResults] = useState<BankMatchCandidate[]>([]);
  const [ignoreOpen, setIgnoreOpen] = useState(false);
  const [ignoreReason, setIgnoreReason] = useState("");

  useEffect(() => {
    setClient(clientParam ?? "all");
  }, [clientParam]);

  const load = useCallback(async () => {
    if (!session?.email) return;
    setLoading(true);
    setError(null);
    try {
      const res = await fetchBankTransactionsApi(actor, {
        clientId: client === "all" ? undefined : client,
        status: status === "all" ? undefined : status,
        dateFrom: dateFrom || undefined,
        dateTo: dateTo || undefined,
        q: search || undefined,
      });
      setRows(res.items);
      setStats(res.stats);
      setSnelstartConnected(res.snelstart.anyConnected);
      setSnelstartIntegrationEnabled(res.snelstart.integrationEnabled);
    } catch (err) {
      setError(err instanceof Error ? err.message : "Could not load bank transactions.");
    } finally {
      setLoading(false);
    }
  }, [actor, client, dateFrom, dateTo, search, session?.email, status]);

  useEffect(() => {
    void load();
  }, [load]);

  const confidenceStats = useMemo(() => {
    let high = 0;
    let medium = 0;
    let low = 0;
    for (const tx of rows) {
      if (tx.status === "Ignored" || tx.status === "Matched") continue;
      const score = tx.confidence ?? 0;
      if (score >= 85) high += 1;
      else if (score >= 60) medium += 1;
      else if (score > 0) low += 1;
    }
    return { high, medium, low };
  }, [rows]);

  const visibleRows = useMemo(
    () => (hideIgnored ? rows.filter((r) => r.status !== "Ignored") : rows),
    [hideIgnored, rows],
  );

  const openReview = async (tx: BankTx) => {
    setReviewTx(tx);
    setManualQuery("");
    setManualResults([]);
    if (!session?.email) return;
    const res = await fetchBankMatchesApi(tx.id, actor);
    setMatches(res.matches);
  };

  const runManualSearch = async () => {
    if (!reviewTx || !manualQuery.trim() || !session?.email) return;
    const res = await searchBankMatchesApi(reviewTx.id, manualQuery.trim(), actor);
    setManualResults(res.items);
  };

  const closeReview = () => {
    setReviewTx(null);
    setMatches([]);
    setManualResults([]);
    setManualQuery("");
  };

  const afterAction = async () => {
    closeReview();
    await refreshState();
    await load();
  };

  const emptyAll = !loading && stats.total === 0;
  const emptyFiltered = !loading && stats.total > 0 && visibleRows.length === 0;
  const caughtUp =
    !loading &&
    stats.total > 0 &&
    stats.unmatched === 0 &&
    stats.suggested === 0 &&
    stats.needsReview === 0 &&
    status === "all" &&
    !search &&
    client === "all";

  return (
    <div className="space-y-6">
      <PageHeader
        eyebrow="Workspace"
        title="Bank matching"
        subtitle="Match bank transactions with invoices and bookkeeping records."
        actions={
          <>
            <Button variant="outline" onClick={() => void syncBankTransactions().then(load)}>
              <CloudDownload size={16} /> Sync from SnelStart
            </Button>
            <Button variant="outline" aria-pressed={hideIgnored} onClick={() => setHideIgnored((v) => !v)}>
              <EyeOff size={16} /> {hideIgnored ? "Show ignored" : "Hide ignored"}
            </Button>
            <Button variant="outline" onClick={() => void load()}>
              <RefreshCw size={16} /> Refresh
            </Button>
          </>
        }
      />

      <div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
        <KpiCard label="Unmatched" value={stats.unmatched} hint="No suggestion yet" icon={Landmark} tone="neutral" />
        <KpiCard label="Suggested" value={stats.suggested} hint="Awaiting your review" icon={Landmark} tone="amber" />
        <KpiCard label="Needs review" value={stats.needsReview} hint="Low-confidence matches" icon={Landmark} tone="amber" />
        <KpiCard label="Matched" value={stats.matched} hint="Approved matches" icon={Landmark} tone="green" />
      </div>

      <div className="grid gap-3 sm:grid-cols-3">
        <KpiCard label="High confidence" value={confidenceStats.high} hint="Score ≥ 85%" icon={Landmark} tone="green" />
        <KpiCard label="Medium confidence" value={confidenceStats.medium} hint="Score 60–84%" icon={Landmark} tone="amber" />
        <KpiCard label="Low confidence" value={confidenceStats.low} hint="Score below 60%" icon={Landmark} tone="neutral" />
      </div>

      <SectionCard>
        <div className="flex flex-wrap items-center gap-2 border-b border-border px-4 py-3">
          <div className="relative">
            <Search size={16} className="pointer-events-none absolute top-1/2 left-2.5 -translate-y-1/2 text-muted-foreground" />
            <Input
              value={search}
              onChange={(e) => setSearch(e.target.value)}
              placeholder="Search transactions"
              className="h-9 w-56 bg-surface pl-8"
            />
          </div>
          <Select value={status} onValueChange={setStatus}>
            <SelectTrigger className="h-9 w-[160px] bg-surface">
              <SelectValue placeholder="Status" />
            </SelectTrigger>
            <SelectContent>
              {STATUS_FILTERS.map((s) => (
                <SelectItem key={s.value} value={s.value}>
                  {s.label}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>
          <Select
            value={client}
            onValueChange={(value) => {
              setClient(value);
              void navigate({
                to: "/bank",
                search: value === "all" ? {} : { client: value },
                replace: true,
              });
            }}
          >
            <SelectTrigger className="h-9 w-[170px] bg-surface">
              <SelectValue placeholder="Client" />
            </SelectTrigger>
            <SelectContent>
              <SelectItem value="all">All clients</SelectItem>
              {clients.map((c) => (
                <SelectItem key={c.id} value={c.id}>
                  {c.name}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>
          <Input type="date" value={dateFrom} onChange={(e) => setDateFrom(e.target.value)} className="h-9 w-[150px] bg-surface" />
          <Input type="date" value={dateTo} onChange={(e) => setDateTo(e.target.value)} className="h-9 w-[150px] bg-surface" />
        </div>

        {loading ? (
          <div className="px-6 py-12 text-center text-[13px] text-muted-foreground">Loading bank transactions…</div>
        ) : error ? (
          <EmptyState
            icon={Landmark}
            title="Could not load transactions"
            description={error}
            action={
              <Button variant="outline" onClick={() => void load()}>
                Try again
              </Button>
            }
          />
        ) : emptyAll ? (
          <EmptyState
            icon={Landmark}
            title="No bank transactions available yet"
            description={
              !snelstartIntegrationEnabled
                ? "SnelStart integration is not enabled yet. Bank transactions can still be added manually for matching."
                : snelstartConnected
                  ? "Bank transactions will appear here once they are synchronized from SnelStart."
                  : "SnelStart is not connected for any client. Connect the integration to import bank transactions."
            }
          />
        ) : caughtUp ? (
          <EmptyState
            icon={Landmark}
            title="You're all caught up"
            description="No transactions currently require matching."
          />
        ) : emptyFiltered ? (
          <EmptyState icon={Landmark} title="No transactions found" description="No rows match your current filters." />
        ) : (
          <div className="overflow-x-auto">
            <table className="w-full min-w-[1100px] text-[13.5px]">
              <thead className="border-b border-border bg-muted/50 text-[11.5px] tracking-wide text-muted-foreground uppercase">
                <tr>
                  <th className="px-5 py-3 text-left font-semibold">Date</th>
                  <th className="px-3 py-3 text-left font-semibold">Description</th>
                  <th className="px-3 py-3 text-right font-semibold">Amount</th>
                  <th className="px-3 py-3 text-left font-semibold">Client</th>
                  <th className="px-3 py-3 text-left font-semibold">Suggested match</th>
                  <th className="px-3 py-3 text-left font-semibold">Confidence</th>
                  <th className="px-3 py-3 text-left font-semibold">Status</th>
                  <th className="px-5 py-3 text-right font-semibold">Action</th>
                </tr>
              </thead>
              <tbody className="divide-y divide-border">
                {visibleRows.map((t) => {
                  const suggested = t.suggestedInvoiceId ? getInvoice(t.suggestedInvoiceId) : undefined;
                  return (
                    <tr key={t.id} className="transition-colors hover:bg-muted/50">
                      <td className="px-5 py-3 text-muted-foreground">{t.date || "—"}</td>
                      <td className="px-3 py-3">
                        <p className="font-medium">{t.description}</p>
                        {t.paymentReference ? (
                          <p className="mt-0.5 text-[11.5px] text-muted-foreground">{t.paymentReference}</p>
                        ) : null}
                      </td>
                      <td className="num px-3 py-3 text-right font-medium">{eur(t.amount)}</td>
                      <td className="px-3 py-3 text-muted-foreground">{clientName(t.clientId)}</td>
                      <td className="px-3 py-3">
                        {suggested ? (
                          <Link to="/invoices/$id" params={{ id: suggested.id }} search={{}} className="text-primary hover:underline">
                            {suggested.number?.trim() ? suggested.number : suggested.file}
                          </Link>
                        ) : (
                          <span className="text-muted-foreground">—</span>
                        )}
                      </td>
                      <td className="px-3 py-3">
                        {t.confidence && t.confidence > 0 ? (
                          <div className="space-y-0.5">
                            <ConfidenceIndicator value={t.confidence} compact />
                            <span className="text-[11px] text-muted-foreground">{confidenceLabel(t.confidence)}</span>
                          </div>
                        ) : (
                          <span className="text-muted-foreground">—</span>
                        )}
                      </td>
                      <td className="px-3 py-3">
                        <Pill tone={TONE[t.status]}>{t.status}</Pill>
                      </td>
                      <td className="px-5 py-3 text-right">
                        {t.status === "Matched" || t.status === "Ignored" ? (
                          <Button size="sm" variant="outline" onClick={() => void openReview(t)}>
                            View
                          </Button>
                        ) : (
                          <Button size="sm" onClick={() => void openReview(t)}>
                            Review
                          </Button>
                        )}
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        )}
      </SectionCard>

      <Dialog open={!!reviewTx} onOpenChange={(open) => !open && closeReview()}>
        <DialogContent className="max-h-[90vh] overflow-y-auto sm:max-w-2xl">
          {reviewTx ? (
            <>
              <DialogHeader>
                <DialogTitle>Review match</DialogTitle>
                <DialogDescription>Confirm or adjust the bookkeeping match for this bank transaction.</DialogDescription>
              </DialogHeader>

              <div className="space-y-4">
                <div className="rounded-xl border border-border bg-muted/30 p-4">
                  <p className="text-[12px] font-semibold uppercase tracking-wide text-muted-foreground">Bank transaction</p>
                  <p className="mt-2 font-medium">{reviewTx.description}</p>
                  <p className="text-[13px] text-muted-foreground">
                    {reviewTx.date || "—"} · {clientName(reviewTx.clientId)} · {eur(reviewTx.amount)}
                  </p>
                  {reviewTx.paymentReference ? (
                    <p className="mt-1 text-[12.5px] text-muted-foreground">Ref: {reviewTx.paymentReference}</p>
                  ) : null}
                </div>

                <div>
                  <p className="mb-2 text-[13px] font-semibold">Suggested matches</p>
                  {matches.length === 0 ? (
                    <p className="rounded-lg border border-dashed border-border px-4 py-6 text-[13px] text-muted-foreground">
                      No confident match found. Try searching manually for an invoice or bookkeeping record.
                    </p>
                  ) : (
                    <ul className="space-y-2">
                      {matches.map((m) => (
                        <li key={m.invoiceId} className="rounded-lg border border-border p-3">
                          <div className="flex flex-wrap items-start justify-between gap-2">
                            <div>
                              <p className="font-medium">
                                {m.supplier} · {m.number}
                              </p>
                              <p className="text-[12.5px] text-muted-foreground">
                                {eur(m.total)} · Outstanding {eur(m.outstanding)} · {m.confidenceLabel} ({Math.round(m.score)}%)
                              </p>
                              {m.isPartial ? (
                                <p className="mt-1 text-[12px] text-attention">Partial payment allocation</p>
                              ) : null}
                            </div>
                            {reviewTx.status !== "Matched" && reviewTx.status !== "Ignored" ? (
                              <Button
                                size="sm"
                                onClick={() => {
                                  approveBankMatch(reviewTx.id, m.invoiceId, m.allocatedAmount);
                                  void afterAction();
                                }}
                              >
                                Match
                              </Button>
                            ) : null}
                          </div>
                        </li>
                      ))}
                    </ul>
                  )}
                </div>

                <div className="space-y-2">
                  <Label htmlFor="manual-search">Search records</Label>
                  <div className="flex gap-2">
                    <Input
                      id="manual-search"
                      value={manualQuery}
                      onChange={(e) => setManualQuery(e.target.value)}
                      placeholder="Invoice number or supplier"
                      className="bg-surface"
                    />
                    <Button variant="outline" onClick={() => void runManualSearch()} disabled={!manualQuery.trim()}>
                      Search
                    </Button>
                  </div>
                  {manualResults.length > 0 ? (
                    <ul className="space-y-2 pt-2">
                      {manualResults.map((m) => (
                        <li key={m.invoiceId} className="flex items-center justify-between rounded-lg border border-border px-3 py-2">
                          <div>
                            <p className="text-[13px] font-medium">
                              {m.supplier} · {m.number}
                            </p>
                            <p className="text-[12px] text-muted-foreground">{eur(m.total)} · {m.confidenceLabel}</p>
                          </div>
                          {reviewTx.status !== "Matched" && reviewTx.status !== "Ignored" ? (
                            <Button
                              size="sm"
                              variant="outline"
                              onClick={() => {
                                approveBankMatch(reviewTx.id, m.invoiceId, m.allocatedAmount);
                                void afterAction();
                              }}
                            >
                              Select
                            </Button>
                          ) : null}
                        </li>
                      ))}
                    </ul>
                  ) : null}
                </div>
              </div>

              {reviewTx.status !== "Matched" && reviewTx.status !== "Ignored" ? (
                <DialogFooter className="gap-2 sm:justify-between">
                  <div className="flex flex-wrap gap-2">
                    <Button
                      variant="outline"
                      onClick={() => {
                        rejectBankMatch(reviewTx.id);
                        void afterAction();
                      }}
                    >
                      Reject suggestion
                    </Button>
                    <Button variant="ghost" onClick={() => setIgnoreOpen(true)}>
                      Ignore
                    </Button>
                  </div>
                  <Button variant="outline" onClick={closeReview}>
                    Close
                  </Button>
                </DialogFooter>
              ) : (
                <DialogFooter>
                  <Button variant="outline" onClick={closeReview}>
                    Close
                  </Button>
                </DialogFooter>
              )}
            </>
          ) : null}
        </DialogContent>
      </Dialog>

      <Dialog open={ignoreOpen} onOpenChange={setIgnoreOpen}>
        <DialogContent className="sm:max-w-md">
          <DialogHeader>
            <DialogTitle>Ignore transaction</DialogTitle>
            <DialogDescription>This transaction will not require matching. A reason is required.</DialogDescription>
          </DialogHeader>
          <Textarea
            value={ignoreReason}
            onChange={(e) => setIgnoreReason(e.target.value)}
            placeholder="Bank fee already accounted for"
            className="min-h-24"
          />
          <DialogFooter>
            <Button variant="outline" onClick={() => setIgnoreOpen(false)}>
              Cancel
            </Button>
            <Button
              disabled={!ignoreReason.trim() || !reviewTx}
              onClick={() => {
                if (!reviewTx) return;
                ignoreBankTransaction(reviewTx.id, ignoreReason.trim());
                setIgnoreOpen(false);
                setIgnoreReason("");
                void afterAction();
              }}
            >
              Ignore transaction
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </div>
  );
}
