import { useState, useEffect, useMemo } from "react";
import { format } from "date-fns";
import { cn } from "@/lib/utils";
import { useNavigate } from "react-router-dom";
import { supabase } from "@/integrations/supabase/client";
import { buildLedgerAccountBreakdown, LedgerBreakdownEntry } from "@/lib/unitLedgerAccountBreakdown";
import { generateOwnerStatementPDF } from "@/lib/generateOwnerStatementPDF";
import { useToast } from "@/hooks/use-toast";
import { useAuth } from "@/contexts/AuthContext";
import { useAllSupabaseUsers } from "@/hooks/useAllSupabaseUsers";
import { DollarSign, Search, Download, Gavel, FileText, Trash2, Upload, MoreHorizontal, CheckCircle, Archive, ExternalLink, ListTodo, Play, CalendarIcon } from "lucide-react";
import { Calendar } from "@/components/ui/calendar";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import BulkApplyWorkflowDialog from "@/components/bulk-workflow/BulkApplyWorkflowDialog";
import BuildiumARImportDialog from "@/components/BuildiumARImportDialog";
import ARAgingSummary from "@/components/outstanding-balances/ARAgingSummary";
import LedgerCSVImportDialog from "@/components/LedgerCSVImportDialog";
import BulkTaskDialog from "@/components/outstanding-balances/BulkTaskDialog";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Badge } from "@/components/ui/badge";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, DropdownMenuSeparator, DropdownMenuLabel, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent } from "@/components/ui/dropdown-menu";
import { toast as sonnerToast } from "sonner";

const DELINQUENCY_STAGES = [
  { value: "current", label: "Current", color: "bg-emerald-100 text-emerald-800 border-emerald-200" },
  { value: "late_notice", label: "Late Notice", color: "bg-yellow-100 text-yellow-800 border-yellow-200" },
  { value: "initial_reminder", label: "Initial Reminder", color: "bg-amber-100 text-amber-800 border-amber-200" },
  { value: "final_reminder", label: "Final Reminder", color: "bg-orange-100 text-orange-800 border-orange-200" },
  { value: "nola", label: "NOLA", color: "bg-red-100 text-red-800 border-red-200" },
  { value: "intent_to_lien", label: "Intent to Lien", color: "bg-red-200 text-red-900 border-red-300" },
  { value: "lien_filed", label: "Lien Filed", color: "bg-purple-100 text-purple-800 border-purple-200" },
  { value: "foreclosure", label: "Foreclosure", color: "bg-purple-200 text-purple-900 border-purple-300" },
  { value: "attorney", label: "With Attorney", color: "bg-slate-200 text-slate-800 border-slate-300" },
  { value: "bankruptcy_hold", label: "Bankruptcy Hold", color: "bg-rose-200 text-rose-900 border-rose-300" },
  { value: "payment_plan", label: "Payment Plan", color: "bg-blue-100 text-blue-800 border-blue-200" },
] as const;

const ARCHIVED_COLLECTION_STATUSES = new Set(["resolved", "closed", "archived"]);

function isActiveCollectionStatus(status?: string | null) {
  const normalized = (status || "").toLowerCase().trim().replace(/\s+/g, "_").replace(/-/g, "_");
  return !ARCHIVED_COLLECTION_STATUSES.has(normalized);
}

interface BalanceRow {
  unitKey: string;
  ownerIds: string[];
  ownerNames: string[];
  unitId: string | null;
  unitNumber: string;
  address: string;
  associationId: string;
  associationName: string;
  balance: number;
  assessments: number;
  lateFees: number;
  interest: number;
  adminFees: number;
  legalFees: number;
  violations: number;
  bankFees: number;
  payments: number;
  totalDue: number;
  stage: string | null;
  agingDays: number;
  oldestUnpaidDate: string | null;
}

export default function OutstandingBalancesPage() {
  const { toast } = useToast();
  const { user } = useAuth();
  const { users } = useAllSupabaseUsers();
  const navigate = useNavigate();
  const [loading, setLoading] = useState(true);
  const [rows, setRows] = useState<BalanceRow[]>([]);
  const [associations, setAssociations] = useState<{ id: string; name: string }[]>([]);
  const [selectedAssociation, setSelectedAssociation] = useState("all");
  const [search, setSearch] = useState("");
  const [selected, setSelected] = useState<Set<string>>(new Set());
  const [movingToCollections, setMovingToCollections] = useState(false);
  const [deleting, setDeleting] = useState(false);
  const [generatingStatements, setGeneratingStatements] = useState(false);
  const [importOpen, setImportOpen] = useState(false);
  const [agingTab, setAgingTab] = useState("all");
  const [filterDateFrom, setFilterDateFrom] = useState<Date | undefined>(undefined);
  const [filterDateTo, setFilterDateTo] = useState<Date | undefined>(undefined);
  const [bulkTaskOpen, setBulkTaskOpen] = useState(false);
  const [bulkWorkflowOpen, setBulkWorkflowOpen] = useState(false);
  const [bulkStageUpdating, setBulkStageUpdating] = useState(false);

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

  const fetchData = async () => {
    setLoading(true);
    try {
      // Fetch all owners (may exceed 1000 default limit)
      const fetchAllOwners = async () => {
        const all: any[] = [];
        let from = 0;
        const pageSize = 1000;
        while (true) {
          const { data, error } = await supabase
            .from("owners")
            .select("id, first_name, last_name, balance, association_id, unit_id, delinquency_stage, units(id, unit_number, address, status), associations(id, name)")
            .eq("status", "active")
            .order("last_name")
            .range(from, from + pageSize - 1);
          if (error) throw error;
          all.push(...(data || []));
          if (!data || data.length < pageSize) break;
          from += pageSize;
        }
        return all;
      };

      // Fetch all ledger entries (may exceed 1000 default limit)
      const fetchAllLedger = async () => {
        const all: any[] = [];
        let from = 0;
        const pageSize = 1000;
        while (true) {
          const { data, error } = await supabase
            .from("owner_ledger_entries")
            .select("owner_id, transaction_type, description, debit, credit, date")
            .range(from, from + pageSize - 1);
          if (error) throw error;
          all.push(...(data || []));
          if (!data || data.length < pageSize) break;
          from += pageSize;
        }
        return all;
      };

      const [allOwners, assocRes, allLedger] = await Promise.all([
        fetchAllOwners(),
        supabase.from("associations").select("id, name").eq("status", "active").order("name"),
        fetchAllLedger(),
      ]);

      setAssociations(assocRes.data || []);

      // Group ledger entries by owner_id
      const ownerEntriesMap = new Map<string, LedgerBreakdownEntry[]>();
      const ownerNetBalance = new Map<string, number>();
      const ownerOldestUnpaidDate = new Map<string, string | null>();

      allLedger.forEach((e: any) => {
        if (!ownerEntriesMap.has(e.owner_id)) {
          ownerEntriesMap.set(e.owner_id, []);
          ownerNetBalance.set(e.owner_id, 0);
        }
        ownerEntriesMap.get(e.owner_id)!.push({
          id: e.owner_id + '-' + (ownerEntriesMap.get(e.owner_id)!.length),
          transaction_type: e.transaction_type,
          debit: e.debit,
          credit: e.credit,
          date: e.date,
          created_at: null,
        });
        // Simple net balance: debit - credit
        ownerNetBalance.set(e.owner_id, (ownerNetBalance.get(e.owner_id) || 0) + (e.debit || 0) - (e.credit || 0));
      });

      // Build waterfall breakdown per owner for A/R categorization
      type BreakdownFields = 'assessments' | 'lateFees' | 'interest' | 'adminFees' | 'legalFees' | 'violations' | 'bankFees';
      const typeToField = (type: string): BreakdownFields => {
        if (type.includes("late")) return "lateFees";
        if (type.includes("interest")) return "interest";
        if (type.includes("admin")) return "adminFees";
        if (type.includes("legal")) return "legalFees";
        if (type.includes("violation")) return "violations";
        if (type.includes("bank")) return "bankFees";
        if (type.includes("special")) return "assessments";
        return "assessments";
      };

      const emptyBreakdown = { assessments: 0, lateFees: 0, interest: 0, adminFees: 0, legalFees: 0, violations: 0, bankFees: 0, payments: 0 };

      const ledgerMap = new Map<string, typeof emptyBreakdown>();
      ownerEntriesMap.forEach((entries, ownerId) => {
        const { rows: breakdownRows, totalPayments } = buildLedgerAccountBreakdown(entries);
        const result = { ...emptyBreakdown };
        result.payments = totalPayments;

        breakdownRows.forEach((row) => {
          const field = typeToField(row.type);
          result[field] += row.openBalance;
        });

        ledgerMap.set(ownerId, result);
      });

      // Compute oldest unpaid charge date per owner using chronological payment application
      ownerEntriesMap.forEach((entries, ownerId) => {
        const net = ownerNetBalance.get(ownerId) || 0;
        if (net <= 0) {
          ownerOldestUnpaidDate.set(ownerId, null);
          return;
        }

        // Sort entries chronologically
        const sorted = [...entries].sort((a, b) => (a.date || "").localeCompare(b.date || ""));
        // Track charges and payments in order, find oldest charge that's still unpaid
        const charges: { date: string; amount: number }[] = [];
        let totalPaid = 0;

        for (const entry of sorted) {
          const debit = Number(entry.debit) || 0;
          const credit = Number(entry.credit) || 0;
          if (debit > 0) charges.push({ date: entry.date || "", amount: debit });
          if (credit > 0) totalPaid += credit;
        }

        // Apply payments to oldest charges first
        let remaining = totalPaid;
        let oldestUnpaid: string | null = null;
        for (const charge of charges) {
          if (remaining >= charge.amount) {
            remaining -= charge.amount;
          } else {
            // This charge is partially or fully unpaid
            oldestUnpaid = charge.date;
            break;
          }
        }
        ownerOldestUnpaidDate.set(ownerId, oldestUnpaid);
      });

      // Group all active owners by unit_id before filtering so unit-level credits
      // from one co-owner can offset charges assigned to another co-owner.
      const unitGroupMap = new Map<string, any[]>();
      allOwners.forEach((o: any) => {
        const key = o.unit_id || `owner-${o.id}`;
        if (!unitGroupMap.has(key)) unitGroupMap.set(key, []);
        unitGroupMap.get(key)!.push(o);
      });

      const mapped: BalanceRow[] = [];
      unitGroupMap.forEach((groupOwners, key) => {
        const first = groupOwners[0];
        const unit = first.units as any;
        const assoc = first.associations as any;

        // Combine waterfall-allocated ledger data for all owners in the unit
        const combinedLedger = { assessments: 0, lateFees: 0, interest: 0, adminFees: 0, legalFees: 0, violations: 0, bankFees: 0, payments: 0 };
        groupOwners.forEach((o: any) => {
          const l = ledgerMap.get(o.id);
          if (l) {
            combinedLedger.assessments += l.assessments;
            combinedLedger.lateFees += l.lateFees;
            combinedLedger.interest += l.interest;
            combinedLedger.adminFees += l.adminFees;
            combinedLedger.legalFees += l.legalFees;
            combinedLedger.violations += l.violations;
            combinedLedger.bankFees += l.bankFees;
            combinedLedger.payments += l.payments;
          }
        });

        // Use ledger truth only — stale `o.balance` columns can falsely show units with credits as outstanding.
        const combinedBalance = groupOwners.reduce((s: number, o: any) => s + (ownerNetBalance.get(o.id) ?? 0), 0);
        const totalDue = combinedLedger.assessments + combinedLedger.lateFees + combinedLedger.interest + combinedLedger.adminFees + combinedLedger.legalFees + combinedLedger.violations + combinedLedger.bankFees;

        // Compute aging days from oldest UNPAID charge date across all owners in the unit
        let oldestDate: string | null = null;
        groupOwners.forEach((o: any) => {
          const d = ownerOldestUnpaidDate.get(o.id);
          if (d && (!oldestDate || d < oldestDate)) {
            oldestDate = d;
          }
        });
        const today = new Date();
        const agingDays = oldestDate ? Math.floor((today.getTime() - new Date(oldestDate).getTime()) / (1000 * 60 * 60 * 24)) : 0;

        // Skip units with zero or credit balance — they don't owe anything (epsilon for floating point)
        if (combinedBalance <= 0.01) return;

        mapped.push({
          unitKey: key,
          ownerIds: groupOwners.map((o: any) => o.id),
          ownerNames: groupOwners.map((o: any) => `${o.last_name}, ${o.first_name}`),
          unitId: first.unit_id,
          unitNumber: unit?.unit_number || "—",
          address: unit?.address || "—",
          associationId: first.association_id,
          associationName: assoc?.name || "Unknown",
          balance: combinedBalance,
          ...combinedLedger,
          totalDue,
          stage: first.delinquency_stage || null,
          agingDays,
          oldestUnpaidDate: oldestDate,
        });
      });

      setRows(mapped);
    } catch (err: any) {
      toast({ variant: "destructive", title: "Error", description: err.message });
    } finally {
      setLoading(false);
    }
  };

  const filtered = useMemo(() => {
    let result = rows;
    if (selectedAssociation !== "all") {
      result = result.filter((r) => r.associationId === selectedAssociation);
    }
    if (search) {
      const q = search.toLowerCase();
      result = result.filter(
        (r) =>
          r.ownerNames.some(n => n.toLowerCase().includes(q)) ||
          r.unitNumber.toLowerCase().includes(q) ||
          r.address.toLowerCase().includes(q)
      );
    }
    // Aging filter
    if (agingTab === "0-30") {
      result = result.filter((r) => r.agingDays >= 0 && r.agingDays <= 30);
    } else if (agingTab === "31-60") {
      result = result.filter((r) => r.agingDays >= 31 && r.agingDays <= 60);
    } else if (agingTab === "61-90") {
      result = result.filter((r) => r.agingDays >= 61 && r.agingDays <= 90);
    } else if (agingTab === "91+") {
      result = result.filter((r) => r.agingDays >= 91);
    }
    // Date range filter on oldest unpaid charge date
    if (filterDateFrom) {
      result = result.filter((r) => r.oldestUnpaidDate && new Date(r.oldestUnpaidDate) >= filterDateFrom);
    }
    if (filterDateTo) {
      result = result.filter((r) => r.oldestUnpaidDate && new Date(r.oldestUnpaidDate) <= filterDateTo);
    }
    return result.sort((a, b) => b.balance - a.balance);
  }, [rows, selectedAssociation, search, agingTab, filterDateFrom, filterDateTo]);

  const totals = useMemo(() => {
    return filtered.reduce(
      (acc, r) => ({
        balance: acc.balance + r.balance,
        assessments: acc.assessments + r.assessments,
        lateFees: acc.lateFees + r.lateFees,
        interest: acc.interest + r.interest,
        adminFees: acc.adminFees + r.adminFees,
        legalFees: acc.legalFees + r.legalFees,
        violations: acc.violations + r.violations,
        bankFees: acc.bankFees + r.bankFees,
        payments: acc.payments + r.payments,
      }),
      { balance: 0, assessments: 0, lateFees: 0, interest: 0, adminFees: 0, legalFees: 0, violations: 0, bankFees: 0, payments: 0 }
    );
  }, [filtered]);

  const fmt = (n: number) => `$${Math.abs(n).toLocaleString(undefined, { minimumFractionDigits: 2 })}`;

  const toggleSelect = (id: string) => {
    setSelected((prev) => {
      const next = new Set(prev);
      if (next.has(id)) next.delete(id);
      else next.add(id);
      return next;
    });
  };

  const toggleSelectAll = () => {
    if (selected.size === filtered.length) {
      setSelected(new Set());
    } else {
      setSelected(new Set(filtered.map((r) => r.unitKey)));
    }
  };

  const handleMoveToCollections = async () => {
    if (selected.size === 0) return;
    setMovingToCollections(true);

    try {
      const selectedRows = filtered.filter((r) => selected.has(r.unitKey));

      // Already grouped by unit — just check for existing collections
      const unitIds = selectedRows.map(r => r.unitId).filter(Boolean) as string[];
      const ownerIds = selectedRows.flatMap(r => r.ownerIds).filter(Boolean);

      let existingCollections: any[] = [];
      // Only query for existing collections if we have IDs to check
      if (unitIds.length > 0 || ownerIds.length > 0) {
        const orClauses: string[] = [];
        if (unitIds.length > 0) orClauses.push(`unit_id.in.(${unitIds.join(",")})`);
        if (ownerIds.length > 0) orClauses.push(`owner_id.in.(${ownerIds.join(",")})`);

        const { data } = await supabase
          .from("collections")
          .select("id, unit_id, owner_id, status")
          .or(orClauses.join(","));
        existingCollections = (data || []).filter((collection: any) => isActiveCollectionStatus(collection.status));
      }

      const existingUnitIds = new Set((existingCollections).filter(c => c.unit_id).map(c => c.unit_id));
      const existingOwnerIds = new Set((existingCollections).filter(c => c.owner_id && !c.unit_id).map(c => c.owner_id));



      const inserts: any[] = [];
      let skipped = 0;

      selectedRows.forEach((row) => {
        const unitId = row.unitId;

        // Skip if a collection already exists for this unit or owner
        if (unitId && existingUnitIds.has(unitId)) { skipped++; return; }
        if (!unitId && existingOwnerIds.has(row.ownerIds[0])) { skipped++; return; }

        inserts.push({
          association_id: row.associationId,
          owner_id: row.ownerIds[0],
          unit_id: unitId,
          amount_owed: row.balance,
          status: "late_notice",
          notes: `Moved from Outstanding Balances. Owners: ${row.ownerNames.join(", ")}. A/R breakdown — Assessments: ${fmt(row.assessments)}, Late Fees: ${fmt(row.lateFees)}, Interest: ${fmt(row.interest)}, Admin Fees: ${fmt(row.adminFees)}, Legal Fees: ${fmt(row.legalFees)}, Violations: ${fmt(row.violations)}, Bank Fees: ${fmt(row.bankFees)}.`,
          created_by: user?.id || null,
        });
      });

      if (inserts.length === 0) {
        sonnerToast.info("All selected accounts are already in collections.");
      } else {
        const { error } = await supabase.from("collections").insert(inserts);
        if (error) throw error;

        // Auto-mark units as delinquent
        const unitIdsToMark = inserts.map((i: any) => i.unit_id).filter(Boolean);
        if (unitIdsToMark.length > 0) {
          await supabase.from("units").update({ status: "delinquent" } as any).in("id", unitIdsToMark);
        }

        const msg = skipped > 0
          ? `${inserts.length} unit(s) moved to collections (${skipped} already existed, skipped)`
          : `${inserts.length} unit(s) moved to collections`;
        sonnerToast.success(msg);
      }
      setSelected(new Set());
    } catch (err: any) {
      sonnerToast.error("Failed to create collection records", { description: err.message });
    } finally {
      setMovingToCollections(false);
    }
  };

  const handleBulkDelete = async () => {
    if (selected.size === 0) return;
    const confirmed = window.confirm(`Delete all ledger entries and reset balances for ${selected.size} selected unit(s)? This cannot be undone.`);
    if (!confirmed) return;

    setDeleting(true);
    try {
      const selectedRows = filtered.filter((r) => selected.has(r.unitKey));
      const allOwnerIds = selectedRows.flatMap((r) => r.ownerIds);

      for (const ownerId of allOwnerIds) {
        // First get ledger entry IDs for this owner
        const { data: entries } = await supabase
          .from("owner_ledger_entries")
          .select("id")
          .eq("owner_id", ownerId);
        
        if (entries && entries.length > 0) {
          const entryIds = entries.map((e: any) => e.id);
          // Delete any deposit batch items referencing these ledger entries
          await supabase.from("deposit_batch_items").delete().in("owner_ledger_entry_id", entryIds);
          // Now delete the ledger entries
          const { error } = await supabase.from("owner_ledger_entries").delete().eq("owner_id", ownerId);
          if (error) throw error;
        }
      }

      for (const ownerId of allOwnerIds) {
        const { error } = await supabase.from("owners").update({ balance: 0 }).eq("id", ownerId);
        if (error) throw error;
      }

      sonnerToast.success(`Deleted ledger entries for ${allOwnerIds.length} owner(s) across ${selected.size} unit(s)`);
      setSelected(new Set());
      fetchData();
    } catch (err: any) {
      sonnerToast.error("Failed to delete", { description: err.message });
    } finally {
      setDeleting(false);
    }
  };

  const handleBulkGenerateStatements = async () => {
    if (selected.size === 0) return;
    setGeneratingStatements(true);
    try {
      const selectedRows = filtered.filter((r) => selected.has(r.unitKey));
      let generated = 0;
      let skipped = 0;

      for (const row of selectedRows) {
        for (const ownerId of row.ownerIds) {
          // Fetch full ledger entries for this owner
          const allEntries: any[] = [];
          let from = 0;
          const pageSize = 1000;
          while (true) {
            const { data, error } = await supabase
              .from("owner_ledger_entries")
              .select("*, units(id, unit_number, address, account_number, status), associations(id, name)")
              .eq("owner_id", ownerId)
              .order("date", { ascending: true })
              .order("created_at", { ascending: true })
              .range(from, from + pageSize - 1);
            if (error) throw error;
            allEntries.push(...(data || []));
            if (!data || data.length < pageSize) break;
            from += pageSize;
          }

          if (allEntries.length === 0) {
            skipped++;
            continue;
          }

          // Build unit info from the first entry or row data
          const firstEntry = allEntries[0];
          const unit = {
            owner_name: row.ownerNames.join(", "),
            unit_id: row.unitNumber,
            property_address: row.address,
            account_number: firstEntry?.units?.account_number || "N/A",
            balance: row.balance,
            clients: { name: row.associationName },
          };

          // Map entries to the format expected by generateOwnerStatementPDF
          const transactions = allEntries.map((e: any) => ({
            transaction_date: e.date,
            created_at: e.created_at,
            description: e.description,
            transaction_type: e.transaction_type,
            debit_credit: parseFloat(e.debit || 0) > 0 ? "debit" : "credit",
            amount: parseFloat(e.debit || 0) > 0 ? e.debit : e.credit,
            status: e.status,
          }));

          await generateOwnerStatementPDF({ unit, transactions });
          generated++;
        }
      }

      const msg = skipped > 0
        ? `Generated ${generated} statement(s), skipped ${skipped} with no entries`
        : `Generated ${generated} statement(s)`;
      sonnerToast.success(msg);
    } catch (err: any) {
      sonnerToast.error("Failed to generate statements", { description: err.message });
    } finally {
      setGeneratingStatements(false);
    }
  };

  const handleGenerateNotice = (row: BalanceRow, formType: string) => {
    const params = new URLSearchParams({
      tab: formType,
      associationId: row.associationId,
      ownerId: row.ownerIds[0] || '',
      ownerName: row.ownerNames.join(", "),
      address: row.address,
      unitNumber: row.unitNumber,
      association: row.associationName,
      balance: row.balance.toFixed(2),
      assessments: row.assessments.toFixed(2),
      lateFees: row.lateFees.toFixed(2),
      interest: row.interest.toFixed(2),
      legalFees: row.legalFees.toFixed(2),
      adminFees: row.adminFees.toFixed(2),
      violations: row.violations.toFixed(2),
      bankFees: row.bankFees.toFixed(2),
      payments: row.payments.toFixed(2),
    });
    navigate(`/dashboard/forms-letters?${params.toString()}`);
  };

  const handleExportPDF = async () => {
    const { exportCollectionsPdf } = await import("@/lib/collectionsPdfExport");
    const stageLabel = (s: string | null) =>
      DELINQUENCY_STAGES.find((x) => x.value === s)?.label || "—";
    exportCollectionsPdf(
      filtered.map((r) => ({
        ownerName: r.ownerNames.join("; "),
        unitNumber: r.unitNumber,
        association: r.associationName,
        balance: r.balance,
        stage: stageLabel(r.stage),
        daysDelinquent: r.agingDays || 0,
        lastNoticeDate: r.oldestUnpaidDate,
        assessments: r.assessments,
        lateFees: r.lateFees,
        interest: r.interest,
        legalFees: r.legalFees,
        adminFees: r.adminFees,
        violations: r.violations,
        bankFees: r.bankFees,
        payments: r.payments,
      })),
      "Delinquent Accounts Report"
    );
  };

  const handleExportCSV = async () => {
    const headers = ["Owner", "Unit", "Address", "Association", "Assessments", "Late Fees", "Interest", "Admin Fees", "Legal Fees", "Violations", "Bank Fees", "Payments", "Balance"];
    const csvRows = [headers.join(",")];
    filtered.forEach((r) => {
      csvRows.push(
        [
          `"${r.ownerNames.join("; ")}"`,
          `"${r.unitNumber}"`,
          `"${r.address}"`,
          `"${r.associationName}"`,
          r.assessments.toFixed(2),
          r.lateFees.toFixed(2),
          r.interest.toFixed(2),
          r.adminFees.toFixed(2),
          r.legalFees.toFixed(2),
          r.violations.toFixed(2),
          r.bankFees.toFixed(2),
          r.payments.toFixed(2),
          r.balance.toFixed(2),
        ].join(",")
      );
    });
    const csv = csvRows.join("\n");
    const { saveCsv } = await import("@/lib/saveFile");
    await saveCsv(csv, `outstanding_balances_${new Date().toISOString().slice(0, 10)}.csv`);
  };

  const handleUpdateStage = async (row: BalanceRow, stage: string) => {
    try {
      for (const ownerId of row.ownerIds) {
        const { error } = await supabase.from("owners").update({ delinquency_stage: stage } as any).eq("id", ownerId);
        if (error) throw error;
      }
      setRows(prev => prev.map(r => r.unitKey === row.unitKey ? { ...r, stage } : r));
      sonnerToast.success(`Stage updated to "${DELINQUENCY_STAGES.find(s => s.value === stage)?.label || stage}"`);
    } catch (err: any) {
      sonnerToast.error("Failed to update stage", { description: err.message });
    }
  };

  const handleDeleteSingle = async (row: BalanceRow) => {
    const confirmed = window.confirm(`Delete all ledger entries and reset balance for ${row.ownerNames.join(", ")}? This cannot be undone.`);
    if (!confirmed) return;
    try {
      for (const ownerId of row.ownerIds) {
        await supabase.from("owner_ledger_entries").delete().eq("owner_id", ownerId);
        await supabase.from("owners").update({ balance: 0, delinquency_stage: null } as any).eq("id", ownerId);
      }
      sonnerToast.success("Account deleted and balance reset");
      fetchData();
    } catch (err: any) {
      sonnerToast.error("Failed to delete", { description: err.message });
    }
  };

  const handleMarkResolved = async (row: BalanceRow) => {
    try {
      for (const ownerId of row.ownerIds) {
        await supabase.from("owners").update({ balance: 0, delinquency_stage: "resolved" } as any).eq("id", ownerId);
      }
      // Also check if there's a collection record and update it
      if (row.unitId) {
        await supabase.from("collections").update({ status: "resolved" }).eq("unit_id", row.unitId).not("status", "eq", "resolved");
      }
      sonnerToast.success("Account marked as resolved and archived");
      fetchData();
    } catch (err: any) {
      sonnerToast.error("Failed to resolve", { description: err.message });
    }
  };

  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">
            <DollarSign className="h-6 w-6 text-primary" /> Outstanding Balances
          </h1>
          <p className="text-sm text-muted-foreground mt-1">
            Accounts receivable breakdown by unit and owner.
          </p>
        </div>
        <div className="flex items-center gap-2 flex-wrap">
          {selected.size > 0 && (
            <>
              <DropdownMenu>
                <DropdownMenuTrigger asChild>
                  <Button variant="outline" className="gap-2" disabled={bulkStageUpdating}>
                    <FileText className="h-4 w-4" />
                    {bulkStageUpdating ? "Updating…" : `Update Stage (${selected.size})`}
                  </Button>
                </DropdownMenuTrigger>
                <DropdownMenuContent>
                  {DELINQUENCY_STAGES.map(s => (
                    <DropdownMenuItem key={s.value} onClick={async () => {
                      setBulkStageUpdating(true);
                      try {
                        const selectedRows = filtered.filter(r => selected.has(r.unitKey));
                        const allOwnerIds = selectedRows.flatMap(r => r.ownerIds);
                        for (const ownerId of allOwnerIds) {
                          await supabase.from("owners").update({ delinquency_stage: s.value } as any).eq("id", ownerId);
                        }
                        setRows(prev => prev.map(r => selected.has(r.unitKey) ? { ...r, stage: s.value } : r));
                        sonnerToast.success(`Updated ${selectedRows.length} accounts to "${s.label}"`);
                        setSelected(new Set());
                      } catch (err: any) {
                        sonnerToast.error("Bulk stage update failed", { description: err.message });
                      } finally {
                        setBulkStageUpdating(false);
                      }
                    }}>
                      <span className={`inline-block w-2 h-2 rounded-full mr-2 ${s.color.split(" ")[0]}`} />
                      {s.label}
                    </DropdownMenuItem>
                  ))}
                </DropdownMenuContent>
              </DropdownMenu>
              <Button
                variant="outline"
                className="gap-2"
                onClick={handleBulkGenerateStatements}
                disabled={generatingStatements}
              >
                <FileText className="h-4 w-4" />
                {generatingStatements ? "Generating…" : `Statements (${selected.size})`}
              </Button>
              <Button
                variant="outline"
                className="gap-2"
                onClick={() => setBulkWorkflowOpen(true)}
              >
                <Play className="h-4 w-4" />
                Apply Workflow ({selected.size})
              </Button>
              <Button
                variant="outline"
                className="gap-2"
                onClick={() => setBulkTaskOpen(true)}
              >
                <ListTodo className="h-4 w-4" />
                Create Tasks ({selected.size})
              </Button>
              <Button
                variant="destructive"
                className="gap-2"
                onClick={handleBulkDelete}
                disabled={deleting}
              >
                <Trash2 className="h-4 w-4" />
                {deleting ? "Deleting…" : `Delete (${selected.size})`}
              </Button>
              <Button
                variant="destructive"
                className="gap-2"
                onClick={handleMoveToCollections}
                disabled={movingToCollections}
              >
                <Gavel className="h-4 w-4" />
                {movingToCollections
                  ? "Moving…"
                  : `Move to Collections (${selected.size})`}
              </Button>
            </>
          )}
          <LedgerCSVImportDialog onSuccess={fetchData} />
          <Button variant="outline" className="gap-2" onClick={() => setImportOpen(true)}>
            <Upload className="h-4 w-4" /> Import Buildium AR
          </Button>
          <Button variant="outline" className="gap-2" onClick={handleExportCSV} disabled={!filtered.length}>
            <Download className="h-4 w-4" /> Export CSV
          </Button>
          <Button variant="outline" className="gap-2" onClick={handleExportPDF} disabled={!filtered.length}>
            <FileText className="h-4 w-4" /> Export PDF
          </Button>
        </div>
      </div>

      {/* Summary Cards */}
      <div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 gap-4">
        <Card>
          <CardHeader className="pb-2">
            <CardTitle className="text-xs font-medium text-muted-foreground">Total A/R</CardTitle>
          </CardHeader>
          <CardContent>
            <p className="text-xl font-bold text-destructive">{fmt(totals.balance)}</p>
          </CardContent>
        </Card>
        <Card>
          <CardHeader className="pb-2">
            <CardTitle className="text-xs font-medium text-muted-foreground">Delinquent Units</CardTitle>
          </CardHeader>
          <CardContent>
            <p className="text-xl font-bold text-foreground">{filtered.length}</p>
          </CardContent>
        </Card>
        <Card>
          <CardHeader className="pb-2">
            <CardTitle className="text-xs font-medium text-muted-foreground">Assessments</CardTitle>
          </CardHeader>
          <CardContent>
            <p className="text-xl font-bold text-foreground">{fmt(totals.assessments)}</p>
          </CardContent>
        </Card>
        <Card>
          <CardHeader className="pb-2">
            <CardTitle className="text-xs font-medium text-muted-foreground">Fees & Interest</CardTitle>
          </CardHeader>
          <CardContent>
            <p className="text-xl font-bold text-foreground">
              {fmt(totals.lateFees + totals.interest + totals.adminFees + totals.bankFees)}
            </p>
          </CardContent>
        </Card>
        <Card>
          <CardHeader className="pb-2">
            <CardTitle className="text-xs font-medium text-muted-foreground">Payments Applied</CardTitle>
          </CardHeader>
          <CardContent>
            <p className="text-xl font-bold text-primary">{fmt(totals.payments)}</p>
          </CardContent>
        </Card>
      </div>

      {/* A/R Aging Report */}
      <ARAgingSummary rows={selectedAssociation !== "all" ? rows.filter(r => r.associationId === selectedAssociation) : rows} associationFilter={selectedAssociation} />

      {/* Filters */}
      <div className="flex flex-col sm:flex-row gap-3 flex-wrap">
        <Select value={selectedAssociation} onValueChange={setSelectedAssociation}>
          <SelectTrigger className="w-full sm:w-[260px]">
            <SelectValue placeholder="All Associations" />
          </SelectTrigger>
          <SelectContent>
            <SelectItem value="all">All Associations</SelectItem>
            {associations.map((a) => (
              <SelectItem key={a.id} value={a.id}>
                {a.name}
              </SelectItem>
            ))}
          </SelectContent>
        </Select>
        <div className="relative flex-1">
          <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
          <Input
            placeholder="Search by owner, unit, or address..."
            value={search}
            onChange={(e) => setSearch(e.target.value)}
            className="pl-9"
          />
        </div>
        <Popover>
          <PopoverTrigger asChild>
            <Button variant="outline" className={cn("w-[150px] justify-start text-left font-normal", !filterDateFrom && "text-muted-foreground")}>
              <CalendarIcon className="mr-2 h-4 w-4" />
              {filterDateFrom ? format(filterDateFrom, "MM/dd/yy") : "From date"}
            </Button>
          </PopoverTrigger>
          <PopoverContent className="w-auto p-0" align="start">
            <Calendar mode="single" selected={filterDateFrom} onSelect={setFilterDateFrom} initialFocus className="p-3 pointer-events-auto" />
          </PopoverContent>
        </Popover>
        <Popover>
          <PopoverTrigger asChild>
            <Button variant="outline" className={cn("w-[150px] justify-start text-left font-normal", !filterDateTo && "text-muted-foreground")}>
              <CalendarIcon className="mr-2 h-4 w-4" />
              {filterDateTo ? format(filterDateTo, "MM/dd/yy") : "To date"}
            </Button>
          </PopoverTrigger>
          <PopoverContent className="w-auto p-0" align="start">
            <Calendar mode="single" selected={filterDateTo} onSelect={setFilterDateTo} initialFocus className="p-3 pointer-events-auto" />
          </PopoverContent>
        </Popover>
        {(filterDateFrom || filterDateTo) && (
          <Button variant="ghost" size="sm" onClick={() => { setFilterDateFrom(undefined); setFilterDateTo(undefined); }}>
            <DollarSign className="h-4 w-4 mr-1" /> Clear Dates
          </Button>
        )}
      </div>

      {/* Aging Tabs */}
      <Tabs value={agingTab} onValueChange={(v) => { setAgingTab(v); setSelected(new Set()); }}>
        <TabsList>
          <TabsTrigger value="all">All</TabsTrigger>
          <TabsTrigger value="0-30">0–30 Days</TabsTrigger>
          <TabsTrigger value="31-60">31–60 Days</TabsTrigger>
          <TabsTrigger value="61-90">61–90 Days</TabsTrigger>
          <TabsTrigger value="91+">91+ Days</TabsTrigger>
        </TabsList>
      </Tabs>

      {/* 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>
      ) : filtered.length === 0 ? (
        <Card>
          <CardContent className="py-12 text-center text-muted-foreground">
            No outstanding balances found.
          </CardContent>
        </Card>
      ) : (
        <Card>
          <div className="overflow-x-auto">
            <Table>
              <TableHeader>
                <TableRow>
                  <TableHead className="w-10">
                    <Checkbox
                      checked={selected.size === filtered.length && filtered.length > 0}
                      onCheckedChange={toggleSelectAll}
                    />
                  </TableHead>
                  <TableHead>Owner</TableHead>
                  <TableHead>Unit</TableHead>
                  <TableHead>Association</TableHead>
                  <TableHead>Stage</TableHead>
                  <TableHead className="text-right">Assessments</TableHead>
                  <TableHead className="text-right">Late Fees</TableHead>
                  <TableHead className="text-right">Interest</TableHead>
                  <TableHead className="text-right">Admin Fees</TableHead>
                  <TableHead className="text-right">Legal Fees</TableHead>
                  <TableHead className="text-right">Violations</TableHead>
                  <TableHead className="text-right">Bank Fees</TableHead>
                  <TableHead className="text-right">Payments</TableHead>
                  <TableHead className="text-right">Balance Due</TableHead>
                  <TableHead className="w-10" />
                </TableRow>
              </TableHeader>
              <TableBody>
                {filtered.map((r) => (
                  <TableRow
                    key={r.unitKey}
                    className={selected.has(r.unitKey) ? "bg-primary/5" : ""}
                  >
                    <TableCell>
                      <Checkbox
                        checked={selected.has(r.unitKey)}
                        onCheckedChange={() => toggleSelect(r.unitKey)}
                      />
                    </TableCell>
                    <TableCell className="font-medium">
                      <div>{r.ownerNames.join(", ")}</div>
                      {r.ownerNames.length > 1 && <div className="text-xs text-muted-foreground">{r.ownerNames.length} co-owners</div>}
                    </TableCell>
                    <TableCell>
                      <div className="flex items-center gap-1.5">
                        <span className="text-sm">{r.unitNumber}</span>
                        {r.unitId && (
                          <a
                            href={`/dashboard/units/${r.unitId}`}
                            target="_blank"
                            rel="noopener noreferrer"
                            title="Open unit ledger in new window"
                            className="inline-flex items-center justify-center rounded px-1.5 py-0.5 text-xs font-medium text-primary hover:bg-primary/10 transition-colors gap-1"
                            onClick={(e) => e.stopPropagation()}
                          >
                            <ExternalLink className="h-3 w-3" />
                            Ledger
                          </a>
                        )}
                      </div>
                      {r.address !== "—" && (
                        <div className="text-xs text-muted-foreground">{r.address}</div>
                      )}
                    </TableCell>
                    <TableCell>
                      <Badge variant="outline" className="text-xs">
                        {r.associationName}
                      </Badge>
                    </TableCell>
                    <TableCell>
                      {(() => {
                        const stageInfo = DELINQUENCY_STAGES.find(s => s.value === r.stage);
                        return stageInfo ? (
                          <span className={`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium border ${stageInfo.color}`}>
                            {stageInfo.label}
                          </span>
                        ) : (
                          <span className="text-xs text-muted-foreground">—</span>
                        );
                      })()}
                    </TableCell>
                    <TableCell className="text-right tabular-nums">{fmt(r.assessments)}</TableCell>
                    <TableCell className="text-right tabular-nums">{fmt(r.lateFees)}</TableCell>
                    <TableCell className="text-right tabular-nums">{fmt(r.interest)}</TableCell>
                    <TableCell className="text-right tabular-nums">{fmt(r.adminFees)}</TableCell>
                    <TableCell className="text-right tabular-nums">{fmt(r.legalFees)}</TableCell>
                    <TableCell className="text-right tabular-nums">{fmt(r.violations)}</TableCell>
                    <TableCell className="text-right tabular-nums">{fmt(r.bankFees)}</TableCell>
                    <TableCell className="text-right tabular-nums text-primary">{fmt(r.payments)}</TableCell>
                    <TableCell className="text-right tabular-nums font-semibold text-destructive">
                      {fmt(r.balance)}
                    </TableCell>
                    <TableCell>
                      <DropdownMenu>
                        <DropdownMenuTrigger asChild>
                          <Button variant="ghost" size="icon" className="h-8 w-8">
                            <MoreHorizontal className="h-4 w-4" />
                          </Button>
                        </DropdownMenuTrigger>
                        <DropdownMenuContent align="end" className="w-52">
                          <DropdownMenuSub>
                            <DropdownMenuSubTrigger>
                              <FileText className="h-4 w-4 mr-2" /> Set Stage
                            </DropdownMenuSubTrigger>
                            <DropdownMenuSubContent>
                              {DELINQUENCY_STAGES.map(s => (
                                <DropdownMenuItem key={s.value} onClick={() => handleUpdateStage(r, s.value)}>
                                  <span className={`inline-block w-2 h-2 rounded-full mr-2 ${s.color.split(" ")[0]}`} />
                                  {s.label}
                                  {r.stage === s.value && <CheckCircle className="h-3 w-3 ml-auto text-primary" />}
                                </DropdownMenuItem>
                              ))}
                            </DropdownMenuSubContent>
                          </DropdownMenuSub>
                          <DropdownMenuSeparator />
                          <DropdownMenuLabel className="text-xs">Generate Notice</DropdownMenuLabel>
                          <DropdownMenuItem onClick={() => handleGenerateNotice(r, "late-notices")}>
                            Late Notice
                          </DropdownMenuItem>
                          <DropdownMenuItem onClick={() => handleGenerateNotice(r, "nola")}>
                            Notice of Late Assessment
                          </DropdownMenuItem>
                          <DropdownMenuItem onClick={() => handleGenerateNotice(r, "itl")}>
                            Intent to Lien
                          </DropdownMenuItem>
                          <DropdownMenuSeparator />
                          <DropdownMenuItem onClick={() => handleMarkResolved(r)}>
                            <Archive className="h-4 w-4 mr-2" /> Mark Resolved
                          </DropdownMenuItem>
                          <DropdownMenuItem onClick={() => handleDeleteSingle(r)} className="text-destructive focus:text-destructive">
                            <Trash2 className="h-4 w-4 mr-2" /> Delete Account
                          </DropdownMenuItem>
                        </DropdownMenuContent>
                      </DropdownMenu>
                    </TableCell>
                  </TableRow>
                ))}
                {/* Totals row */}
                <TableRow className="bg-muted/50 font-semibold border-t-2">
                  <TableCell />
                  <TableCell colSpan={4}>Totals ({filtered.length} units)</TableCell>
                  <TableCell className="text-right tabular-nums">{fmt(totals.assessments)}</TableCell>
                  <TableCell className="text-right tabular-nums">{fmt(totals.lateFees)}</TableCell>
                  <TableCell className="text-right tabular-nums">{fmt(totals.interest)}</TableCell>
                  <TableCell className="text-right tabular-nums">{fmt(totals.adminFees)}</TableCell>
                  <TableCell className="text-right tabular-nums">{fmt(totals.legalFees)}</TableCell>
                  <TableCell className="text-right tabular-nums">{fmt(totals.violations)}</TableCell>
                  <TableCell className="text-right tabular-nums">{fmt(totals.bankFees)}</TableCell>
                  <TableCell className="text-right tabular-nums text-primary">{fmt(totals.payments)}</TableCell>
                  <TableCell className="text-right tabular-nums text-destructive">{fmt(totals.balance)}</TableCell>
                  <TableCell />
                </TableRow>
              </TableBody>
            </Table>
          </div>
        </Card>
      )}

      <BuildiumARImportDialog
        open={importOpen}
        onOpenChange={setImportOpen}
        associations={associations}
        onImportComplete={fetchData}
      />

      <BulkTaskDialog
        open={bulkTaskOpen}
        onOpenChange={setBulkTaskOpen}
        selectedUnits={filtered.filter((r) => selected.has(r.unitKey))}
        users={users}
        onSuccess={() => { setSelected(new Set()); }}
      />

      <BulkApplyWorkflowDialog
        open={bulkWorkflowOpen}
        onOpenChange={setBulkWorkflowOpen}
        units={filtered
          .filter((r) => selected.has(r.unitKey) && r.unitId)
          .map((r) => ({
            unitId: r.unitId!,
            unitNumber: r.unitNumber,
            associationId: r.associationId,
            associationName: r.associationName,
          }))}
        onSuccess={() => { setSelected(new Set()); }}
      />
    </div>
  );
}
