import { useState, useEffect, useRef, useCallback } from "react";
import { useNavigate } from "react-router-dom";
import { supabase } from "@/integrations/supabase/client";
import { useToast } from "@/hooks/use-toast";
import { UserCheck, Plus, Search, Eye, Upload, X, ArrowUpDown, Edit, Trash2, MoreHorizontal, AlertTriangle, Loader2, Bell, Printer, Sparkles, Download } from "lucide-react";
import { downloadChecksPdf, type CheckData } from "@/utils/checkPdfGenerator";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { Checkbox } from "@/components/ui/checkbox";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Card, CardContent } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription } from "@/components/ui/dialog";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@/components/ui/alert-dialog";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
const statusColors: Record<string, string> = {
  pending: "bg-amber-100 text-amber-700",
  approved: "bg-emerald-100 text-emerald-700",
  rejected: "bg-red-100 text-red-700",
  paid: "bg-emerald-100 text-emerald-700",
  overdue: "bg-red-100 text-red-700",
  draft: "bg-muted text-muted-foreground",
};

type SortField = "id" | "association_id" | "vendor_name" | "amount" | "status" | "created_at" | "bill_date";
type SortDir = "asc" | "desc";

export default function BillApprovalsPage({ boardAssociationIds }: { boardAssociationIds?: string[] } = {}) {
  const { toast } = useToast();
  const isBoardView = !!boardAssociationIds?.length;
  const navigate = useNavigate();
  const [bills, setBills] = useState<any[]>([]);
  const [associations, setAssociations] = useState<any[]>([]);
  const [accounts, setAccounts] = useState<any[]>([]);
  const [vendors, setVendors] = useState<any[]>([]);
  const [boardMembers, setBoardMembers] = useState<any[]>([]);
  const [vendorNotFound, setVendorNotFound] = useState<string | null>(null);
  const [approvalsByBill, setApprovalsByBill] = useState<Record<string, any[]>>({});
  const [loading, setLoading] = useState(true);

  // Filters
  const [search, setSearch] = useState("");
  const [clientFilter, setClientFilter] = useState("all");
  const [statusFilter, setStatusFilter] = useState("all");
  const [glFilter, setGlFilter] = useState("all");
  const [activeTab, setActiveTab] = useState<"pending" | "approved">("pending");
  const monthStart = (() => { const d = new Date(); return new Date(d.getFullYear(), d.getMonth(), 1).toISOString().split("T")[0]; })();
  const monthEnd = (() => { const d = new Date(); return new Date(d.getFullYear(), d.getMonth() + 1, 0).toISOString().split("T")[0]; })();
  const [approvedFrom, setApprovedFrom] = useState<string>(monthStart);
  const [approvedTo, setApprovedTo] = useState<string>(monthEnd);

  // Sorting
  const [sortField, setSortField] = useState<SortField>("created_at");
  const [sortDir, setSortDir] = useState<SortDir>("desc");

  // Dialog
  const [dialogOpen, setDialogOpen] = useState(false);
  const [detailBill, setDetailBill] = useState<any>(null);
  const [detailOpen, setDetailOpen] = useState(false);
  const [editingBill, setEditingBill] = useState<any>(null);
  const [editOpen, setEditOpen] = useState(false);
  const [deleteTarget, setDeleteTarget] = useState<any>(null);

  // Notify Board
  const [notifyOpen, setNotifyOpen] = useState(false);
  const [notifyAssociationId, setNotifyAssociationId] = useState<string>("");
  const [notifyBoardMembers, setNotifyBoardMembers] = useState<any[]>([]);
  const [notifyLoadingMembers, setNotifyLoadingMembers] = useState(false);
  const [notifySending, setNotifySending] = useState(false);

  // Bulk check printing
  const [selectedBillIds, setSelectedBillIds] = useState<Set<string>>(new Set());
  const [printing, setPrinting] = useState(false);
  const [printDialogOpen, setPrintDialogOpen] = useState(false);
  const [printBanks, setPrintBanks] = useState<any[]>([]);
  const [printAssocBankMap, setPrintAssocBankMap] = useState<Record<string, string>>({});
  const [printIncludeMicr, setPrintIncludeMicr] = useState(false);
  const [combinePerVendor, setCombinePerVendor] = useState(true);
  const [printLoadingBanks, setPrintLoadingBanks] = useState(false);


  // Form
  const [form, setForm] = useState({
    association_id: "", expense_account_id: "", approval_member_ids: [] as string[],
    vendor_id: "", vendor_name: "", invoice_number: "", bill_date: new Date().toISOString().split("T")[0],
    due_date: new Date(Date.now() + 30 * 86400000).toISOString().split("T")[0],
    amount: "", description: "",
  });
  const [uploadFile, setUploadFile] = useState<File | null>(null);
  const [submitting, setSubmitting] = useState(false);
  const fileRef = useRef<HTMLInputElement>(null);

  const normalizeApprovalRecord = (record: any) => {
    const linkedInvoice = record.source_invoice_id ? record.invoices : null;
    return {
      ...record,
      amount: linkedInvoice?.amount ?? record.amount,
      due_date: linkedInvoice?.due_date ?? record.due_date,
      bill_date: linkedInvoice?.issue_date ?? record.bill_date,
      invoice_number: linkedInvoice?.invoice_number ?? record.invoice_number,
      description: linkedInvoice?.description ?? record.description,
      attachment_url: linkedInvoice?.raw_pdf_url ?? record.attachment_url,
      notes: linkedInvoice?.vendor_name ?? record.notes,
    };
  };

  // Buildium import state
  const [buildiumOpen, setBuildiumOpen] = useState(false);
  const [buildiumAssoc, setBuildiumAssoc] = useState<string>("all");
  const [buildiumFrom, setBuildiumFrom] = useState<string>("");
  const [buildiumTo, setBuildiumTo] = useState<string>("");
  const [buildiumImporting, setBuildiumImporting] = useState(false);

  const importFromBuildium = async () => {
    setBuildiumImporting(true);
    try {
      const { data, error } = await supabase.functions.invoke("buildium-sync", {
        body: {
          syncType: "bills",
          selectedAssociationIds: buildiumAssoc === "all" ? [] : [buildiumAssoc],
          dateFrom: buildiumFrom || undefined,
          dateTo: buildiumTo || undefined,
        },
      });
      if (error) throw error;
      const r = data?.results?.bills || {};
      toast({
        title: "Buildium import complete",
        description: `Fetched ${r.fetched || 0} • Created ${r.created || 0} • Updated ${r.updated || 0} • Linked duplicates ${r.linked_duplicates || 0} • Vendors created ${r.vendors_created || 0}`,
      });
      setBuildiumOpen(false);
      fetchData();
    } catch (err: any) {
      toast({ variant: "destructive", title: "Import failed", description: err?.message || String(err) });
    } finally {
      setBuildiumImporting(false);
    }
  };

  const fetchData = useCallback(async () => {
    setLoading(true);

    let assignedBillIds: string[] | null = null;
    if (isBoardView) {
      // Restrict to bills where the current user is an assigned approver
      const { data: userData } = await supabase.auth.getUser();
      const uid = userData?.user?.id;
      assignedBillIds = [];
      if (uid) {
        const { data: myMemberships } = await supabase
          .from("board_members")
          .select("member_name, association_id")
          .eq("user_id", uid)
          .in("association_id", boardAssociationIds!);
        const names = Array.from(new Set((myMemberships || []).map((m: any) => m.member_name).filter(Boolean)));
        if (names.length > 0) {
          const { data: myApprovals } = await supabase
            .from("bill_approvals")
            .select("bill_id")
            .in("association_id", boardAssociationIds!)
            .in("vendor_name", names)
            .not("bill_id", "is", null);
          assignedBillIds = Array.from(new Set((myApprovals || []).map((a: any) => a.bill_id).filter(Boolean)));
        }
      }
      if (assignedBillIds.length === 0) {
        setBills([]);
        setApprovalsByBill({});
        const [aRes2, coaRes2, vRes2] = await Promise.all([
          supabase.from("associations").select("id, name").eq("status", "active").order("name"),
          supabase.from("chart_of_accounts").select("id, account_name, account_number, account_type").eq("account_type", "expense").order("account_number"),
          supabase.from("vendors").select("id, name, address, association_id, association_ids").eq("is_active", true).order("name"),
        ]);
        setAssociations(aRes2.data || []);
        setAccounts(coaRes2.data || []);
        setVendors(vRes2.data || []);
        setLoading(false);
        return;
      }
    }

    // Paginate bills (Supabase caps default queries at 1,000 rows)
    const PAGE = 1000;
    const allBills: any[] = [];
    for (let from = 0; ; from += PAGE) {
      let q = supabase
        .from("bills")
        .select("*, associations(name), chart_of_accounts(account_name, account_number), invoices:source_invoice_id(id, vendor_name, invoice_number, issue_date, due_date, amount, description, raw_pdf_url)")
        .order("created_at", { ascending: false });
      if (isBoardView) {
        q = q.in("association_id", boardAssociationIds!).in("id", assignedBillIds!);
      }
      const { data, error } = await q.range(from, from + PAGE - 1);
      if (error || !data) break;
      allBills.push(...data);
      if (data.length < PAGE) break;
    }
    const [aRes, coaRes, vRes] = await Promise.all([
      supabase.from("associations").select("id, name").eq("status", "active").order("name"),
      supabase.from("chart_of_accounts").select("id, account_name, account_number, account_type").eq("account_type", "expense").order("account_number"),
      supabase.from("vendors").select("id, name, address, association_id, association_ids").eq("is_active", true).order("name"),
    ]);

    const billsList = allBills.map(normalizeApprovalRecord);
    setBills(billsList);
    setAssociations(aRes.data || []);
    setAccounts(coaRes.data || []);
    setVendors(vRes.data || []);

    // Fetch approvals for all bills
    if (billsList.length > 0) {
      const billIds = billsList.map((b: any) => b.id);
      const { data: approvals } = await supabase
        .from("bill_approvals")
        .select("id, bill_id, vendor_name, status")
        .in("bill_id", billIds);
      const grouped: Record<string, any[]> = {};
      (approvals || []).forEach((a: any) => {
        if (!grouped[a.bill_id]) grouped[a.bill_id] = [];
        grouped[a.bill_id].push(a);
      });
      setApprovalsByBill(grouped);
    } else {
      setApprovalsByBill({});
    }

    setLoading(false);
  }, [isBoardView, boardAssociationIds]);

  useEffect(() => { fetchData(); }, [fetchData]);

  // Fetch board members when client changes
  useEffect(() => {
    if (!form.association_id) { setBoardMembers([]); return; }
    supabase
      .from("board_members")
      .select("id, member_name, member_email, approval_authority, user_id")
      .eq("association_id", form.association_id)
      .eq("approval_authority", true)
      .order("member_name")
      .then(({ data }) => setBoardMembers(data || []));
  }, [form.association_id]);

  // Fetch board members for the Notify Board dialog
  useEffect(() => {
    if (!notifyAssociationId) { setNotifyBoardMembers([]); return; }
    setNotifyLoadingMembers(true);
    supabase
      .from("board_members")
      .select("id, member_name, member_email, approval_authority, user_id")
      .eq("association_id", notifyAssociationId)
      .eq("approval_authority", true)
      .order("member_name")
      .then(({ data }) => {
        setNotifyBoardMembers(data || []);
        setNotifyLoadingMembers(false);
      });
  }, [notifyAssociationId]);

  const openNotifyBoard = () => {
    setNotifyAssociationId(associations[0]?.id || "");
    setNotifyOpen(true);
  };

  const handleSendNotifyBoard = async () => {
    if (!notifyAssociationId) {
      toast({ variant: "destructive", title: "Select a client", description: "Please choose an association to notify." });
      return;
    }
    if (notifyBoardMembers.length === 0) {
      toast({ variant: "destructive", title: "No board members", description: "No board members with approval authority found for this association." });
      return;
    }
    setNotifySending(true);
    try {
      const association = associations.find((a: any) => a.id === notifyAssociationId);
      const associationName = association?.name || "your association";
      const link = "https://avria.cloud/dashboard/bill-approvals";

      // In-app notifications for board members with linked user accounts
      const inAppPromises = notifyBoardMembers
        .filter((bm: any) => bm.user_id)
        .map((bm: any) =>
          supabase.rpc("insert_notification", {
            p_user_id: bm.user_id,
            p_type: "bill_approval",
            p_title: "Bills Awaiting Your Approval",
            p_message: `One or more bills have been uploaded for ${associationName} and require your approval.`,
            p_related_item_type: "bill",
            p_link: "/homeowner/board/bill-approvals",
          })
        );

      // Email notifications via transactional email
      const stamp = Date.now();
      const emailPromises = notifyBoardMembers
        .filter((bm: any) => bm.member_email)
        .map((bm: any) =>
          supabase.functions.invoke("send-transactional-email", {
            body: {
              templateName: "bill-approval-request",
              recipientEmail: bm.member_email,
              idempotencyKey: `bill-approval-notify-${notifyAssociationId}-${bm.id}-${stamp}`,
              templateData: {
                recipientName: bm.member_name,
                associationName,
                link,
              },
            },
          })
        );

      const results = await Promise.allSettled([...inAppPromises, ...emailPromises]);
      const failed = results.filter((r) => r.status === "rejected").length;
      const emailCount = emailPromises.length;
      const inAppCount = inAppPromises.length;

      // Also send per-bill secure approve/deny links (one email per pending bill per board member).
      let voteSent = 0, voteFailed = 0;
      try {
        const { data: voteRes, error: voteErr } = await supabase.functions.invoke(
          "send-bill-approval-invites",
          { body: { association_id: notifyAssociationId, base_url: window.location.origin } }
        );
        if (voteErr) {
          console.warn("send-bill-approval-invites error:", voteErr);
        } else {
          voteSent = voteRes?.sent ?? 0;
          voteFailed = voteRes?.failed ?? 0;
        }
      } catch (e) {
        console.warn("send-bill-approval-invites threw:", e);
      }

      if (failed > 0) {
        toast({
          variant: "destructive",
          title: "Some notifications failed",
          description: `${failed} of ${results.length} notifications could not be sent.`,
        });
      } else {
        toast({
          title: "Board Notified",
          description: `Sent ${emailCount} summary email${emailCount !== 1 ? "s" : ""}, ${inAppCount} in-app notification${inAppCount !== 1 ? "s" : ""}, and ${voteSent} per-bill approve/deny email${voteSent !== 1 ? "s" : ""}${voteFailed ? ` (${voteFailed} failed)` : ""}.`,
        });
      }
      setNotifyOpen(false);
    } catch (err: any) {
      toast({ variant: "destructive", title: "Error", description: err.message });
    } finally {
      setNotifySending(false);
    }
  };

  // Filtering
  const filtered = bills.filter((b) => {
    // "All" tab: pending + approved together (everything except paid)
    // "Approved" tab: only paid bills, filtered by date range
    const isPaid = b.status === "paid";
    if (activeTab === "approved") {
      if (!isPaid) return false;
      const ref = b.bill_date || b.created_at;
      if (ref) {
        const refStr = String(ref).slice(0, 10);
        if (approvedFrom && refStr < approvedFrom) return false;
        if (approvedTo && refStr > approvedTo) return false;
      }
    } else {
      if (isPaid) return false;
    }
    if (clientFilter !== "all" && b.association_id !== clientFilter) return false;
    if (statusFilter !== "all" && b.status !== statusFilter) return false;
    if (glFilter !== "all" && b.expense_account_id !== glFilter) return false;
    if (search) {
      const q = search.toLowerCase();
      return (
        (b.vendor_name || b.description || "").toLowerCase().includes(q) ||
        (b.id || "").toLowerCase().includes(q) ||
        (b.invoice_number || "").toLowerCase().includes(q)
      );
    }
    return true;
  });

  // Sorting
  const sorted = [...filtered].sort((a, b) => {
    let aVal = a[sortField];
    let bVal = b[sortField];
    if (sortField === "amount") { aVal = Number(aVal); bVal = Number(bVal); }
    if (sortField === "association_id") { aVal = a.associations?.name || ""; bVal = b.associations?.name || ""; }
    if (aVal < bVal) return sortDir === "asc" ? -1 : 1;
    if (aVal > bVal) return sortDir === "asc" ? 1 : -1;
    return 0;
  });

  const toggleSort = (field: SortField) => {
    if (sortField === field) setSortDir(d => d === "asc" ? "desc" : "asc");
    else { setSortField(field); setSortDir("asc"); }
  };

  const SortHeader = ({ field, children }: { field: SortField; children: React.ReactNode }) => (
    <TableHead className="cursor-pointer select-none" onClick={() => toggleSort(field)}>
      <span className="flex items-center gap-1">{children} <ArrowUpDown className="h-3 w-3 text-muted-foreground" /></span>
    </TableHead>
  );

  // Create bill
  const handleCreate = async () => {
    if (!form.association_id || !form.vendor_id || !form.amount) {
      toast({ variant: "destructive", title: "Missing fields", description: "Client, Vendor, and Amount are required." });
      return;
    }
    const selectedVendor = vendors.find((v: any) => v.id === form.vendor_id);
    const vendorDisplayName = selectedVendor?.name || form.vendor_name || "Unknown Vendor";
    setSubmitting(true);
    try {
      let attachmentUrl: string | null = null;
      if (uploadFile) {
        const path = `bill-approvals/${Date.now()}_${uploadFile.name}`;
        const { data: upData } = await supabase.storage.from("files").upload(path, uploadFile);
        if (upData) {
          const { data: { publicUrl } } = supabase.storage.from("files").getPublicUrl(upData.path);
          attachmentUrl = publicUrl;
        }
      }

      const { data: userData } = await supabase.auth.getUser();

      const { data: newBill, error } = await supabase.from("bills").insert({
        association_id: form.association_id,
        vendor_id: form.vendor_id || null,
        description: form.description || `Bill from ${vendorDisplayName}`,
        amount: parseFloat(form.amount) || 0,
        bill_date: form.bill_date,
        due_date: form.due_date || null,
        invoice_number: form.invoice_number || null,
        expense_account_id: form.expense_account_id || null,
        attachment_url: attachmentUrl,
        status: "pending",
        created_by: userData?.user?.id || null,
        notes: vendorDisplayName,
      }).select("id").single();

      if (error) throw error;

      // If board members were selected for approval, create approval requests for each
      if (form.approval_member_ids.length > 0) {
        const approvalRows = form.approval_member_ids.map((memberId) => {
          const bm = boardMembers.find((m: any) => m.id === memberId);
          return {
            association_id: form.association_id,
            bill_id: newBill?.id || null,
            vendor_name: bm?.member_name || vendorDisplayName,
            amount: parseFloat(form.amount) || 0,
            status: "pending",
            notes: form.description || null,
            created_by: userData?.user?.id || null,
          };
        });
        await supabase.from("bill_approvals").insert(approvalRows);

        // Send in-app notifications to selected board members
        const notifyPromises = form.approval_member_ids
          .map((memberId) => boardMembers.find((m: any) => m.id === memberId))
          .filter((bm: any) => bm?.user_id)
          .map((bm: any) =>
            supabase.rpc("insert_notification", {
              p_user_id: bm.user_id,
              p_type: "bill_approval",
              p_title: "New Bill Awaiting Your Approval",
              p_message: `A bill for $${parseFloat(form.amount).toFixed(2)} from ${vendorDisplayName} requires your approval.`,
              p_related_item_type: "bill",
              p_link: "/homeowner/board/bill-approvals",
            })
          );
        await Promise.allSettled(notifyPromises);
      }

      toast({ title: "Bill Created", description: "Bill has been submitted for approval." });
      setDialogOpen(false);
      setUploadFile(null);
      fetchData();
    } catch (err: any) {
      toast({ variant: "destructive", title: "Error", description: err.message });
    } finally {
      setSubmitting(false);
    }
  };

  const openNew = () => {
    setForm({
      association_id: associations[0]?.id || "", expense_account_id: "", approval_member_ids: [],
      vendor_id: "", vendor_name: "", invoice_number: "", bill_date: new Date().toISOString().split("T")[0],
      due_date: new Date(Date.now() + 30 * 86400000).toISOString().split("T")[0],
      amount: "", description: "",
    });
    setVendorNotFound(null);
    setUploadFile(null);
    setDialogOpen(true);
  };

  const openEdit = (bill: any) => {
    setEditingBill(bill);
    setForm({
      association_id: bill.association_id || "",
      expense_account_id: bill.expense_account_id || "",
      approval_member_ids: [],
      vendor_id: bill.vendor_id || "",
      vendor_name: bill.notes || "",
      invoice_number: bill.invoice_number || "",
      bill_date: bill.bill_date || new Date().toISOString().split("T")[0],
      due_date: bill.due_date || "",
      amount: bill.amount?.toString() || "",
      description: bill.description || "",
    });
    setVendorNotFound(null);
    setUploadFile(null);
    setEditOpen(true);
  };

  const handleUpdate = async () => {
    if (!editingBill) return;
    if (!form.association_id || !form.vendor_id || !form.amount) {
      toast({ variant: "destructive", title: "Missing fields", description: "Client, Vendor, and Amount are required." });
      return;
    }
    const selectedVendor = vendors.find((v: any) => v.id === form.vendor_id);
    const vendorDisplayName = selectedVendor?.name || form.vendor_name || "Unknown Vendor";

    setSubmitting(true);
    try {
      let attachmentUrl: string | null = editingBill.attachment_url || null;

      if (uploadFile) {
        const path = `bill-approvals/${Date.now()}_${uploadFile.name}`;
        const { data: upData } = await supabase.storage.from("files").upload(path, uploadFile);
        if (upData) {
          const { data: { publicUrl } } = supabase.storage.from("files").getPublicUrl(upData.path);
          attachmentUrl = publicUrl;
        }
      }

      const { error } = await supabase.from("bills").update({
        association_id: form.association_id,
        vendor_id: form.vendor_id || null,
        description: form.description || `Bill from ${vendorDisplayName}`,
        amount: parseFloat(form.amount) || 0,
        bill_date: form.bill_date,
        due_date: form.due_date || null,
        invoice_number: form.invoice_number || null,
        expense_account_id: form.expense_account_id || null,
        notes: vendorDisplayName,
        attachment_url: attachmentUrl,
      }).eq("id", editingBill.id);

      if (error) throw error;

      // Also update linked invoice's raw_pdf_url if a new file was uploaded
      if (uploadFile && attachmentUrl && editingBill.source_invoice_id) {
        await supabase.from("invoices").update({ raw_pdf_url: attachmentUrl }).eq("id", editingBill.source_invoice_id);
      }

      toast({ title: "Bill Updated", description: "Bill changes have been saved." });
      setEditOpen(false);
      setEditingBill(null);
      setUploadFile(null);
      fetchData();
    } catch (err: any) {
      toast({ variant: "destructive", title: "Error", description: err.message });
    } finally {
      setSubmitting(false);
    }
  };

  const handleDelete = async () => {
    if (!deleteTarget) return;
    try {
      await supabase.from("bill_approvals").delete().eq("bill_id", deleteTarget.id);
      const { error } = await supabase.from("bills").delete().eq("id", deleteTarget.id);
      if (error) throw error;
      toast({ title: "Bill Deleted", description: "Bill has been removed." });
      setDeleteTarget(null);
      fetchData();
    } catch (err: any) {
      toast({ variant: "destructive", title: "Error", description: err.message });
      setDeleteTarget(null);
    }
  };

  const getVendorName = (bill: any) => bill.notes || bill.description || "—";
  const getClientName = (bill: any) => bill.associations?.name || "—";
  const getGlLabel = (bill: any) => bill.chart_of_accounts ? `${bill.chart_of_accounts.account_number} - ${bill.chart_of_accounts.account_name}` : "—";

  const getStatusDisplay = (bill: any) => {
    // Check overdue
    if (bill.status === "pending" && bill.due_date && new Date(bill.due_date) < new Date()) {
      return "overdue";
    }
    return bill.status;
  };

  const filteredAccounts = form.association_id
    ? accounts.filter((a: any) => !a.association_id || a.association_id === form.association_id)
    : [];

  const filteredVendors = form.association_id
    ? vendors.filter((v: any) => v.association_id === form.association_id || (Array.isArray(v.association_ids) && v.association_ids.includes(form.association_id)))
    : [];

  // Open the print dialog: load active bank accounts for the selected bills' associations
  const openPrintDialog = async () => {
    const selectedBills = bills.filter((b) => selectedBillIds.has(b.id));
    if (selectedBills.length === 0) {
      toast({ variant: "destructive", title: "No bills selected", description: "Select one or more bills to print checks for." });
      return;
    }
    const assocIds = Array.from(new Set(selectedBills.map((b) => b.association_id).filter(Boolean)));
    if (assocIds.length === 0) {
      toast({ variant: "destructive", title: "Missing association", description: "Selected bills have no association." });
      return;
    }

    setPrintLoadingBanks(true);
    setPrintDialogOpen(true);
    try {
      const { data: banks, error } = await supabase
        .from("bank_accounts")
        .select("id, account_name, bank_name, routing_number, account_number, next_check_number, association_id")
        .in("association_id", assocIds)
        .eq("status", "active")
        .order("account_name");
      if (error) throw error;
      setPrintBanks(banks || []);

      // Default each association to its first bank
      const defaultMap: Record<string, string> = {};
      for (const id of assocIds) {
        const first = (banks || []).find((b: any) => b.association_id === id);
        if (first) defaultMap[id] = first.id;
      }
      setPrintAssocBankMap(defaultMap);
    } catch (err: any) {
      console.error(err);
      toast({ variant: "destructive", title: "Failed to load bank accounts", description: err.message });
      setPrintDialogOpen(false);
    } finally {
      setPrintLoadingBanks(false);
    }
  };

  // Bulk print checks for selected bills, using user-chosen bank accounts; supports reprint
  const handlePrintChecksForSelected = async () => {
    const selectedBills = bills.filter((b) => selectedBillIds.has(b.id));
    if (selectedBills.length === 0) return;

    const byAssoc: Record<string, any[]> = {};
    for (const b of selectedBills) {
      if (!b.association_id) continue;
      (byAssoc[b.association_id] ||= []).push(b);
    }
    const assocIds = Object.keys(byAssoc);

    // Verify a bank account is chosen for every association
    const missing = assocIds.filter((id) => !printAssocBankMap[id]);
    if (missing.length > 0) {
      const names = missing.map((id) => associations.find((a: any) => a.id === id)?.name || id).join(", ");
      toast({ variant: "destructive", title: "Choose a bank account", description: `Select a bank account for: ${names}.` });
      return;
    }

    const banksByAssoc: Record<string, any> = {};
    for (const id of assocIds) {
      const bank = printBanks.find((b: any) => b.id === printAssocBankMap[id]);
      if (bank) banksByAssoc[id] = bank;
    }

    if (printIncludeMicr) {
      const badBank = Object.values(banksByAssoc).find((b: any) => !b.routing_number || !b.account_number);
      if (badBank) {
        toast({ variant: "destructive", title: "Missing routing/account number", description: `Add them on "${badBank.account_name}" before printing MICR.` });
        return;
      }
    }

    setPrinting(true);
    try {
      const { data: userData } = await supabase.auth.getUser();

      // Load per-association check layouts (optional)
      const { data: layoutsData } = await supabase
        .from("check_layouts")
        .select("*")
        .in("association_id", assocIds);
      const layoutsByAssoc: Record<string, any> = {};
      (layoutsData || []).forEach((l: any) => { layoutsByAssoc[l.association_id] = l; });

      let totalPrinted = 0;
      let totalReprinted = 0;

      for (const assocId of assocIds) {
        const bank = banksByAssoc[assocId];
        const groupBills = byAssoc[assocId];
        let nextNum = bank.next_check_number || 1001;

        const checkDataList: CheckData[] = [];
        const billStatusUpdates: Array<{ billId: string; checkId: string; checkNumber: string }> = [];
        const txInserts: any[] = [];
        const today = new Date().toISOString().slice(0, 10);
        const assocName = associations.find((a: any) => a.id === assocId)?.name || "";

        // Group bills by payee (vendor) when combining is enabled.
        // Otherwise treat each bill as its own group.
        const billGroups: Array<{ payeeKey: string; payee: string; bills: any[] }> = [];
        if (combinePerVendor) {
          const map = new Map<string, { payee: string; bills: any[] }>();
          for (const bill of groupBills) {
            const payee = getVendorName(bill);
            const key = (payee || "").trim().toLowerCase();
            if (!map.has(key)) map.set(key, { payee, bills: [] });
            map.get(key)!.bills.push(bill);
          }
          for (const [payeeKey, v] of map) billGroups.push({ payeeKey, payee: v.payee, bills: v.bills });
        } else {
          for (const bill of groupBills) {
            const payee = getVendorName(bill);
            billGroups.push({ payeeKey: bill.id, payee, bills: [bill] });
          }
        }

        for (const grp of billGroups) {
          const payee = grp.payee;
          const firstBill = grp.bills[0];
          const vendorRow: any = firstBill.vendor_id ? vendors.find((v: any) => v.id === firstBill.vendor_id) : null;
          const payeeAddress = vendorRow?.address || null;
          const totalAmount = grp.bills.reduce((s, b) => s + (Number(b.amount) || 0), 0);

          // Build line items for the stub (one row per bill in the group)
          const lineItems = grp.bills.map((b) => ({
            invoice_number: b.invoice_number || null,
            date: b.bill_date || today,
            description: b.description || null,
            amount: Number(b.amount) || 0,
          }));

          const memo = grp.bills.length > 1
            ? `Combined payment (${grp.bills.length} invoice${grp.bills.length === 1 ? "" : "s"})`
            : (firstBill.invoice_number ? `Inv #${firstBill.invoice_number}` : (firstBill.description || null));

          // Identify any already-printed bills in the group so we can reprint
          // under the existing check number when the entire group shares one.
          const existingCheckIds = Array.from(new Set(grp.bills.map((b) => b.check_id).filter(Boolean)));
          let existingCheck: any = null;
          if (existingCheckIds.length === 1) {
            const { data: existing } = await supabase
              .from("checks")
              .select("id, check_number, bank_account_id")
              .eq("id", existingCheckIds[0])
              .maybeSingle();
            existingCheck = existing;
          }

          let checkNumber: string;
          let checkId: string;

          if (existingCheck) {
            // REPRINT: reuse existing check, do not advance counter or insert tx
            checkId = existingCheck.id;
            checkNumber = existingCheck.check_number || String(nextNum++);

            await supabase
              .from("checks")
              .update({
                check_number: checkNumber,
                check_date: today,
                amount: totalAmount,
                payee,
                memo,
                printed: true,
                status: "printed",
                bank_account_id: bank.id,
              })
              .eq("id", existingCheck.id);

            totalReprinted += 1;
          } else {
            // FIRST PRINT: create one check + one bank transaction for the whole group
            checkNumber = String(nextNum++);
            const { data: chk, error: chkErr } = await supabase
              .from("checks")
              .insert({
                association_id: assocId,
                bank_account_id: bank.id,
                payee,
                amount: totalAmount,
                check_number: checkNumber,
                check_date: today,
                memo,
                status: "printed",
                printed: true,
                created_by: userData?.user?.id || null,
              })
              .select("id")
              .single();
            if (chkErr) throw chkErr;
            checkId = chk.id;

            for (const b of grp.bills) {
              billStatusUpdates.push({ billId: b.id, checkId, checkNumber });
            }

            txInserts.push({
              association_id: assocId,
              bank_account_id: bank.id,
              date: today,
              debit: totalAmount,
              credit: 0,
              description: `Check #${checkNumber} to ${payee}` +
                (grp.bills.length > 1 ? ` (combined ${grp.bills.length} bills)` : (memo ? ` — ${memo}` : "")),
              reference_number: checkNumber,
              related_entity_id: checkId,
              related_entity_type: "check",
              transaction_type: "check",
            });

            totalPrinted += 1;
          }

          // Always make sure every bill in the group is linked to this check
          // (covers the reprint case where some bills may have been added later)
          for (const b of grp.bills) {
            if (b.check_id !== checkId) {
              billStatusUpdates.push({ billId: b.id, checkId, checkNumber });
            }
          }

          checkDataList.push({
            check_number: checkNumber,
            check_date: today,
            payee,
            payee_address: payeeAddress,
            amount: totalAmount,
            memo,
            line_items: lineItems.length > 1 ? lineItems : null,
            bank_account_name: bank.account_name,
            bank_routing_number: bank.routing_number,
            bank_account_number: bank.account_number,
            association_name: assocName,
            layout: (layoutsByAssoc[assocId] as any) || null,
          });
        }

        for (const u of billStatusUpdates) {
          await supabase
            .from("bills")
            .update({
              status: "paid",
              paid_date: today,
              payment_method: "check",
              check_id: u.checkId,
            })
            .eq("id", u.billId);
        }

        if (txInserts.length > 0) await supabase.from("bank_transactions").insert(txInserts);

        if (nextNum > (bank.next_check_number || 1001)) {
          await supabase
            .from("bank_accounts")
            .update({ next_check_number: nextNum })
            .eq("id", bank.id);
        }

        const safeName = (assocName || "association").replace(/[^a-z0-9-_]+/gi, "_");
        await downloadChecksPdf(checkDataList, `checks-${safeName}-${today}.pdf`, { includeMicr: printIncludeMicr });
      }

      const parts: string[] = [];
      if (totalPrinted) parts.push(`${totalPrinted} new`);
      if (totalReprinted) parts.push(`${totalReprinted} reprinted`);
      toast({ title: "Checks generated", description: `${parts.join(", ")} across ${assocIds.length} association(s).` });
      setSelectedBillIds(new Set());
      setPrintDialogOpen(false);
      fetchData();
    } catch (err: any) {
      console.error("Bulk print failed:", err);
      toast({ variant: "destructive", title: "Print failed", description: err.message });
    } finally {
      setPrinting(false);
    }
  };

  // A bill is selectable if it has an association and isn't rejected. Paid bills with a check_id are reprintable.
  const isPrintable = (bill: any) =>
    !!bill.association_id && bill.status !== "rejected";

  return (
    <div className="space-y-6">
      {/* Header */}
      <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
        <div>
          <h1 className="text-2xl font-bold text-foreground flex items-center gap-2">
            <UserCheck className="h-6 w-6 text-primary" /> Bill Approvals List
          </h1>
          <p className="text-sm text-muted-foreground mt-1">Manage and track vendor bills requiring approval.</p>
        </div>
        {!isBoardView && (
          <div className="flex gap-2 flex-wrap">
            <Button onClick={openNotifyBoard} variant="outline" size="sm" className="gap-2">
              <Bell className="w-4 h-4" /> Notify Board
            </Button>
            <Button
              onClick={openPrintDialog}
              disabled={selectedBillIds.size === 0 || printing}
              variant="outline"
              size="sm"
              className="gap-2"
            >
              {printing ? <Loader2 className="w-4 h-4 animate-spin" /> : <Printer className="w-4 h-4" />}
              Print Checks ({selectedBillIds.size})
            </Button>
            <Button variant="outline" className="gap-2" onClick={() => setBuildiumOpen(true)}>
              <Download className="h-4 w-4" /> Import from Buildium
            </Button>
            <Button variant="outline" className="gap-2" onClick={() => navigate("/dashboard/bill-approvals?tab=parser")}>
              <Sparkles className="h-4 w-4" /> Submit Invoice
            </Button>
            <Button className="gap-2" onClick={openNew}>
              <Plus className="h-4 w-4" /> Create Bill
            </Button>
          </div>
        )}
      </div>

      {/* Tabs: Pending vs Approved */}
      <Tabs value={activeTab} onValueChange={(v) => setActiveTab(v as "pending" | "approved")}>
        <TabsList>
          <TabsTrigger value="pending">Pending & Approved</TabsTrigger>
          <TabsTrigger value="approved">Paid</TabsTrigger>
        </TabsList>
      </Tabs>

      {activeTab === "approved" && (
        <div className="flex flex-wrap items-end gap-3">
          <div className="flex flex-col gap-1">
            <Label className="text-xs">From</Label>
            <Input type="date" value={approvedFrom} onChange={(e) => setApprovedFrom(e.target.value)} className="w-[160px]" />
          </div>
          <div className="flex flex-col gap-1">
            <Label className="text-xs">To</Label>
            <Input type="date" value={approvedTo} onChange={(e) => setApprovedTo(e.target.value)} className="w-[160px]" />
          </div>
          <div className="flex gap-2">
            <Button variant="outline" size="sm" onClick={() => { setApprovedFrom(monthStart); setApprovedTo(monthEnd); }}>This Month</Button>
            <Button variant="outline" size="sm" onClick={() => {
              const d = new Date(); const s = new Date(d.getFullYear(), d.getMonth() - 1, 1);
              const e = new Date(d.getFullYear(), d.getMonth(), 0);
              setApprovedFrom(s.toISOString().split("T")[0]); setApprovedTo(e.toISOString().split("T")[0]);
            }}>Last Month</Button>
            <Button variant="outline" size="sm" onClick={() => {
              const d = new Date(); setApprovedFrom(new Date(d.getFullYear(), 0, 1).toISOString().split("T")[0]);
              setApprovedTo(new Date(d.getFullYear(), 11, 31).toISOString().split("T")[0]);
            }}>YTD</Button>
            <Button variant="ghost" size="sm" onClick={() => { setApprovedFrom(""); setApprovedTo(""); }}>All Time</Button>
          </div>
        </div>
      )}

      {/* Search & Filters */}
      <div className="flex flex-col sm:flex-row gap-3 items-start sm:items-center">
        <div className="relative flex-1 max-w-md">
          <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
          <Input
            placeholder="Search by Vendor Name or Bill ID..."
            value={search}
            onChange={(e) => setSearch(e.target.value)}
            className="pl-9"
          />
        </div>
        <div className="flex gap-2 ml-auto">
          <Select value={clientFilter} onValueChange={setClientFilter}>
            <SelectTrigger className="w-[160px]"><SelectValue placeholder="All Clients" /></SelectTrigger>
            <SelectContent>
              <SelectItem value="all">All Clients</SelectItem>
              {associations.map((a: any) => <SelectItem key={a.id} value={a.id}>{a.name}</SelectItem>)}
            </SelectContent>
          </Select>
          <Select value={statusFilter} onValueChange={setStatusFilter}>
            <SelectTrigger className="w-[150px]"><SelectValue placeholder="All Statuses" /></SelectTrigger>
            <SelectContent>
              <SelectItem value="all">All Statuses</SelectItem>
              <SelectItem value="pending">Pending</SelectItem>
              <SelectItem value="approved">Approved</SelectItem>
              <SelectItem value="rejected">Rejected</SelectItem>
              <SelectItem value="paid">Paid</SelectItem>
              <SelectItem value="draft">Draft</SelectItem>
            </SelectContent>
          </Select>
          <Select value={glFilter} onValueChange={setGlFilter}>
            <SelectTrigger className="w-[170px]"><SelectValue placeholder="All GL Accounts" /></SelectTrigger>
            <SelectContent>
              <SelectItem value="all">All GL Accounts</SelectItem>
              {accounts.map((a: any) => <SelectItem key={a.id} value={a.id}>{a.account_number} - {a.account_name}</SelectItem>)}
            </SelectContent>
          </Select>
        </div>
      </div>

      {/* Table */}
      {loading ? (
        <div className="flex justify-center py-12">
          <div className="h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent" />
        </div>
      ) : sorted.length === 0 ? (
        <Card><CardContent className="py-12 text-center text-muted-foreground">No bills found.</CardContent></Card>
      ) : (
        <Card>
          <Table>
            <TableHeader>
              <TableRow>
                {!isBoardView && (
                  <TableHead className="w-10">
                    <Checkbox
                      checked={
                        sorted.filter(isPrintable).length > 0 &&
                        sorted.filter(isPrintable).every((b) => selectedBillIds.has(b.id))
                      }
                      onCheckedChange={(checked) => {
                        const printable = sorted.filter(isPrintable).map((b) => b.id);
                        setSelectedBillIds(checked ? new Set(printable) : new Set());
                      }}
                      aria-label="Select all printable bills"
                    />
                  </TableHead>
                )}
                <SortHeader field="id">Bill ID</SortHeader>
                <SortHeader field="association_id">Client</SortHeader>
                <TableHead>Vendor</TableHead>
                <SortHeader field="amount">Amount</SortHeader>
                <SortHeader field="bill_date">Bill Date</SortHeader>
                <TableHead>Approvers</TableHead>
                <SortHeader field="status">Status</SortHeader>
                <SortHeader field="created_at">Created Date</SortHeader>
                <TableHead>Actions</TableHead>
              </TableRow>
            </TableHeader>
            <TableBody>
              {sorted.map((b) => {
                const displayStatus = getStatusDisplay(b);
                return (
                   <TableRow key={b.id} className="cursor-pointer hover:bg-muted/50" onClick={() => navigate(isBoardView ? `/homeowner/board/bill-approvals/${b.id}` : `/dashboard/bill-approvals/${b.id}`)}>
                    {!isBoardView && (
                      <TableCell onClick={(e) => e.stopPropagation()}>
                        <Checkbox
                          checked={selectedBillIds.has(b.id)}
                          disabled={!isPrintable(b)}
                          onCheckedChange={(checked) => {
                            setSelectedBillIds((prev) => {
                              const s = new Set(prev);
                              if (checked) s.add(b.id); else s.delete(b.id);
                              return s;
                            });
                          }}
                          aria-label={`Select bill ${b.id}`}
                        />
                      </TableCell>
                    )}
                    <TableCell className="font-mono text-xs text-muted-foreground">{b.id.slice(0, 8)}...</TableCell>
                    <TableCell>{getClientName(b)}</TableCell>
                    <TableCell className="font-medium">{getVendorName(b)}</TableCell>
                    <TableCell className="font-mono">${Number(b.amount).toLocaleString("en-US", { minimumFractionDigits: 2 })}</TableCell>
                    <TableCell className="text-sm">
                      {b.bill_date ? new Date(b.bill_date + "T12:00:00").toLocaleDateString("en-US", { year: "numeric", month: "short", day: "numeric" }) : "—"}
                    </TableCell>
                    <TableCell className="text-sm">
                      {(approvalsByBill[b.id] || []).length > 0 ? (
                        <div className="space-y-0.5">
                          {approvalsByBill[b.id].map((a: any) => (
                            <div key={a.id} className="flex items-center gap-1.5">
                              <Badge variant="outline" className={`text-xs ${a.status === 'approved' ? 'border-emerald-300 text-emerald-700' : a.status === 'denied' || a.status === 'rejected' ? 'border-red-300 text-red-700' : 'border-amber-300 text-amber-700'}`}>
                                {a.status === 'approved' ? '✓' : a.status === 'denied' || a.status === 'rejected' ? '✗' : '⏳'}
                              </Badge>
                              <span className="text-xs truncate max-w-[120px]">{a.vendor_name}</span>
                            </div>
                          ))}
                        </div>
                      ) : (
                        <span className="text-muted-foreground italic">None</span>
                      )}
                    </TableCell>
                    <TableCell>
                      <Badge className={statusColors[displayStatus] || "bg-muted text-muted-foreground"}>
                        {displayStatus.charAt(0).toUpperCase() + displayStatus.slice(1)}
                      </Badge>
                    </TableCell>
                    <TableCell className="text-sm">
                      {new Date(b.created_at).toLocaleDateString("en-US", { year: "numeric", month: "short", day: "numeric" })}
                    </TableCell>
                    <TableCell onClick={(e) => e.stopPropagation()}>
                      <DropdownMenu>
                        <DropdownMenuTrigger asChild>
                          <Button variant="ghost" size="icon" className="h-8 w-8">
                            <MoreHorizontal className="h-4 w-4" />
                          </Button>
                        </DropdownMenuTrigger>
                        <DropdownMenuContent align="end">
                          <DropdownMenuItem onClick={() => { setDetailBill(b); setDetailOpen(true); }}>
                            <Eye className="h-4 w-4 mr-2" /> View Details
                          </DropdownMenuItem>
                          {!isBoardView && (
                            <>
                              <DropdownMenuItem onClick={() => openEdit(b)}>
                                <Edit className="h-4 w-4 mr-2" /> Edit
                              </DropdownMenuItem>
                              <DropdownMenuItem className="text-destructive" onClick={() => setDeleteTarget(b)}>
                                <Trash2 className="h-4 w-4 mr-2" /> Delete
                              </DropdownMenuItem>
                            </>
                          )}
                        </DropdownMenuContent>
                      </DropdownMenu>
                    </TableCell>
                  </TableRow>
                );
              })}
            </TableBody>
          </Table>
        </Card>
      )}

      {/* Create Bill Dialog */}
      <Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
        <DialogContent className="sm:max-w-lg max-h-[90vh] overflow-y-auto">
          <DialogHeader>
            <DialogTitle>Bill Details</DialogTitle>
          </DialogHeader>
          <div className="space-y-4">
            {/* Client / Association */}
            <div>
              <Label>Client / Association <span className="text-destructive">*</span></Label>
              <Select value={form.association_id} onValueChange={(v) => setForm({ ...form, association_id: v, expense_account_id: "", approval_member_ids: [] })}>
                <SelectTrigger><SelectValue placeholder="Select Client" /></SelectTrigger>
                <SelectContent>
                  {associations.map((a: any) => <SelectItem key={a.id} value={a.id}>{a.name}</SelectItem>)}
                </SelectContent>
              </Select>
            </div>

            {/* GL Account */}
            <div>
              <Label>GL Account (Expense) <span className="text-destructive">*</span></Label>
              <Select
                value={form.expense_account_id}
                onValueChange={(v) => setForm({ ...form, expense_account_id: v })}
                disabled={!form.association_id}
              >
                <SelectTrigger>
                  <SelectValue placeholder={form.association_id ? "Select GL Account" : "Select a client first"} />
                </SelectTrigger>
                <SelectContent>
                  {filteredAccounts.map((a: any) => (
                    <SelectItem key={a.id} value={a.id}>{a.account_number} - {a.account_name}</SelectItem>
                  ))}
                </SelectContent>
              </Select>
            </div>

            {/* Request Approval From */}
            <div>
              <Label>Request Approval From (Optional)</Label>
              <div className="border rounded-md p-3 max-h-[150px] overflow-y-auto bg-muted/30 space-y-2 mt-1">
                {!form.association_id ? (
                  <p className="text-sm text-muted-foreground italic">Select a client first.</p>
                ) : boardMembers.length === 0 ? (
                  <p className="text-sm text-muted-foreground italic">No board members with approval authority found.</p>
                ) : (
                  boardMembers.map((bm: any) => (
                    <div key={bm.id} className="flex items-center space-x-2 bg-background p-2 rounded border">
                      <Checkbox
                        id={`approver-${bm.id}`}
                        checked={form.approval_member_ids.includes(bm.id)}
                        onCheckedChange={(checked) => {
                          setForm(prev => ({
                            ...prev,
                            approval_member_ids: checked
                              ? [...prev.approval_member_ids, bm.id]
                              : prev.approval_member_ids.filter((id: string) => id !== bm.id),
                          }));
                        }}
                      />
                      <Label htmlFor={`approver-${bm.id}`} className="cursor-pointer flex-1">
                        <span className="font-medium text-sm">{bm.member_name}</span>
                        {bm.member_email && <span className="text-xs text-muted-foreground block">{bm.member_email}</span>}
                      </Label>
                    </div>
                  ))
                )}
              </div>
              {form.approval_member_ids.length > 0 && (
                <p className="text-xs text-muted-foreground mt-1">{form.approval_member_ids.length} approver(s) selected</p>
              )}
            </div>

            {/* Vendor Not Found Warning */}
            {vendorNotFound && (
              <Alert variant="destructive" className="border-amber-300 bg-amber-50 text-amber-900">
                <AlertTriangle className="h-4 w-4" />
                <AlertDescription>
                  Vendor "<strong>{vendorNotFound}</strong>" was not recognized. Please{" "}
                  <a href="/dashboard/vendors" target="_blank" rel="noopener noreferrer" className="underline font-semibold text-primary">
                    add this vendor
                  </a>{" "}
                  first, then retry.
                </AlertDescription>
              </Alert>
            )}

            {/* Vendor & Invoice */}
            <div className="grid grid-cols-2 gap-4">
              <div>
                <Label>Vendor <span className="text-destructive">*</span></Label>
                <Select
                  value={form.vendor_id}
                  onValueChange={(v) => {
                    const selected = filteredVendors.find((vn: any) => vn.id === v);
                    setForm({ ...form, vendor_id: v, vendor_name: selected?.name || "" });
                    setVendorNotFound(null);
                  }}
                  disabled={!form.association_id}
                >
                  <SelectTrigger><SelectValue placeholder={form.association_id ? "Select vendor" : "Select a client first"} /></SelectTrigger>
                  <SelectContent>
                    {filteredVendors.map((v: any) => <SelectItem key={v.id} value={v.id}>{v.name}</SelectItem>)}
                  </SelectContent>
                </Select>
              </div>
              <div>
                <Label>Bill / Invoice Number</Label>
                <Input placeholder="Optional" value={form.invoice_number} onChange={(e) => setForm({ ...form, invoice_number: e.target.value })} />
              </div>
            </div>

            {/* Dates */}
            <div className="grid grid-cols-2 gap-4">
              <div>
                <Label>Bill Date <span className="text-destructive">*</span></Label>
                <Input type="date" value={form.bill_date} onChange={(e) => setForm({ ...form, bill_date: e.target.value })} />
              </div>
              <div>
                <Label>Due Date <span className="text-destructive">*</span></Label>
                <Input type="date" value={form.due_date} onChange={(e) => setForm({ ...form, due_date: e.target.value })} />
              </div>
            </div>

            {/* Amount */}
            <div>
              <Label>Amount <span className="text-destructive">*</span></Label>
              <div className="relative">
                <span className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground text-sm">$</span>
                <Input
                  type="number"
                  step="0.01"
                  min="0"
                  placeholder="0.00"
                  className="pl-7"
                  value={form.amount}
                  onChange={(e) => setForm({ ...form, amount: e.target.value })}
                />
              </div>
            </div>

            {/* Description */}
            <div>
              <Label>Description / Memo</Label>
              <Textarea
                placeholder="What was this expense for? (Optional)"
                value={form.description}
                onChange={(e) => setForm({ ...form, description: e.target.value })}
                rows={3}
              />
            </div>

            {/* File Upload */}
            <div>
              <Label>Supporting Document (PDF)</Label>
              <div
                className="mt-1 border-2 border-dashed rounded-lg p-6 text-center cursor-pointer transition-colors hover:bg-muted/50 border-border"
                onClick={() => fileRef.current?.click()}
              >
                <input
                  ref={fileRef}
                  type="file"
                  accept=".pdf,.jpg,.jpeg,.png"
                  className="hidden"
                  onChange={(e) => setUploadFile(e.target.files?.[0] || null)}
                />
                {uploadFile ? (
                  <div className="flex items-center justify-center gap-2">
                    <span className="text-sm font-medium text-foreground">{uploadFile.name}</span>
                    <Button variant="ghost" size="icon" className="h-6 w-6" onClick={(e) => { e.stopPropagation(); setUploadFile(null); }}>
                      <X className="h-3 w-3" />
                    </Button>
                  </div>
                ) : (
                  <>
                    <div className="mx-auto mb-2 bg-primary/10 rounded-full p-3 w-fit">
                      <Upload className="h-5 w-5 text-primary" />
                    </div>
                    <p className="text-sm text-foreground font-medium">Click to upload or drag and drop</p>
                    <p className="text-xs text-muted-foreground mt-1">PDF, JPG, PNG (max 10MB)</p>
                  </>
                )}
              </div>
            </div>
          </div>
          <DialogFooter className="mt-4">
            <Button variant="outline" onClick={() => setDialogOpen(false)} disabled={submitting}>Cancel</Button>
            <Button onClick={handleCreate} disabled={submitting} className="gap-2">
              {submitting ? <div className="h-4 w-4 animate-spin rounded-full border-2 border-primary-foreground border-t-transparent" /> : <Plus className="h-4 w-4" />}
              Create Bill
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      {/* View Details Dialog */}
      <Dialog open={detailOpen} onOpenChange={setDetailOpen}>
        <DialogContent className="sm:max-w-3xl max-h-[90vh] overflow-y-auto">
          <DialogHeader>
            <DialogTitle>Bill Details</DialogTitle>
          </DialogHeader>
          {detailBill && (
            <div className="space-y-4">
              <div className="grid grid-cols-2 gap-4">
                <div><p className="text-xs text-muted-foreground uppercase">Bill ID</p><p className="text-sm font-mono">{detailBill.id.slice(0, 12)}...</p></div>
                <div><p className="text-xs text-muted-foreground uppercase">Status</p><Badge className={statusColors[getStatusDisplay(detailBill)] || ""}>{getStatusDisplay(detailBill)}</Badge></div>
                <div><p className="text-xs text-muted-foreground uppercase">Client</p><p className="text-sm font-medium">{getClientName(detailBill)}</p></div>
                <div><p className="text-xs text-muted-foreground uppercase">Vendor</p><p className="text-sm font-medium">{getVendorName(detailBill)}</p></div>
                <div><p className="text-xs text-muted-foreground uppercase">Amount</p><p className="text-sm font-bold">${Number(detailBill.amount).toLocaleString("en-US", { minimumFractionDigits: 2 })}</p></div>
                <div><p className="text-xs text-muted-foreground uppercase">Invoice #</p><p className="text-sm">{detailBill.invoice_number || "—"}</p></div>
                <div><p className="text-xs text-muted-foreground uppercase">Bill Date</p><p className="text-sm">{detailBill.bill_date}</p></div>
                <div><p className="text-xs text-muted-foreground uppercase">Due Date</p><p className="text-sm">{detailBill.due_date || "—"}</p></div>
                <div><p className="text-xs text-muted-foreground uppercase">GL Account</p><p className="text-sm">{getGlLabel(detailBill)}</p></div>
                <div><p className="text-xs text-muted-foreground uppercase">Created</p><p className="text-sm">{new Date(detailBill.created_at).toLocaleDateString()}</p></div>
              </div>
              {detailBill.description && (
                <div><p className="text-xs text-muted-foreground uppercase">Description</p><p className="text-sm">{detailBill.description}</p></div>
              )}
              {/* Embedded PDF / Attachment Preview */}
              {detailBill.attachment_url && (
                <div className="border rounded-lg overflow-hidden">
                  <div className="flex items-center justify-between px-4 py-2 bg-muted/50 border-b">
                    <p className="text-sm font-medium truncate">Attachment</p>
                    <a href={detailBill.attachment_url} target="_blank" rel="noopener noreferrer">
                      <Button variant="outline" size="sm" className="gap-1 text-xs">
                        <Eye className="h-3 w-3" /> Open
                      </Button>
                    </a>
                  </div>
                  {detailBill.attachment_url.toLowerCase().endsWith(".pdf") ? (
                    <iframe
                      src={`${detailBill.attachment_url}#toolbar=1&navpanes=0`}
                      className="w-full h-[400px] border-0"
                      title="PDF Preview"
                    />
                  ) : (
                    <div className="p-4">
                      <img src={detailBill.attachment_url} alt="Bill attachment" className="w-full rounded-md border" />
                    </div>
                  )}
                </div>
              )}
            </div>
          )}
          <DialogFooter>
            <Button variant="outline" onClick={() => setDetailOpen(false)}>Close</Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      {/* Edit Bill Dialog */}
      <Dialog open={editOpen} onOpenChange={(open) => { setEditOpen(open); if (!open) setEditingBill(null); }}>
        <DialogContent className="sm:max-w-lg max-h-[90vh] overflow-y-auto">
          <DialogHeader>
            <DialogTitle>Edit Bill</DialogTitle>
            <DialogDescription>Update the bill details below.</DialogDescription>
          </DialogHeader>
          <div className="space-y-4">
            <div>
              <Label>Client / Association <span className="text-destructive">*</span></Label>
              <Select value={form.association_id} onValueChange={(v) => setForm({ ...form, association_id: v, expense_account_id: "" })}>
                <SelectTrigger><SelectValue placeholder="Select Client" /></SelectTrigger>
                <SelectContent>
                  {associations.map((a: any) => <SelectItem key={a.id} value={a.id}>{a.name}</SelectItem>)}
                </SelectContent>
              </Select>
            </div>
            <div>
              <Label>GL Account (Expense)</Label>
              <Select value={form.expense_account_id} onValueChange={(v) => setForm({ ...form, expense_account_id: v })} disabled={!form.association_id}>
                <SelectTrigger><SelectValue placeholder="Select GL Account" /></SelectTrigger>
                <SelectContent>
                  {filteredAccounts.map((a: any) => <SelectItem key={a.id} value={a.id}>{a.account_number} - {a.account_name}</SelectItem>)}
                </SelectContent>
              </Select>
            </div>
            {vendorNotFound && (
              <Alert variant="destructive" className="border-amber-300 bg-amber-50 text-amber-900">
                <AlertTriangle className="h-4 w-4" />
                <AlertDescription>
                  Vendor "<strong>{vendorNotFound}</strong>" was not recognized. Please{" "}
                  <a href="/dashboard/vendors" target="_blank" rel="noopener noreferrer" className="underline font-semibold text-primary">
                    add this vendor
                  </a>{" "}
                  first, then retry.
                </AlertDescription>
              </Alert>
            )}
            <div className="grid grid-cols-2 gap-4">
              <div>
                <Label>Vendor <span className="text-destructive">*</span></Label>
                <Select
                  value={form.vendor_id}
                  onValueChange={(v) => {
                    const selected = filteredVendors.find((vn: any) => vn.id === v);
                    setForm({ ...form, vendor_id: v, vendor_name: selected?.name || "" });
                    setVendorNotFound(null);
                  }}
                  disabled={!form.association_id}
                >
                  <SelectTrigger><SelectValue placeholder={form.association_id ? "Select vendor" : "Select a client first"} /></SelectTrigger>
                  <SelectContent>
                    {filteredVendors.map((v: any) => <SelectItem key={v.id} value={v.id}>{v.name}</SelectItem>)}
                  </SelectContent>
                </Select>
              </div>
              <div>
                <Label>Invoice Number</Label>
                <Input value={form.invoice_number} onChange={(e) => setForm({ ...form, invoice_number: e.target.value })} />
              </div>
            </div>
            <div className="grid grid-cols-2 gap-4">
              <div>
                <Label>Bill Date</Label>
                <Input type="date" value={form.bill_date} onChange={(e) => setForm({ ...form, bill_date: e.target.value })} />
              </div>
              <div>
                <Label>Due Date</Label>
                <Input type="date" value={form.due_date} onChange={(e) => setForm({ ...form, due_date: e.target.value })} />
              </div>
            </div>
            <div>
              <Label>Amount <span className="text-destructive">*</span></Label>
              <Input type="number" step="0.01" value={form.amount} onChange={(e) => setForm({ ...form, amount: e.target.value })} />
            </div>
            <div>
              <Label>Description</Label>
              <Textarea value={form.description} onChange={(e) => setForm({ ...form, description: e.target.value })} />
            </div>

            {/* Attachment */}
            <div>
              <Label>Attachment (PDF / Image)</Label>
              {editingBill?.attachment_url && !uploadFile && (
                <div className="mt-1 mb-2 flex items-center gap-2 text-sm">
                  <a href={editingBill.attachment_url} target="_blank" rel="noopener noreferrer" className="text-primary hover:underline truncate max-w-[300px]">
                    {decodeURIComponent(editingBill.attachment_url.split("/").pop() || "attachment")}
                  </a>
                  <Badge variant="outline" className="text-xs">Current</Badge>
                </div>
              )}
              <div
                className="mt-1 border-2 border-dashed rounded-lg p-4 text-center cursor-pointer transition-colors hover:bg-muted/50 border-border"
                onClick={() => fileRef.current?.click()}
              >
                <input
                  ref={fileRef}
                  type="file"
                  accept=".pdf,.jpg,.jpeg,.png"
                  className="hidden"
                  onChange={(e) => setUploadFile(e.target.files?.[0] || null)}
                />
                {uploadFile ? (
                  <div className="flex items-center justify-center gap-2">
                    <span className="text-sm font-medium text-foreground">{uploadFile.name}</span>
                    <Button variant="ghost" size="icon" className="h-6 w-6" onClick={(e) => { e.stopPropagation(); setUploadFile(null); }}>
                      <X className="h-3 w-3" />
                    </Button>
                  </div>
                ) : (
                  <>
                    <Upload className="h-4 w-4 mx-auto mb-1 text-muted-foreground" />
                    <p className="text-xs text-muted-foreground">{editingBill?.attachment_url ? "Upload a replacement file" : "Upload an attachment"}</p>
                  </>
                )}
              </div>
            </div>
          </div>
          <DialogFooter>
            <Button variant="outline" onClick={() => setEditOpen(false)} disabled={submitting}>Cancel</Button>
            <Button onClick={handleUpdate} disabled={submitting}>
              {submitting ? "Saving..." : "Save Changes"}
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      {/* Delete Confirmation */}
      <AlertDialog open={!!deleteTarget} onOpenChange={(open) => { if (!open) setDeleteTarget(null); }}>
        <AlertDialogContent>
          <AlertDialogHeader>
            <AlertDialogTitle>Delete Bill</AlertDialogTitle>
            <AlertDialogDescription>
              Are you sure you want to delete this bill? This action cannot be undone.
            </AlertDialogDescription>
          </AlertDialogHeader>
          <AlertDialogFooter>
            <AlertDialogCancel>Cancel</AlertDialogCancel>
            <AlertDialogAction onClick={handleDelete} className="bg-destructive text-destructive-foreground hover:bg-destructive/90">
              Delete
            </AlertDialogAction>
          </AlertDialogFooter>
        </AlertDialogContent>
      </AlertDialog>

      {/* Notify Board Dialog */}
      <Dialog open={notifyOpen} onOpenChange={setNotifyOpen}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle className="flex items-center gap-2"><Bell className="h-4 w-4" /> Notify Board</DialogTitle>
            <DialogDescription>
              Send an email and in-app notification to all board members with approval authority for the selected association.
            </DialogDescription>
          </DialogHeader>
          <div className="space-y-4 py-2">
            <div>
              <Label>Association <span className="text-destructive">*</span></Label>
              <Select value={notifyAssociationId} onValueChange={setNotifyAssociationId}>
                <SelectTrigger><SelectValue placeholder="Select association" /></SelectTrigger>
                <SelectContent>
                  {associations.map((a: any) => <SelectItem key={a.id} value={a.id}>{a.name}</SelectItem>)}
                </SelectContent>
              </Select>
            </div>

            <div>
              <Label>Recipients</Label>
              <div className="border rounded-md p-3 max-h-[180px] overflow-y-auto bg-muted/30 space-y-1.5 mt-1 text-sm">
                {!notifyAssociationId ? (
                  <p className="text-muted-foreground italic">Select an association to see recipients.</p>
                ) : notifyLoadingMembers ? (
                  <p className="text-muted-foreground italic">Loading...</p>
                ) : notifyBoardMembers.length === 0 ? (
                  <p className="text-muted-foreground italic">No board members with approval authority found.</p>
                ) : (
                  notifyBoardMembers.map((bm: any) => (
                    <div key={bm.id} className="flex items-center justify-between bg-background p-2 rounded border">
                      <div>
                        <div className="font-medium">{bm.member_name}</div>
                        {bm.member_email && <div className="text-xs text-muted-foreground">{bm.member_email}</div>}
                      </div>
                      <div className="flex gap-1">
                        {bm.member_email && <Badge variant="outline" className="text-xs">Email</Badge>}
                        {bm.user_id && <Badge variant="outline" className="text-xs">In-app</Badge>}
                      </div>
                    </div>
                  ))
                )}
              </div>
            </div>

            <div className="bg-muted/30 border rounded-md p-3 text-sm text-muted-foreground">
              <p className="font-medium text-foreground mb-1">Message preview</p>
              <p className="italic">
                "One or more bills have been uploaded for your approval. Please sign in and navigate to the following link to review: https://avria.cloud/dashboard/bill-approvals"
              </p>
              <p className="mt-2 text-xs">
                Board members will <strong>also receive a separate secure email for each pending bill</strong> with one-click <strong>Approve</strong> and <strong>Deny</strong> buttons — no sign-in required.
              </p>
            </div>
          </div>
          <DialogFooter>
            <Button variant="outline" onClick={() => setNotifyOpen(false)} disabled={notifySending}>Cancel</Button>
            <Button onClick={handleSendNotifyBoard} disabled={notifySending || !notifyAssociationId || notifyBoardMembers.length === 0}>
              {notifySending && <Loader2 className="w-4 h-4 mr-2 animate-spin" />}
              Send Notifications
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      {/* Print Checks Dialog: choose bank account per association + MICR option */}
      <Dialog open={printDialogOpen} onOpenChange={(open) => { if (!printing) setPrintDialogOpen(open); }}>
        <DialogContent className="max-w-2xl">
          <DialogHeader>
            <DialogTitle className="flex items-center gap-2"><Printer className="h-5 w-5 text-primary" /> Print Checks</DialogTitle>
            <DialogDescription>
              Choose which bank account each client's checks should be drawn from. Already-printed bills will be reprinted using their existing check number.
            </DialogDescription>
          </DialogHeader>

          {printLoadingBanks ? (
            <div className="flex justify-center py-8"><Loader2 className="h-6 w-6 animate-spin text-muted-foreground" /></div>
          ) : (
            <div className="space-y-4 max-h-[60vh] overflow-y-auto pr-1">
              {(() => {
                const selectedBills = bills.filter((b) => selectedBillIds.has(b.id));
                const assocIds = Array.from(new Set(selectedBills.map((b) => b.association_id).filter(Boolean)));
                if (assocIds.length === 0) {
                  return <p className="text-sm text-muted-foreground">No bills selected.</p>;
                }
                return assocIds.map((assocId) => {
                  const assocName = associations.find((a: any) => a.id === assocId)?.name || "Unknown";
                  const assocBills = selectedBills.filter((b) => b.association_id === assocId);
                  const banks = printBanks.filter((b: any) => b.association_id === assocId);
                  const reprintCount = assocBills.filter((b) => !!b.check_id).length;
                  const newCount = assocBills.length - reprintCount;
                  const selectedBank = banks.find((b: any) => b.id === printAssocBankMap[assocId]);
                  return (
                    <div key={assocId} className="rounded-lg border p-3 space-y-2 bg-card">
                      <div className="flex items-start justify-between gap-3">
                        <div>
                          <div className="font-medium text-sm">{assocName}</div>
                          <div className="text-xs text-muted-foreground mt-0.5">
                            {assocBills.length} check{assocBills.length === 1 ? "" : "s"}
                            {reprintCount > 0 && (
                              <span className="ml-1">({newCount} new, {reprintCount} reprint{reprintCount === 1 ? "" : "s"})</span>
                            )}
                          </div>
                        </div>
                        {selectedBank?.next_check_number != null && newCount > 0 && (
                          <div className="text-xs text-muted-foreground">
                            Next #: <span className="font-mono">{selectedBank.next_check_number}</span>
                          </div>
                        )}
                      </div>
                      {banks.length === 0 ? (
                        <p className="text-xs text-destructive">No active bank account for this client. Add one first.</p>
                      ) : (
                        <Select
                          value={printAssocBankMap[assocId] || ""}
                          onValueChange={(v) => setPrintAssocBankMap((prev) => ({ ...prev, [assocId]: v }))}
                        >
                          <SelectTrigger><SelectValue placeholder="Select bank account" /></SelectTrigger>
                          <SelectContent>
                            {banks.map((b: any) => (
                              <SelectItem key={b.id} value={b.id}>
                                {b.account_name}{b.bank_name ? ` — ${b.bank_name}` : ""}
                              </SelectItem>
                            ))}
                          </SelectContent>
                        </Select>
                      )}
                    </div>
                  );
                });
              })()}

              <div className="flex items-start gap-3 pt-2 border-t">
                <Checkbox
                  id="combine-vendor"
                  checked={combinePerVendor}
                  onCheckedChange={(v) => setCombinePerVendor(!!v)}
                  className="mt-1"
                />
                <div>
                  <Label htmlFor="combine-vendor" className="cursor-pointer">Combine bills per vendor into one check</Label>
                  <p className="text-xs text-muted-foreground mt-1">
                    Bills for the same vendor (within the same association) are merged into a single check, with each invoice itemized on the stub.
                  </p>
                </div>
              </div>

              <div className="flex items-start gap-3 pt-2 border-t">
                <Checkbox
                  id="print-micr"
                  checked={printIncludeMicr}
                  onCheckedChange={(v) => setPrintIncludeMicr(!!v)}
                  className="mt-1"
                />
                <div>
                  <Label htmlFor="print-micr" className="cursor-pointer">Print on blank check stock (include MICR line)</Label>
                  <p className="text-xs text-muted-foreground mt-1">
                    Requires routing & account numbers on the bank account, and a MICR-capable printer with magnetic ink.
                  </p>
                </div>
              </div>
            </div>
          )}

          <DialogFooter>
            <Button variant="outline" onClick={() => setPrintDialogOpen(false)} disabled={printing}>Cancel</Button>
            <Button onClick={handlePrintChecksForSelected} disabled={printing || printLoadingBanks} className="gap-2">
              {printing ? <Loader2 className="w-4 h-4 animate-spin" /> : <Printer className="w-4 h-4" />}
              {printing ? "Generating…" : `Print ${selectedBillIds.size} Check${selectedBillIds.size === 1 ? "" : "s"}`}
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      {/* Buildium Import Dialog */}
      <Dialog open={buildiumOpen} onOpenChange={setBuildiumOpen}>
        <DialogContent className="max-w-md">
          <DialogHeader>
            <DialogTitle>Import Bills from Buildium</DialogTitle>
            <DialogDescription>
              Pull bills (and missing vendors) from Buildium. Existing bills are matched by Buildium ID, and likely duplicates (same vendor + amount + invoice # or bill date) are linked instead of re-created.
            </DialogDescription>
          </DialogHeader>
          <div className="space-y-4">
            <div>
              <Label>Association</Label>
              <Select value={buildiumAssoc} onValueChange={setBuildiumAssoc}>
                <SelectTrigger><SelectValue /></SelectTrigger>
                <SelectContent>
                  <SelectItem value="all">All Associations</SelectItem>
                  {associations.map((a: any) => <SelectItem key={a.id} value={a.id}>{a.name}</SelectItem>)}
                </SelectContent>
              </Select>
            </div>
            <div className="grid grid-cols-2 gap-4">
              <div>
                <Label>Due Date From</Label>
                <Input type="date" value={buildiumFrom} onChange={(e) => setBuildiumFrom(e.target.value)} />
              </div>
              <div>
                <Label>Due Date To</Label>
                <Input type="date" value={buildiumTo} onChange={(e) => setBuildiumTo(e.target.value)} />
              </div>
            </div>
          </div>
          <DialogFooter>
            <Button variant="outline" onClick={() => setBuildiumOpen(false)} disabled={buildiumImporting}>Cancel</Button>
            <Button onClick={importFromBuildium} disabled={buildiumImporting} className="gap-2">
              {buildiumImporting ? <Loader2 className="h-4 w-4 animate-spin" /> : <Download className="h-4 w-4" />}
              Import
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </div>
  );
}
