import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useMemo, useRef, useState } from "react";
import { accounting } from "@/lib/accountingClient";
import { supabase } from "@/integrations/supabase/client";
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 { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, DialogFooter } from "@/components/ui/dialog";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Table, TableHeader, TableRow, TableHead, TableBody, TableCell } from "@/components/ui/table";
import { Plus, Trash2, Search, Receipt, Upload, Sparkles, FileText, X, AlertCircle, Printer, Loader2, Pencil, Repeat } from "lucide-react";
import { Link } from "react-router-dom";
import { toast } from "sonner";
import { money, fmtDate } from "./lib/format";
import { StatusBadge } from "./components/StatusBadge";
import { EmptyState } from "./components/EmptyState";
import { TableSkeleton } from "./components/TableSkeleton";
import { parseBill } from "./lib/parseBill";
import { cn } from "@/lib/utils";
import { Switch } from "@/components/ui/switch";
import { Textarea } from "@/components/ui/textarea";
import { generateCheckPDF } from "./lib/checkPdf";

type Item = { description: string; quantity: number; rate: number; account_id?: string | null };

function deriveStatus(b: any): string {
  if (b.status === "void" || b.status === "draft") return b.status;
  const paid = Number(b.paid_amount ?? 0);
  const total = Number(b.total ?? 0);
  if (paid >= total && total > 0) return "paid";
  if (paid > 0 && paid < total) return "partially_paid";
  if (b.due_date && new Date(b.due_date) < new Date() && paid < total) return "overdue";
  return "open";
}

const ACCEPT = "application/pdf,image/jpeg,image/jpg,image/png,image/heic,image/heif";
const HL = "bg-sky-50 border-sky-300";
const ERR = "bg-red-50 border-red-400";

export default function AccountingBillsPage() {
  const { companyId, loading: companyLoading, error: companyError, associationId, associationName } = useCompanyId();
  const cid = companyId ?? "";
  const cur = "USD";
  const qc = useQueryClient();
  const parseFn = parseBill;
  const [open, setOpen] = useState(false);
  const [editId, setEditId] = useState<string | null>(null);
  const [editBill, setEditBill] = useState<any | null>(null);
  const [search, setSearch] = useState("");
  const [statusFilter, setStatusFilter] = useState<string>("all");
  const [autoOnly, setAutoOnly] = useState(false);

  const [vendorId, setVendorId] = useState("");
  const [number, setNumber] = useState("");
  const [issueDate, setIssueDate] = useState(new Date().toLocaleDateString("en-CA", { timeZone: "America/New_York" }));
  const [dueDate, setDueDate] = useState("");
  const [taxPct, setTaxPct] = useState(0);
  const [notes, setNotes] = useState("");
  const [items, setItems] = useState<Item[]>([{ description: "", quantity: 1, rate: 0, account_id: null }]);

  // Upload + parse state
  const [file, setFile] = useState<File | null>(null);
  const [filePreview, setFilePreview] = useState<string | null>(null);
  const [uploadedUrl, setUploadedUrl] = useState<string | null>(null);
  const [parsing, setParsing] = useState(false);
  const [aiFilled, setAiFilled] = useState<Set<string>>(new Set()); // field keys that were AI-filled
  const [missingFields, setMissingFields] = useState<Set<string>>(new Set());
  const [dragOver, setDragOver] = useState(false);
  const [pageDragOver, setPageDragOver] = useState(false);
  const fileRef = useRef<HTMLInputElement>(null);

  const { data: bills = [], isLoading } = useQuery({
    queryKey: ["bills", cid],
    enabled: !!cid,
    queryFn: async () => (await accounting.from("bills").select("*, vendors(name,address)").eq("company_id", cid).order("issue_date", { ascending: false })).data ?? [],
  });
  // Single vendor roster = public.vendors, scoped to this company's association.
  // The chosen public vendor is mapped to its accounting.vendors row on save.
  const { data: vendors = [] } = useQuery({
    queryKey: ["vendors-lookup", associationId],
    enabled: !!associationId,
    queryFn: async () => (await supabase.from("vendors")
      .select("id,name")
      .eq("is_active", true)
      .or(`association_id.eq.${associationId},association_ids.cs.{${associationId}}`)
      .order("name")).data ?? [],
  });
  // Bill line categories: expenses plus asset/equity/liability accounts
  // (e.g. prepaid insurance, reserve-funded repairs to an equity component) —
  // not just expense accounts. Bank accounts are excluded.
  const { data: expenseAccounts = [] } = useQuery({
    queryKey: ["bill-category-accounts", cid],
    enabled: !!cid,
    queryFn: async () =>
      (await accounting.from("accounts").select("id,name,code,type")
        .eq("company_id", cid).in("type", ["expense", "asset", "equity", "liability"])
        .eq("is_bank", false).eq("is_archived", false).order("type").order("code")).data ?? [],
  });

  const enriched = useMemo(
    () => (bills as any[]).map((b: any) => ({ ...b, derivedStatus: deriveStatus(b), balance: Math.max(0, Number(b.total) - Number(b.paid_amount ?? 0)) })),
    [bills]
  );
  const filtered = enriched.filter((b: any) => {
    if (statusFilter !== "all" && b.derivedStatus !== statusFilter) return false;
    if (autoOnly && !b.auto_created) return false;
    if (search && !`${b.number} ${b.vendors?.name ?? ""}`.toLowerCase().includes(search.toLowerCase())) return false;
    return true;
  });

  const aging = useMemo(() => {
    const buckets = { current: 0, "0-30": 0, "31-60": 0, "60+": 0 };
    const today = new Date();
    enriched.forEach((b: any) => {
      if (b.derivedStatus === "paid" || b.derivedStatus === "void" || b.derivedStatus === "draft") return;
      if (!b.due_date) { buckets.current += b.balance; return; }
      const days = Math.floor((today.getTime() - new Date(b.due_date).getTime()) / 86400000);
      if (days < 0) buckets.current += b.balance;
      else if (days <= 30) buckets["0-30"] += b.balance;
      else if (days <= 60) buckets["31-60"] += b.balance;
      else buckets["60+"] += b.balance;
    });
    return buckets;
  }, [enriched]);

  const totalDue = aging.current + aging["0-30"] + aging["31-60"] + aging["60+"];

  const subtotal = useMemo(() => items.reduce((s, i) => s + Number(i.quantity) * Number(i.rate), 0), [items]);
  const tax = +(subtotal * (taxPct / 100)).toFixed(2);
  const total = +(subtotal + tax).toFixed(2);

  const resetForm = () => {
    setEditId(null); setEditBill(null);
    setVendorId(""); setNumber(""); setDueDate(""); setTaxPct(0); setNotes("");
    setItems([{ description: "", quantity: 1, rate: 0, account_id: null }]);
    setFile(null); setFilePreview(null); setUploadedUrl(null);
    setAiFilled(new Set()); setMissingFields(new Set());
  };

  const openEdit = async (b: any) => {
    setEditId(b.id);
    setEditBill(b);
    // The dropdown is keyed by public vendor id; map the stored accounting
    // vendor back to its source public vendor when one exists.
    let pubVendorId = "";
    if (b.vendor_id) {
      const { data: av } = await accounting.from("vendors").select("external_source, external_id").eq("id", b.vendor_id).maybeSingle();
      if (av?.external_source === "acmacc_vendor" && av?.external_id) pubVendorId = String(av.external_id);
    }
    setVendorId(pubVendorId);
    setNumber(b.number ?? "");
    setIssueDate(b.issue_date ?? issueDate);
    setDueDate(b.due_date ?? "");
    const sub = Number(b.subtotal || 0);
    setTaxPct(sub > 0 ? +((Number(b.tax || 0) / sub) * 100).toFixed(4) : 0);
    setNotes(b.notes ?? "");
    setUploadedUrl(b.attachment_url ?? null);
    setFile(null); setFilePreview(null);
    setAiFilled(new Set()); setMissingFields(new Set());
    const { data: its } = await accounting.from("bill_items").select("*").eq("bill_id", b.id);
    setItems(its && its.length
      ? its.map((i: any) => ({ description: i.description ?? "", quantity: Number(i.quantity ?? 1), rate: Number(i.rate ?? 0), account_id: i.account_id ?? null }))
      : [{ description: "", quantity: 1, rate: 0, account_id: null }]);
    setOpen(true);
  };

  const handleFile = (f: File) => {
    setFile(f);
    setUploadedUrl(null);
    if (f.type.startsWith("image/")) {
      setFilePreview(URL.createObjectURL(f));
    } else {
      setFilePreview(null);
    }
    // Open dialog immediately and auto-parse
    setOpen(true);
    runParse(f);
  };

  const uploadFileObj = async (f: File): Promise<string | null> => {
    if (!cid) return null;
    const ext = f.name.split(".").pop() || "bin";
    const path = `${cid}/${Date.now()}-${Math.random().toString(36).slice(2, 8)}.${ext}`;
    const { error } = await supabase.storage.from("bill-attachments").upload(path, f, {
      contentType: f.type || "application/octet-stream",
      upsert: false,
    });
    if (error) { toast.error(`Upload failed: ${error.message}`); return null; }
    const { data } = supabase.storage.from("bill-attachments").getPublicUrl(path);
    setUploadedUrl(data.publicUrl);
    return data.publicUrl;
  };

  const matchAccount = (desc: string): string | null => {
    if (!desc || (expenseAccounts as any[]).length === 0) return null;
    const d = desc.toLowerCase();
    let best: { id: string; score: number } | null = null;
    for (const a of expenseAccounts as any[]) {
      const name = String(a.name).toLowerCase();
      let score = 0;
      if (d.includes(name) || name.includes(d)) score = 100;
      else {
        const words = name.split(/\s+/).filter((w) => w.length > 3);
        score = words.reduce((s, w) => (d.includes(w) ? s + 10 : s), 0);
      }
      if (score > 0 && (!best || score > best.score)) best = { id: a.id, score };
    }
    return best?.id ?? null;
  };

  const runParse = async (f: File) => {
    setParsing(true);
    try {
      const url = await uploadFileObj(f);
      if (!url) { setParsing(false); return; }

      const parsed = await parseFn({ data: { fileUrl: url, mimeType: f.type || "application/octet-stream" } });

      const filled = new Set<string>();
      const missing = new Set<string>();

      if (parsed.bill_number) { setNumber(String(parsed.bill_number)); filled.add("number"); } else missing.add("number");
      if (parsed.bill_date) { setIssueDate(String(parsed.bill_date).slice(0, 10)); filled.add("issueDate"); }
      if (parsed.due_date) { setDueDate(String(parsed.due_date).slice(0, 10)); filled.add("dueDate"); }
      if (parsed.notes) { setNotes(String(parsed.notes)); filled.add("notes"); }

      // Vendor match
      if (parsed.vendor_name) {
        const vn = String(parsed.vendor_name).toLowerCase();
        const v = (vendors as any[]).find((x) => String(x.name).toLowerCase() === vn || String(x.name).toLowerCase().includes(vn) || vn.includes(String(x.name).toLowerCase()));
        if (v) { setVendorId(v.id); filled.add("vendor"); }
        else filled.add("vendor_unmatched");
      }

      // Line items
      if (Array.isArray(parsed.line_items) && parsed.line_items.length > 0) {
        const newItems: Item[] = parsed.line_items.map((li: any) => {
          const desc = String(li.description ?? "");
          const qty = Number(li.quantity ?? 1) || 1;
          const rate = Number(li.unit_price ?? li.amount ?? 0) || 0;
          return { description: desc, quantity: qty, rate, account_id: matchAccount(desc) };
        });
        setItems(newItems);
        filled.add("items");
      }

      // Tax % from subtotal/tax
      const sub = Number(parsed.subtotal ?? 0);
      const taxAmt = Number(parsed.tax_amount ?? 0);
      if (sub > 0 && taxAmt > 0) { setTaxPct(+((taxAmt / sub) * 100).toFixed(2)); filled.add("tax"); }

      setAiFilled(filled);
      setMissingFields(missing);
      toast.success("Bill parsed — please review highlighted fields.");
    } catch (e: any) {
      toast.error(e?.message ?? "Failed to parse bill");
    } finally {
      setParsing(false);
    }
  };

  const save = async (keepOpen = false) => {
    if (!number.trim()) return toast.error("Bill number required");
    if (!vendorId) return toast.error("Select a vendor (Pay to) before saving");
    try {
    let attachmentUrl = uploadedUrl;
    if (file && !attachmentUrl) attachmentUrl = await uploadFileObj(file);

    // Resolve the chosen public vendor to its accounting.vendors row (find-or-create).
    let acctVendorId: string | null = null;
    if (vendorId) {
      const { data: mapped, error: mapErr } = await supabase.rpc("ensure_accounting_vendor", {
        _association_id: associationId,
        _public_vendor_id: vendorId,
      });
      if (mapErr) return toast.error(mapErr.message);
      acctVendorId = (mapped as string) ?? null;
    }

    const itemRows = (billId: string) => items.map(i => ({
      bill_id: billId, description: i.description, quantity: i.quantity, rate: i.rate,
      amount: +(Number(i.quantity) * Number(i.rate)).toFixed(2),
      account_id: i.account_id || null,
    }));

    if (editId) {
      const { error } = await accounting.from("bills").update({
        vendor_id: acctVendorId, number,
        issue_date: issueDate, due_date: dueDate || null,
        subtotal, tax, total,
        notes: notes || null,
        attachment_url: attachmentUrl,
      }).eq("id", editId);
      if (error) return toast.error(error.message);
      await accounting.from("bill_items").delete().eq("bill_id", editId);
      const { error: biErr } = await accounting.from("bill_items").insert(itemRows(editId));
      if (biErr) return toast.error(biErr.message);

      // Keep an already-paid bill's payment in sync with the edited amount. When
      // the total changed and the bill has a single linked bill payment, update
      // that payment (bank transaction + check) and the paid amount to match.
      const wasPaid = Number(editBill?.paid_amount ?? 0) > 0;
      const amountChanged = Math.abs(Number(editBill?.total ?? 0) - total) > 0.005;
      if (wasPaid && amountChanged) {
        const { data: pays } = await accounting.from("transactions").select("id").eq("bill_id", editId);
        if ((pays?.length ?? 0) === 1) {
          await accounting.from("transactions").update({ amount: total }).eq("bill_id", editId);
          await accounting.from("checks").update({ amount: total }).eq("source_bill_id", editId);
          await accounting.from("bills").update({ paid_amount: total, status: total > 0 ? "paid" : "open" }).eq("id", editId);
          toast.success("Bill and payment updated");
        } else {
          toast.warning("Bill updated — it has split/partial payments, so review the payment(s) manually.");
        }
      } else {
        toast.success("Bill updated");
      }
    } else {
      const { data: bill, error } = await accounting.from("bills").insert({
        company_id: cid, vendor_id: acctVendorId, number,
        issue_date: issueDate, due_date: dueDate || null,
        subtotal, tax, total, status: "open",
        notes: notes || null,
        attachment_url: attachmentUrl,
      }).select().single();
      if (error || !bill) return toast.error(error?.message ?? "Failed");
      await accounting.from("bill_items").insert(itemRows(bill.id));
      toast.success("Bill recorded");
    }
    resetForm();
    if (!keepOpen) setOpen(false);
    qc.invalidateQueries({ queryKey: ["bills", cid] });
    qc.invalidateQueries({ queryKey: ["transactions", cid] });
    qc.invalidateQueries({ queryKey: ["accounts", cid] });
    } catch (e: any) {
      toast.error(e?.message ?? "Failed to save bill");
    }
  };

  // ── Read-only bill detail view ──
  const [viewBill, setViewBill] = useState<any | null>(null);
  const [viewItems, setViewItems] = useState<any[]>([]);
  const openView = async (b: any) => {
    setViewBill(b);
    const { data } = await accounting.from("bill_items").select("*").eq("bill_id", b.id);
    setViewItems(data ?? []);
  };

  // Clone a bill into a fresh create form (today's date, blank number).
  const duplicateBill = async (b: any) => {
    setViewBill(null);
    let pubVendorId = "";
    if (b.vendor_id) {
      const { data: av } = await accounting.from("vendors").select("external_source, external_id").eq("id", b.vendor_id).maybeSingle();
      if (av?.external_source === "acmacc_vendor" && av?.external_id) pubVendorId = String(av.external_id);
    }
    const { data: its } = await accounting.from("bill_items").select("*").eq("bill_id", b.id);
    setEditId(null);
    setVendorId(pubVendorId);
    setNumber("");
    setIssueDate(new Date().toLocaleDateString("en-CA", { timeZone: "America/New_York" }));
    setDueDate("");
    const sub = Number(b.subtotal || 0);
    setTaxPct(sub > 0 ? +((Number(b.tax || 0) / sub) * 100).toFixed(4) : 0);
    setNotes(b.notes ?? "");
    setUploadedUrl(null); setFile(null); setFilePreview(null);
    setAiFilled(new Set()); setMissingFields(new Set());
    setItems(its && its.length
      ? its.map((i: any) => ({ description: i.description ?? "", quantity: Number(i.quantity ?? 1), rate: Number(i.rate ?? 0), account_id: i.account_id ?? null }))
      : [{ description: "", quantity: 1, rate: 0, account_id: null }]);
    setOpen(true);
  };

  // Payment dialog state

  const [payBill, setPayBill] = useState<any | null>(null);
  const [payAmount, setPayAmount] = useState(0);
  const [payDate, setPayDate] = useState(new Date().toLocaleDateString("en-CA", { timeZone: "America/New_York" }));
  const [payMethod, setPayMethod] = useState<"check" | "ach" | "wire" | "credit_card" | "cash" | "other">("check");
  const [payAccountId, setPayAccountId] = useState("");
  const [payReference, setPayReference] = useState("");
  const [payMemo, setPayMemo] = useState("");
  const [printCheck, setPrintCheck] = useState(true);
  const [paying, setPaying] = useState(false);

  const { data: bankAccounts = [] } = useQuery({
    queryKey: ["bank-accounts", cid, "bills-payment"],
    enabled: !!cid,
    queryFn: async () =>
      // Payment accounts: banks plus equity accounts (e.g. reserve components
      // paid directly from equity). Archived accounts never appear.
      (await accounting.from("accounts").select("id,name,code,balance,is_bank,type").eq("company_id", cid).or("is_bank.eq.true,type.eq.equity").eq("is_archived", false).order("code")).data ?? [],
  });

  const openPayment = async (b: any) => {
    setPayBill(b);
    setPayAmount(b.balance);
    setPayDate(new Date().toLocaleDateString("en-CA", { timeZone: "America/New_York" }));
    setPayMethod("check");
    setPayMemo("");
    setPrintCheck(true);
    const def = (bankAccounts as any[])[0]?.id ?? "";
    setPayAccountId(def);
    const { data: cs } = await accounting.from("check_settings").select("next_check_number").eq("company_id", cid).maybeSingle();
    setPayReference(String(cs?.next_check_number ?? 1001));
  };

  const onMethodChange = async (m: typeof payMethod) => {
    setPayMethod(m);
    if (m === "check") {
      setPrintCheck(true);
      const { data: cs } = await accounting.from("check_settings").select("next_check_number").eq("company_id", cid).maybeSingle();
      setPayReference(String(cs?.next_check_number ?? 1001));
    } else {
      setPrintCheck(false);
      setPayReference("");
    }
  };

  const printCheckPDF = async (checkNumber: number, vendorName: string, amount: number, date: string, memo: string, bankAccount: any) => {
    const { data: cs } = await accounting.from("check_settings").select("*").eq("company_id", cid).maybeSingle();
    const { data: billItems } = await accounting.from("bill_items").select("description,amount").eq("bill_id", payBill?.id ?? "");
    const vendorAddress = payBill?.vendors?.address ?? undefined;

    // Return-address (payer) block comes from the company check layout the user
    // configured in Settings → Check Layout. Without this, checks print no
    // return address.
    const { data: layout } = await supabase
      .from("company_check_layouts")
      .select("payer_name, payer_address, show_payer_block")
      .maybeSingle();
    // Fall back to the association's General Settings name/address (accounting.companies).
    const { data: companyInfo } = await accounting.from("companies").select("name, address").eq("id", cid).maybeSingle();
    const payerName = (layout?.payer_name || "").trim() || ((companyInfo as any)?.name ?? "");
    const showPayer = layout?.show_payer_block !== false;
    const returnAddress = showPayer ? (layout?.payer_address || (companyInfo as any)?.address || undefined) : undefined;

    const dataUrl = generateCheckPDF(
      [{
        companyName: (payerName || associationName) ?? "Company",
        companyAddress: returnAddress,
        bankName: cs?.bank_name ?? bankAccount?.name ?? undefined,
        bankAddress: cs?.bank_address ?? undefined,
        routingNumber: cs?.routing_number ?? undefined,
        accountNumber: cs?.account_number ?? undefined,
        fractionalRouting: (cs as any)?.fractional_routing ?? undefined,
        checkNumber,
        date: fmtDate(date),
        payee: vendorName,
        payeeAddress: vendorAddress,
        amount,
        memo: memo || undefined,
        lineItems: (billItems ?? []).map((i: any) => ({ description: i.description, amount: Number(i.amount) })),
        printSignature: cs?.print_signature ?? false,
        signatureDataUrl: cs?.signature_url ?? undefined,
      }],
      {
        style: (cs?.default_style as any) ?? "voucher",
        position: (cs?.default_position as any) ?? "top",
        fontSize: (cs?.font_size as any) ?? "medium",
        offsetX: (cs as any)?.offset_x ?? 0,
        offsetY: (cs as any)?.offset_y ?? 0,
        micrOffsetY: (cs as any)?.micr_offset_y ?? 0,
        micrGap1: (cs as any)?.micr_gap_1 ?? 1,
        micrGap2: (cs as any)?.micr_gap_2 ?? 1,
        fieldPositions: (cs as any)?.field_positions ?? {},
      }
    );
    const w = window.open("");
    if (w) {
      w.document.write(`<iframe src="${dataUrl}" style="border:0;width:100%;height:100vh" onload="this.contentWindow.print()"></iframe>`);
    }
  };

  const savePayment = async () => {
    if (!payBill || !payAccountId) return toast.error("Payment account required");
    if (!payAmount || payAmount <= 0) return toast.error("Invalid amount");
    setPaying(true);
    try {
      const bankAccount = (bankAccounts as any[]).find((a) => a.id === payAccountId);
      const vendorName = payBill.vendors?.name ?? "Vendor";
      const refLabel = payReference || payMethod.toUpperCase();

      // Expense category name(s) from the bill — shown on the payment line in the
      // transaction journal for visibility (display only; does not affect the GL).
      const { data: payItems } = await accounting
        .from("bill_items").select("account_id").eq("bill_id", payBill.id).not("account_id", "is", null);
      const payAcctIds = Array.from(new Set((payItems ?? []).map((i: any) => i.account_id)));
      let categoryLabel = "Bill Payment";
      if (payAcctIds.length) {
        const { data: payAccs } = await accounting.from("accounts").select("name").in("id", payAcctIds);
        const names = (payAccs ?? []).map((a: any) => a.name).filter(Boolean);
        if (names.length) categoryLabel = names.join(", ");
      }

      // 1) Bank ledger transaction — debit = money OUT of the bank (payment to vendor).
      // A bill payment must clear Accounts Payable, NOT re-hit the expense account:
      // the expense was already recognized when the bill was entered (accrual).
      // coa_account_id stays null with vendor_id set, so post_transaction_gl posts
      // Dr Accounts Payable / Cr Bank (clears the payable, no second P&L hit). The
      // expense category is stored in `category` for the journal view only.
      await accounting.from("transactions").insert({
        company_id: cid,
        account_id: payAccountId,
        date: payDate,
        type: "debit",
        amount: payAmount,
        description: `Bill Payment · ${vendorName} · Bill ${payBill.number}`,
        category: categoryLabel,
        reference: refLabel,
        coa_account_id: null,                  // → posts against Accounts Payable (via vendor)
        vendor_id: payBill.vendor_id ?? null,  // required so the GL clears A/P
        bill_id: payBill.id,                   // links the payment to the bill it settles
      });

      // 2) Bank balance auto-updated by DB trigger trg_sync_account_balance

      // 3) Update bill paid amount (partial payments leave the bill partially_paid)
      const newPaid = Number(payBill.paid_amount ?? 0) + Number(payAmount);
      await accounting.from("bills").update({ paid_amount: newPaid, status: newPaid >= Number(payBill.total) - 0.005 ? "paid" : "partially_paid" }).eq("id", payBill.id);

      // 4) If check + print: insert check record, print, mark printed, bump next #
      if (payMethod === "check") {
        const checkNumber = parseInt(payReference, 10) || 1001;
        const { data: chk } = await accounting.from("checks").insert({
          company_id: cid,
          bank_account_id: payAccountId,
          check_number: checkNumber,
          date: payDate,
          payee_vendor_id: payBill.vendor_id,
          payee_name: vendorName,
          amount: payAmount,
          memo: payMemo || `Bill ${payBill.number}`,
          source_bill_id: payBill.id,
          status: printCheck ? "printed" : "unprinted",
          printed_at: printCheck ? new Date().toISOString() : null,
        }).select().single();

        // bump next check number
        const { data: csRow } = await accounting.from("check_settings").select("id,next_check_number").eq("company_id", cid).maybeSingle();
        if (csRow) {
          await accounting.from("check_settings").update({ next_check_number: Math.max(csRow.next_check_number, checkNumber + 1) }).eq("id", csRow.id);
        } else {
          await accounting.from("check_settings").insert({ company_id: cid, next_check_number: checkNumber + 1 });
        }

        if (printCheck && chk) {
          printCheckPDF(checkNumber, vendorName, payAmount, payDate, payMemo, bankAccount);
        }
      }

      toast.success("Payment recorded");
      setPayBill(null);
      qc.invalidateQueries({ queryKey: ["bills", cid] });
      qc.invalidateQueries({ queryKey: ["bank-accounts", cid] });
      qc.invalidateQueries({ queryKey: ["transactions", cid] });
      qc.invalidateQueries({ queryKey: ["accounts", cid] });
    } catch (e: any) {
      toast.error(e?.message ?? "Failed to record payment");
    } finally {
      setPaying(false);
    }
  };

  const remove = async (id: string) => {
    await accounting.from("bills").delete().eq("id", id);
    qc.invalidateQueries({ queryKey: ["bills", cid] });
  };

  const hl = (key: string) => cn(
    aiFilled.has(key) && HL,
    missingFields.has(key) && ERR,
  );

  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"
      onDragOver={(e) => { e.preventDefault(); setPageDragOver(true); }}
      onDragLeave={(e) => { if (!e.currentTarget.contains(e.relatedTarget as Node)) setPageDragOver(false); }}
      onDrop={(e) => {
        e.preventDefault();
        setPageDragOver(false);
        const f = e.dataTransfer.files?.[0];
        if (f) handleFile(f);
      }}
    >
      {/* Full-page drop overlay */}
      {pageDragOver && (
        <div className="fixed inset-0 z-50 flex items-center justify-center bg-primary/10 pointer-events-none">
          <div className="rounded-2xl border-4 border-dashed border-primary bg-white shadow-2xl px-12 py-10 text-center">
            <Upload className="mx-auto h-14 w-14 text-primary mb-4" />
            <div className="text-xl font-semibold">Drop bill to parse</div>
            <div className="text-sm text-muted-foreground mt-1">PDF, JPG, PNG, HEIC accepted</div>
          </div>
        </div>
      )}
      <div className="flex items-center justify-between">
        <h1 className="text-2xl font-semibold">Bills</h1>
        <div className="flex items-center gap-2">
        <Button asChild variant="outline"><Link to="/dashboard/accounting/recurring"><Repeat className="mr-1 h-4 w-4" /> Recurring</Link></Button>
        <Dialog open={open} onOpenChange={(o) => { setOpen(o); if (!o) resetForm(); }}>
          <DialogTrigger asChild><Button><Plus className="mr-1 h-4 w-4" /> New Bill</Button></DialogTrigger>
          <DialogContent className="max-w-5xl max-h-[92vh] overflow-hidden p-0 gap-0 flex flex-col">
            <DialogHeader className="px-6 py-4 border-b">
              <DialogTitle>{editId ? "Edit bill" : "Record bill"}</DialogTitle>
            </DialogHeader>

            <div className="flex-1 overflow-y-auto">
              {aiFilled.size > 0 && (
                <div className="mx-6 mt-4 rounded-md border border-amber-300 bg-amber-50 text-amber-900 px-3 py-2 text-sm flex items-start gap-2">
                  <AlertCircle className="h-4 w-4 mt-0.5 shrink-0" />
                  <span>AI has filled in this bill — please review highlighted fields before saving.</span>
                </div>
              )}

              <div className="grid grid-cols-1 lg:grid-cols-2 gap-6 p-6">
                {/* ── Left: attachment panel ── */}
                <div
                  onDragOver={(e) => { e.preventDefault(); setDragOver(true); }}
                  onDragLeave={() => setDragOver(false)}
                  onDrop={(e) => { e.preventDefault(); setDragOver(false); const f = e.dataTransfer.files?.[0]; if (f) handleFile(f); }}
                  className={cn(
                    "relative rounded-lg border-2 border-dashed min-h-[420px] flex flex-col items-center justify-center text-center p-6 transition-colors",
                    dragOver ? "border-primary bg-primary/5" : "border-border",
                    !filePreview && !file && !uploadedUrl && "cursor-pointer hover:bg-muted/40",
                  )}
                  onClick={() => { if (!filePreview && !file && !uploadedUrl) fileRef.current?.click(); }}
                >
                  <input ref={fileRef} type="file" accept={ACCEPT} className="hidden"
                    onChange={(e) => { const f = e.target.files?.[0]; if (f) handleFile(f); }} />

                  {parsing ? (
                    <div className="text-sm text-muted-foreground"><Loader2 className="mx-auto h-6 w-6 animate-spin mb-2" />Parsing with AI…</div>
                  ) : filePreview ? (
                    <img src={filePreview} alt="Bill" className="max-h-[400px] w-full object-contain rounded" />
                  ) : uploadedUrl && !file ? (
                    <a href={uploadedUrl} target="_blank" rel="noopener noreferrer" className="text-sm text-primary hover:underline inline-flex flex-col items-center gap-2">
                      <FileText className="h-12 w-12" /> View attached document
                    </a>
                  ) : file ? (
                    <div className="text-sm text-muted-foreground">
                      <FileText className="mx-auto h-12 w-12 mb-2 text-foreground/70" />
                      <div className="font-medium text-foreground">{file.name}</div>
                      <div className="text-xs">{(file.size / 1024).toFixed(1)} KB</div>
                    </div>
                  ) : (
                    <>
                      <Receipt className="h-12 w-12 text-muted-foreground/40" />
                      <div className="mt-3 font-medium">Upload &amp; view your attachment here</div>
                      <div className="text-xs text-muted-foreground mt-1">PDF, JPG, PNG, HEIC · max 20MB · auto-parses with AI</div>
                      <div className="text-xs text-muted-foreground mt-1">Drag &amp; drop or <span className="text-primary underline">browse</span></div>
                    </>
                  )}

                  {(file || uploadedUrl) && !parsing && (
                    <div className="mt-3 flex justify-center gap-2" onClick={(e) => e.stopPropagation()}>
                      {file && (
                        <Button size="sm" variant="outline" onClick={() => runParse(file)}>
                          <Sparkles className="mr-1 h-3.5 w-3.5" /> Re-parse
                        </Button>
                      )}
                      <Button size="sm" variant="outline" onClick={() => fileRef.current?.click()}>
                        <Upload className="mr-1 h-3.5 w-3.5" /> Replace
                      </Button>
                      <Button size="sm" variant="ghost" onClick={() => { setFile(null); setFilePreview(null); setUploadedUrl(null); setAiFilled(new Set()); setMissingFields(new Set()); }}>
                        <X className="h-3.5 w-3.5" />
                      </Button>
                    </div>
                  )}
                </div>

                {/* ── Right: form ── */}
                <div className="space-y-5">
                  <div>
                    <h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide mb-3">Bill Details</h3>
                    <div className="grid grid-cols-2 gap-3">
                      <div>
                        <Label>Date *</Label>
                        <Input type="date" value={issueDate} onChange={(e) => setIssueDate(e.target.value)} className={hl("issueDate")} />
                      </div>
                      <div>
                        <Label>Due *</Label>
                        <Input type="date" value={dueDate} onChange={(e) => setDueDate(e.target.value)} className={hl("dueDate")} />
                      </div>
                    </div>
                    <div className="mt-3">
                      <Label>Pay to *</Label>
                      <Select value={vendorId} onValueChange={setVendorId}>
                        <SelectTrigger className={hl("vendor") || (aiFilled.has("vendor_unmatched") ? ERR : "")}>
                          <SelectValue placeholder={aiFilled.has("vendor_unmatched") ? "Could not match — pick vendor" : "Select a vendor…"} />
                        </SelectTrigger>
                        <SelectContent>{(vendors as any[]).map((v: any) => <SelectItem key={v.id} value={v.id}>{v.name}</SelectItem>)}</SelectContent>
                      </Select>
                    </div>
                    <div className="grid grid-cols-2 gap-3 mt-3">
                      <div>
                        <Label>Reference number{missingFields.has("number") && <span className="text-red-600 text-xs ml-1">enter manually</span>}</Label>
                        <Input maxLength={40} value={number} onChange={(e) => setNumber(e.target.value)} className={hl("number")} />
                      </div>
                      <div>
                        <Label>Memo</Label>
                        <Input value={notes} onChange={(e) => setNotes(e.target.value)} className={hl("notes")} placeholder="Optional" />
                      </div>
                    </div>
                  </div>

                  <div>
                    <h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide mb-2">Item Details</h3>
                    <div className="rounded-md border overflow-hidden">
                      <div className="grid grid-cols-12 gap-2 bg-muted/50 px-3 py-2 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground">
                        <div className="col-span-4">Account</div>
                        <div className="col-span-4">Description</div>
                        <div className="col-span-1 text-right">Qty</div>
                        <div className="col-span-2 text-right">Rate</div>
                        <div className="col-span-1" />
                      </div>
                      {items.map((it, idx) => (
                        <div key={idx} className={cn("grid grid-cols-12 gap-2 px-3 py-2 border-t items-center", aiFilled.has("items") && "bg-sky-50/40")}>
                          <Select value={it.account_id ?? ""} onValueChange={(v) => { const a = [...items]; a[idx] = { ...it, account_id: v || null }; setItems(a); }}>
                            <SelectTrigger className="col-span-4 h-9 text-xs"><SelectValue placeholder="Select an account" /></SelectTrigger>
                            <SelectContent>
                              {(expenseAccounts as any[]).map((a) => (
                                <SelectItem key={a.id} value={a.id}>{a.code ? `${a.code} · ` : ""}{a.name}</SelectItem>
                              ))}
                            </SelectContent>
                          </Select>
                          <Input className="col-span-4 h-9" placeholder="Description" maxLength={200} value={it.description}
                            onChange={(e) => { const a = [...items]; a[idx] = { ...it, description: e.target.value }; setItems(a); }} />
                          <Input className="col-span-1 h-9 text-right" type="number" min={0} step="0.01" value={it.quantity}
                            onChange={(e) => { const a = [...items]; a[idx] = { ...it, quantity: Number(e.target.value) }; setItems(a); }} />
                          <Input className="col-span-2 h-9 text-right" type="number" min={0} step="0.01" value={it.rate}
                            onChange={(e) => { const a = [...items]; a[idx] = { ...it, rate: Number(e.target.value) }; setItems(a); }} />
                          <Button className="col-span-1 h-9 w-9 justify-self-end" variant="ghost" size="icon" onClick={() => setItems(items.filter((_, i) => i !== idx))}><Trash2 className="h-4 w-4" /></Button>
                        </div>
                      ))}
                    </div>
                    <Button className="mt-2" variant="outline" size="sm" onClick={() => setItems([...items, { description: "", quantity: 1, rate: 0, account_id: null }])}><Plus className="mr-1 h-3 w-3" /> Add item</Button>
                  </div>

                  <div className="flex items-end justify-between gap-4">
                    <div className="w-28">
                      <Label>Tax %</Label>
                      <Input type="number" min={0} value={taxPct} onChange={(e) => setTaxPct(Number(e.target.value))} className={hl("tax")} />
                    </div>
                    <div className="text-right text-sm space-y-0.5">
                      <div className="text-muted-foreground">Subtotal <span className="ml-3 text-foreground font-medium">{money(subtotal, cur)}</span></div>
                      <div className="text-muted-foreground">Tax <span className="ml-3 text-foreground font-medium">{money(tax, cur)}</span></div>
                    </div>
                  </div>
                </div>
              </div>
            </div>

            {/* Total bar */}
            <div className="bg-primary text-primary-foreground px-6 py-3 flex items-center justify-end text-sm font-medium">
              Total bill amount: <span className="ml-2 text-base font-semibold">{money(total, cur)}</span>
            </div>

            <DialogFooter className="px-6 py-3 border-t sm:justify-start gap-2">
              <Button onClick={() => save(false)}>{editId ? "Save bill" : "Save"}</Button>
              {!editId && <Button variant="outline" onClick={() => save(true)}>Save &amp; add another</Button>}
              <Button variant="ghost" onClick={() => { setOpen(false); resetForm(); }}>Cancel</Button>
            </DialogFooter>
          </DialogContent>
        </Dialog>
        </div>
      </div>

      {/* Aging summary */}
      <Card>
        <CardHeader className="pb-3">
          <CardTitle className="text-base">Payables aging · {money(totalDue, cur)} due</CardTitle>
        </CardHeader>
        <CardContent>
          <div className="grid grid-cols-2 gap-4 md:grid-cols-4">
            {[
              { label: "Not yet due", value: aging.current, tone: "bg-slate-50 text-slate-700 border-slate-200" },
              { label: "0 – 30 days", value: aging["0-30"], tone: "bg-amber-50 text-amber-700 border-amber-200" },
              { label: "31 – 60 days", value: aging["31-60"], tone: "bg-orange-50 text-orange-700 border-orange-200" },
              { label: "60+ days", value: aging["60+"], tone: "bg-red-50 text-red-700 border-red-200" },
            ].map((b) => (
              <div key={b.label} className={`rounded-lg border p-4 ${b.tone}`}>
                <div className="text-xs font-medium uppercase tracking-wide opacity-75">{b.label}</div>
                <div className="mt-2 text-2xl font-semibold">{money(b.value, cur)}</div>
              </div>
            ))}
          </div>
        </CardContent>
      </Card>

      <div className="flex flex-wrap items-center gap-3">
        <div className="relative flex-1 min-w-[220px]">
          <Search className="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
          <Input className="pl-9" placeholder="Search bill # or vendor" value={search} onChange={(e) => setSearch(e.target.value)} />
        </div>
        <Select value={statusFilter} onValueChange={setStatusFilter}>
          <SelectTrigger className="w-44"><SelectValue /></SelectTrigger>
          <SelectContent>
            <SelectItem value="all">All statuses</SelectItem>
            <SelectItem value="open">Open</SelectItem>
            <SelectItem value="overdue">Overdue</SelectItem>
            <SelectItem value="partially_paid">Partially Paid</SelectItem>
            <SelectItem value="paid">Paid</SelectItem>
          </SelectContent>
        </Select>
        <label className="inline-flex items-center gap-2 text-sm">
          <input type="checkbox" checked={autoOnly} onChange={(e) => setAutoOnly(e.target.checked)} className="rounded border-border" />
          Show Auto-Created Bills
        </label>
      </div>

      <Card>
        <CardHeader><CardTitle>All bills</CardTitle></CardHeader>
        <CardContent>
          <Table>
            <TableHeader>
              <TableRow>
                <TableHead>Bill #</TableHead>
                <TableHead>Vendor</TableHead>
                <TableHead>Issued</TableHead>
                <TableHead>Due</TableHead>
                <TableHead className="text-right">Total</TableHead>
                <TableHead className="text-right">Balance</TableHead>
                <TableHead>Status</TableHead>
                <TableHead></TableHead>
              </TableRow>
            </TableHeader>
            <TableBody>
              {isLoading && <TableSkeleton columns={8} />}
              {!isLoading && filtered.map((b: any) => (
                <TableRow key={b.id}>
                  <TableCell className="font-medium">
                    <button onClick={() => openView(b)} className="inline-flex items-center gap-1 text-primary hover:underline">
                      {b.attachment_url && <FileText className="h-3 w-3" />} {b.number}
                    </button>
                    {b.auto_created && <span className="ml-2 inline-flex items-center rounded bg-amber-100 text-amber-700 px-1.5 py-0.5 text-[10px] font-semibold">Auto</span>}
                  </TableCell>
                  <TableCell>
                    <button onClick={() => openView(b)} className="hover:underline">{b.vendors?.name ?? "—"}</button>
                  </TableCell>
                  <TableCell>{fmtDate(b.issue_date)}</TableCell>
                  <TableCell>{fmtDate(b.due_date)}</TableCell>
                  <TableCell className="text-right">{money(b.total, cur)}</TableCell>
                  <TableCell className="text-right">{money(b.balance, cur)}</TableCell>
                  <TableCell><StatusBadge status={b.derivedStatus.replace("_", " ")} /></TableCell>
                  <TableCell className="flex gap-1">
                    {b.derivedStatus !== "paid" && b.derivedStatus !== "void" && (
                      <Button size="sm" variant="outline" onClick={() => openPayment(b)}>Record payment</Button>

                    )}
                    {b.derivedStatus !== "void" && (
                      <Button size="icon" variant="ghost" onClick={() => openEdit(b)} title="Edit bill"><Pencil className="h-4 w-4" /></Button>
                    )}
                    <Button size="icon" variant="ghost" onClick={() => remove(b.id)}><Trash2 className="h-4 w-4" /></Button>
                  </TableCell>
                </TableRow>
              ))}
              {!isLoading && filtered.length === 0 && (
                <TableRow className="hover:bg-transparent"><TableCell colSpan={8} className="p-0">
                  <EmptyState icon={Receipt} title="No bills match" description="Record a bill from a vendor to track what you owe." />
                </TableCell></TableRow>
              )}
            </TableBody>
          </Table>
        </CardContent>
      </Card>

      {/* Payment dialog */}
      <Dialog open={!!payBill} onOpenChange={(o) => !o && setPayBill(null)}>
        <DialogContent className="max-w-lg">
          <DialogHeader>
            <DialogTitle>Record payment {payBill ? `· Bill ${payBill.number}` : ""}</DialogTitle>
          </DialogHeader>
          {payBill && (
            <div className="space-y-3">
              <div className="text-sm text-muted-foreground">
                Vendor: <b>{payBill.vendors?.name ?? "—"}</b> · Balance:{" "}
                <b>{money(payBill.balance, cur)}</b>
              </div>
              <div className="grid grid-cols-2 gap-3">
                <div>
                  <Label>Payment date</Label>
                  <Input type="date" value={payDate} onChange={(e) => setPayDate(e.target.value)} />
                </div>
                <div>
                  <Label>Amount</Label>
                  <Input type="number" min={0} step="0.01" value={payAmount} onChange={(e) => setPayAmount(Number(e.target.value))} />
                </div>
                <div>
                  <Label>Payment method</Label>
                  <Select value={payMethod} onValueChange={(v) => onMethodChange(v as any)}>
                    <SelectTrigger><SelectValue /></SelectTrigger>
                    <SelectContent>
                      <SelectItem value="check">Check</SelectItem>
                      <SelectItem value="ach">ACH</SelectItem>
                      <SelectItem value="wire">Wire</SelectItem>
                      <SelectItem value="credit_card">Credit Card</SelectItem>
                      <SelectItem value="cash">Cash</SelectItem>
                      <SelectItem value="other">Other</SelectItem>
                    </SelectContent>
                  </Select>
                </div>
                <div>
                  <Label>Payment account</Label>
                  <Select value={payAccountId} onValueChange={setPayAccountId}>
                    <SelectTrigger><SelectValue placeholder="Select 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 className="col-span-2">
                  <Label>{payMethod === "check" ? "Check number" : "Reference"}</Label>
                  <Input value={payReference} onChange={(e) => setPayReference(e.target.value)} maxLength={40} />
                </div>
                <div className="col-span-2">
                  <Label>Memo</Label>
                  <Textarea rows={2} value={payMemo} onChange={(e) => setPayMemo(e.target.value)} maxLength={200} />
                </div>
                {payMethod === "check" && (
                  <div className="col-span-2 flex items-center justify-between rounded-md border p-3 bg-muted/30">
                    <div className="flex items-center gap-2">
                      <Printer className="h-4 w-4" />
                      <div>
                        <div className="text-sm font-medium">Print check after saving</div>
                        <div className="text-xs text-muted-foreground">Opens print dialog & marks as printed</div>
                      </div>
                    </div>
                    <Switch checked={printCheck} onCheckedChange={setPrintCheck} />
                  </div>
                )}
              </div>
            </div>
          )}
          <DialogFooter>
            <Button variant="outline" onClick={() => setPayBill(null)}>Cancel</Button>
            <Button onClick={savePayment} disabled={paying}>
              {paying ? "Saving…" : payMethod === "check" && printCheck ? "Save & Print" : "Save payment"}
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      {/* Bill detail (read-only overview) */}
      <Dialog open={!!viewBill} onOpenChange={(o) => { if (!o) { setViewBill(null); setViewItems([]); } }}>
        <DialogContent className="max-w-3xl max-h-[92vh] overflow-y-auto p-0 gap-0">
          {viewBill && (() => {
            const acctName = (id: string | null) => {
              const a = (expenseAccounts as any[]).find((x) => x.id === id);
              return a ? `${a.code ? a.code + " " : ""}${a.name}` : "—";
            };
            const v = viewBill;
            const canPay = v.derivedStatus !== "paid" && v.derivedStatus !== "void";
            return (
              <>
                {/* Header */}
                <div className="px-6 py-4 border-b">
                  <div className="flex items-start justify-between gap-3 flex-wrap">
                    <div>
                      <div className="flex items-center gap-2">
                        <h2 className="text-xl font-semibold">{v.vendors?.name ?? "Vendor"}</h2>
                        <StatusBadge status={v.derivedStatus.replace("_", " ")} />
                      </div>
                      <p className="text-sm text-muted-foreground mt-0.5">
                        Bill {v.number} · {money(v.total, cur)} · Due {fmtDate(v.due_date)}
                        <button onClick={() => { setViewBill(null); openEdit(v); }} className="ml-2 text-primary hover:underline">Edit</button>
                      </p>
                    </div>
                    <div className="flex items-center gap-2">
                      {canPay && <Button size="sm" onClick={() => { setViewBill(null); openPayment(v); }}>Pay bill</Button>}
                      <Button size="sm" variant="outline" onClick={() => duplicateBill(v)}>Duplicate</Button>
                      <Button size="sm" variant="outline" className="text-destructive hover:text-destructive" onClick={() => { remove(v.id); setViewBill(null); }}>Delete</Button>
                    </div>
                  </div>
                </div>

                <div className="grid grid-cols-1 md:grid-cols-3 gap-4 p-6">
                  {/* Bill details + items */}
                  <div className="md:col-span-2 space-y-4">
                    <Card>
                      <CardHeader className="py-3 bg-muted/40 border-b"><CardTitle className="text-sm">Bill details</CardTitle></CardHeader>
                      <CardContent className="pt-4 grid grid-cols-3 gap-4 text-sm">
                        <Field label="Date" value={fmtDate(v.issue_date)} />
                        <Field label="Due" value={fmtDate(v.due_date)} />
                        <Field label="Pay to" value={v.vendors?.name ?? "—"} />
                        <Field label="Reference number" value={v.number || "—"} />
                        <Field label="Memo" value={v.notes || "—"} />
                        {v.attachment_url && (
                          <div>
                            <div className="text-xs uppercase tracking-wide text-muted-foreground">Attachment</div>
                            <a href={v.attachment_url} target="_blank" rel="noopener noreferrer" className="text-primary hover:underline inline-flex items-center gap-1 mt-1">
                              <FileText className="h-3.5 w-3.5" /> View
                            </a>
                          </div>
                        )}
                      </CardContent>
                    </Card>

                    <Card>
                      <CardHeader className="py-3 bg-muted/40 border-b"><CardTitle className="text-sm">Item details</CardTitle></CardHeader>
                      <CardContent className="p-0">
                        <Table>
                          <TableHeader>
                            <TableRow>
                              <TableHead>Account</TableHead>
                              <TableHead>Description</TableHead>
                              <TableHead className="text-right">Amount</TableHead>
                            </TableRow>
                          </TableHeader>
                          <TableBody>
                            {viewItems.map((it: any) => (
                              <TableRow key={it.id}>
                                <TableCell>{acctName(it.account_id)}</TableCell>
                                <TableCell>{it.description || "—"}</TableCell>
                                <TableCell className="text-right">{money(Number(it.amount), cur)}</TableCell>
                              </TableRow>
                            ))}
                            <TableRow className="font-semibold border-t-2">
                              <TableCell colSpan={2}>Total</TableCell>
                              <TableCell className="text-right">{money(v.total, cur)}</TableCell>
                            </TableRow>
                          </TableBody>
                        </Table>
                      </CardContent>
                    </Card>
                  </div>

                  {/* Amount sidebar */}
                  <div className="space-y-4">
                    <Card>
                      <CardHeader className="py-3 bg-muted/40 border-b"><CardTitle className="text-sm">Bill amount</CardTitle></CardHeader>
                      <CardContent className="pt-4 space-y-2 text-sm">
                        <div className="flex justify-between"><span className="text-muted-foreground">Total</span><span className="font-medium">{money(v.total, cur)}</span></div>
                        <div className="flex justify-between"><span className="text-muted-foreground">Paid</span><span className="font-medium">{money(Number(v.paid_amount ?? 0), cur)}</span></div>
                        <div className="flex justify-between border-t pt-2 text-base"><span>Remaining</span><span className="font-semibold">{money(v.balance, cur)}</span></div>
                      </CardContent>
                    </Card>
                  </div>
                </div>
              </>
            );
          })()}
        </DialogContent>
      </Dialog>
    </div>
  );
}

function Field({ label, value }: { label: string; value: string }) {
  return (
    <div>
      <div className="text-xs uppercase tracking-wide text-muted-foreground">{label}</div>
      <div className="mt-0.5 text-foreground">{value}</div>
    </div>
  );
}
