import { useMemo, useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { supabase } from "@/integrations/supabase/client";
import { Card, CardContent } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Button } from "@/components/ui/button";
import { FileDown, Download } from "lucide-react";
import jsPDF from "jspdf";
import autoTable from "jspdf-autotable";
import { ReportSheet } from "./ReportSheet";
import { loadBrandedLogo, drawBrandedHeader, drawBrandedFooter } from "../lib/reportHeader";
import {
  fetchAssociationId, fetchOwnerLedger, fetchUnitsAndOwners,
  unitLabel, chargeTypeLabel, money,
  type OwnerLedgerEntry, type UnitInfo, type OwnerInfo,
} from "../lib/ownerLedger";

const TEAL: [number, number, number] = [0, 137, 123];
const BUCKETS = ["0-30", "Over 30", "Over 60", "Over 90"] as const;
const BUCKET_COLORS: [number, number, number][] = [
  [141, 178, 85],   // green  (0-30)
  [234, 179, 8],    // amber  (over 30)
  [234, 124, 8],    // orange (over 60)
  [220, 68, 68],    // red    (over 90)
];

type Buckets = [number, number, number, number];

type UnitAging = {
  key: string;
  label: string;
  collStatus: string | null;
  buckets: Buckets;
  total: number;
  byType: Map<string, { buckets: Buckets; total: number }>;
};

function emptyBuckets(): Buckets { return [0, 0, 0, 0]; }
function bucketIndex(asOf: string, chargeDate: string): number {
  const days = Math.floor((new Date(asOf + "T00:00:00").getTime() - new Date(chargeDate + "T00:00:00").getTime()) / 86400000);
  if (days <= 30) return 0;
  if (days <= 60) return 1;
  if (days <= 90) return 2;
  return 3;
}

const prettyStatus = (s: string | null | undefined) =>
  s ? s.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()) : null;

const dash = (n: number) => (n ? money(n) : "-");

/**
 * Buildium-style AR Aging: per-property open charge balances aged into
 * 0-30 / Over 30 / Over 60 / Over 90 buckets, with charge-type breakdown,
 * collection status, summary and distribution. Payments and credits apply to
 * charges oldest-first (FIFO), so only genuinely open charge amounts age.
 */
export function ARAgingPropertyReport({ companyId, companyName, logoUrl }: { companyId: string; companyName: string; logoUrl?: string | null }) {
  const [asOf, setAsOf] = useState(() => new Date().toISOString().slice(0, 10));

  const { data, isLoading } = useQuery({
    queryKey: ["ar-aging-property", companyId, asOf],
    enabled: !!companyId,
    queryFn: async () => {
      const associationId = await fetchAssociationId(companyId);
      if (!associationId) return null;
      const [entries, { units, owners }, collectionsRes] = await Promise.all([
        fetchOwnerLedger(associationId, asOf),
        fetchUnitsAndOwners(associationId),
        supabase.from("collections").select("unit_id, owner_id, status, updated_at").eq("association_id", associationId).order("updated_at", { ascending: false }),
      ]);
      return { entries, units, owners, collections: (collectionsRes.data ?? []) as any[] };
    },
  });

  const report = useMemo(() => {
    if (!data) return null;
    const { entries, units, owners, collections } = data;

    const unitById = new Map<string, UnitInfo>();
    for (const u of units) unitById.set(u.id, u);
    const ownerById = new Map<string, OwnerInfo>();
    for (const o of owners) ownerById.set(o.id, o);
    const ownerByUnit = new Map<string, OwnerInfo>();
    for (const o of owners) if (o.unit_id && !ownerByUnit.has(o.unit_id)) ownerByUnit.set(o.unit_id, o);

    // Latest collection status per unit (rows came back newest-first)
    const collByUnit = new Map<string, string>();
    const collByOwner = new Map<string, string>();
    for (const c of collections) {
      if (c.unit_id && !collByUnit.has(c.unit_id)) collByUnit.set(c.unit_id, c.status);
      if (c.owner_id && !collByOwner.has(c.owner_id)) collByOwner.set(c.owner_id, c.status);
    }

    // Group ledger entries per unit (fall back to owner when the entry has no unit)
    const byUnit = new Map<string, OwnerLedgerEntry[]>();
    for (const e of entries) {
      const key = e.unit_id ? `u:${e.unit_id}` : e.owner_id ? `o:${e.owner_id}` : null;
      if (!key) continue;
      const list = byUnit.get(key) ?? [];
      list.push(e);
      byUnit.set(key, list);
    }

    const rows: UnitAging[] = [];
    for (const [key, list] of byUnit) {
      list.sort((a, b) => a.date.localeCompare(b.date));
      // FIFO: total credits pay down the oldest charges first
      let creditPool = list.reduce((s, e) => s + e.credit, 0);
      const buckets = emptyBuckets();
      const byType = new Map<string, { buckets: Buckets; total: number }>();
      let total = 0;

      for (const e of list) {
        if (e.debit <= 0) continue;
        let open = e.debit;
        if (creditPool > 0) {
          const applied = Math.min(creditPool, open);
          creditPool -= applied;
          open -= applied;
        }
        if (open <= 0.004) continue;
        const bi = bucketIndex(asOf, e.date);
        buckets[bi] += open;
        total += open;
        const label = chargeTypeLabel(e.transaction_type);
        const t = byType.get(label) ?? { buckets: emptyBuckets(), total: 0 };
        t.buckets[bi] += open;
        t.total += open;
        byType.set(label, t);
      }
      if (total <= 0.004) continue;

      const unitId = key.startsWith("u:") ? key.slice(2) : null;
      const ownerId = key.startsWith("o:") ? key.slice(2) : null;
      const unit = unitId ? unitById.get(unitId) : undefined;
      const owner = (unitId ? ownerByUnit.get(unitId) : null) ?? (ownerId ? ownerById.get(ownerId) : null) ?? null;
      const collStatus = prettyStatus((unitId && collByUnit.get(unitId)) || (owner && collByOwner.get(owner.id)) || null);

      rows.push({
        key,
        label: unitLabel(unit, owner?.last_name ?? null),
        collStatus,
        buckets,
        total,
        byType,
      });
    }

    rows.sort((a, b) => a.label.localeCompare(b.label));

    // Charge-type summary across all properties
    const summary = new Map<string, { count: number; balance: number }>();
    for (const r of rows) {
      for (const [label, t] of r.byType) {
        const s = summary.get(label) ?? { count: 0, balance: 0 };
        s.count += 1;
        s.balance += t.total;
        summary.set(label, s);
      }
    }
    const summaryRows = [...summary.entries()].sort((a, b) => b[1].balance - a[1].balance);

    const totals = rows.reduce<Buckets>((t, r) => [t[0] + r.buckets[0], t[1] + r.buckets[1], t[2] + r.buckets[2], t[3] + r.buckets[3]], emptyBuckets());
    const counts = rows.reduce<[number, number, number, number]>(
      (t, r) => [t[0] + (r.buckets[0] > 0.004 ? 1 : 0), t[1] + (r.buckets[1] > 0.004 ? 1 : 0), t[2] + (r.buckets[2] > 0.004 ? 1 : 0), t[3] + (r.buckets[3] > 0.004 ? 1 : 0)],
      [0, 0, 0, 0],
    );
    const grandTotal = totals.reduce((s, n) => s + n, 0);
    const distribution = totals.map((n) => (grandTotal > 0 ? (n / grandTotal) * 100 : 0));

    return { rows, summaryRows, totals, counts, grandTotal, distribution };
  }, [data, asOf]);

  const asOfLabel = new Date(asOf + "T00:00:00").toLocaleDateString("en-US", { month: "numeric", day: "numeric", year: "numeric" });

  const exportPDF = async () => {
    if (!report) return;
    const doc = new jsPDF({ unit: "pt", format: "letter" });
    const ML = 40;
    const pageW = doc.internal.pageSize.getWidth();
    const logo = await loadBrandedLogo(logoUrl);
    let y = drawBrandedHeader(doc, {
      logo, title: "AR Aging", subtitle: `As of ${asOfLabel}`,
      metaLines: [{ label: "Properties:", value: companyName || "" }],
    });

    // Summary (left) — charge types
    autoTable(doc, {
      startY: y,
      head: [["Charge", "Balance"]],
      body: [
        ...report.summaryRows.map(([label, s]) => [`${label} (${s.count})`, money(s.balance)]),
        [{ content: "Total", styles: { fontStyle: "bold" } } as any, { content: money(report.grandTotal), styles: { fontStyle: "bold", halign: "right" } } as any],
      ],
      styles: { fontSize: 8, cellPadding: 4 },
      headStyles: { fillColor: TEAL, textColor: 255 },
      columnStyles: { 1: { halign: "right" } },
      margin: { left: ML, right: pageW / 2 + 20 },
      tableWidth: pageW / 2 - ML - 30,
    });
    const summaryEndY = (doc as any).lastAutoTable.finalY;

    // Distribution (right) — stacked bucket bar with % legend
    const barX = pageW / 2 + 20;
    const barW = pageW - ML - barX;
    let dy = y + 4;
    doc.setFont("helvetica", "bold"); doc.setFontSize(8); doc.setTextColor(100);
    doc.text("DISTRIBUTION", barX, dy);
    dy += 8;
    if (report.grandTotal > 0) {
      let x = barX;
      report.distribution.forEach((pct, i) => {
        const w = (pct / 100) * barW;
        if (w <= 0) return;
        const c = BUCKET_COLORS[i];
        doc.setFillColor(c[0], c[1], c[2]);
        doc.rect(x, dy, w, 14, "F");
        x += w;
      });
      dy += 24;
      doc.setFont("helvetica", "normal"); doc.setFontSize(7.5); doc.setTextColor(60);
      report.distribution.forEach((pct, i) => {
        if (pct <= 0) return;
        const c = BUCKET_COLORS[i];
        doc.setFillColor(c[0], c[1], c[2]);
        doc.rect(barX, dy - 6, 7, 7, "F");
        doc.text(`${BUCKETS[i]}: ${pct.toFixed(2)} %`, barX + 11, dy);
        dy += 12;
      });
    }
    y = Math.max(summaryEndY, dy) + 16;

    // Property detail
    const body: any[] = [];
    for (const r of report.rows) {
      body.push([
        { content: r.label + (r.collStatus ? `\nColl Status: ${r.collStatus}` : ""), styles: { fontStyle: "bold" } },
        { content: dash(r.buckets[0]), styles: { fontStyle: "bold", halign: "right" } },
        { content: dash(r.buckets[1]), styles: { fontStyle: "bold", halign: "right" } },
        { content: dash(r.buckets[2]), styles: { fontStyle: "bold", halign: "right" } },
        { content: dash(r.buckets[3]), styles: { fontStyle: "bold", halign: "right" } },
        { content: money(r.total), styles: { fontStyle: "bold", halign: "right" } },
      ]);
      for (const [label, t] of r.byType) {
        body.push([
          { content: `      ${label}`, styles: {} },
          { content: dash(t.buckets[0]), styles: { halign: "right" } },
          { content: dash(t.buckets[1]), styles: { halign: "right" } },
          { content: dash(t.buckets[2]), styles: { halign: "right" } },
          { content: dash(t.buckets[3]), styles: { halign: "right" } },
          { content: money(t.total), styles: { halign: "right" } },
        ]);
      }
    }
    body.push([
      { content: "Total:", styles: { fontStyle: "bold" } },
      ...report.totals.map((n) => ({ content: money(n), styles: { fontStyle: "bold", halign: "right" } })),
      { content: money(report.grandTotal), styles: { fontStyle: "bold", halign: "right" } },
    ]);
    body.push([
      { content: "Property Count:", styles: { fontStyle: "bold" } },
      ...report.counts.map((n) => ({ content: String(n), styles: { fontStyle: "bold", halign: "right" } })),
      { content: "", styles: {} },
    ]);

    autoTable(doc, {
      startY: y,
      head: [["Property", "0-30", "Over 30", "Over 60", "Over 90", "Balance"]],
      body,
      styles: { fontSize: 8, cellPadding: 3 },
      headStyles: { fillColor: TEAL, textColor: 255 },
      columnStyles: { 0: { cellWidth: 220 }, 1: { halign: "right" }, 2: { halign: "right" }, 3: { halign: "right" }, 4: { halign: "right" }, 5: { halign: "right" } },
      margin: { left: ML, right: ML },
    });

    drawBrandedFooter(doc);
    doc.save(`ar-aging-${asOf}.pdf`);
  };

  const exportCSV = () => {
    if (!report) return;
    const f = (n: number) => (Math.round((n + Number.EPSILON) * 100) / 100 || 0).toFixed(2);
    const lines = [["Property", "Coll Status", "Charge Type", "0-30", "Over 30", "Over 60", "Over 90", "Balance"].join(",")];
    const q = (s: string) => `"${s.replace(/"/g, '""')}"`;
    for (const r of report.rows) {
      lines.push([q(r.label), q(r.collStatus ?? ""), "", f(r.buckets[0]), f(r.buckets[1]), f(r.buckets[2]), f(r.buckets[3]), f(r.total)].join(","));
      for (const [label, t] of r.byType) {
        lines.push([q(r.label), "", q(label), f(t.buckets[0]), f(t.buckets[1]), f(t.buckets[2]), f(t.buckets[3]), f(t.total)].join(","));
      }
    }
    lines.push(["TOTAL", "", "", ...report.totals.map(f), f(report.grandTotal)].join(","));
    const blob = new Blob([lines.join("\n")], { type: "text/csv" });
    const a = document.createElement("a");
    a.href = URL.createObjectURL(blob);
    a.download = `ar-aging-${asOf}.csv`;
    a.click();
    URL.revokeObjectURL(a.href);
  };

  return (
    <div className="space-y-4">
      <Card>
        <CardContent className="flex flex-wrap items-end gap-4 py-4">
          <div>
            <Label className="text-xs text-muted-foreground">As of</Label>
            <Input type="date" value={asOf} onChange={(e) => setAsOf(e.target.value || asOf)} className="w-44 mt-1" />
          </div>
          {report && report.rows.length > 0 && (
            <div className="ml-auto flex gap-2">
              <Button variant="outline" onClick={exportCSV}><Download className="mr-1 h-4 w-4" /> CSV</Button>
              <Button onClick={exportPDF}><FileDown className="mr-1 h-4 w-4" /> PDF</Button>
            </div>
          )}
        </CardContent>
      </Card>

      {isLoading ? (
        <Card><CardContent className="p-8 text-center text-sm text-muted-foreground">Loading…</CardContent></Card>
      ) : !report || report.rows.length === 0 ? (
        <Card><CardContent className="p-8 text-center text-sm text-muted-foreground">
          No open balances as of {asOfLabel}. 🎉
        </CardContent></Card>
      ) : (
        <ReportSheet title="AR Aging" subtitle={`As of ${asOfLabel}`} companyName={companyName} logoUrl={logoUrl}>
          {/* Summary + Distribution */}
          <div className="grid gap-6 sm:grid-cols-2 mb-6">
            <div>
              <p className="text-[11px] uppercase tracking-wide font-semibold text-muted-foreground mb-2 text-center">Summary</p>
              <table className="w-full text-sm">
                <thead>
                  <tr className="border-y text-[11px] uppercase tracking-wide text-muted-foreground">
                    <th className="px-3 py-1.5 text-left font-semibold">Charge</th>
                    <th className="px-3 py-1.5 text-right font-semibold">Balance</th>
                  </tr>
                </thead>
                <tbody>
                  {report.summaryRows.map(([label, s]) => (
                    <tr key={label} className="border-b">
                      <td className="px-3 py-1.5">{label} ({s.count})</td>
                      <td className="px-3 py-1.5 text-right tabular-nums">{money(s.balance)}</td>
                    </tr>
                  ))}
                  <tr className="font-bold border-b-2">
                    <td className="px-3 py-1.5 text-right">Total</td>
                    <td className="px-3 py-1.5 text-right tabular-nums">{money(report.grandTotal)}</td>
                  </tr>
                </tbody>
              </table>
            </div>
            <div>
              <p className="text-[11px] uppercase tracking-wide font-semibold text-muted-foreground mb-2 text-center">Distribution</p>
              <div className="flex h-5 w-full overflow-hidden rounded">
                {report.distribution.map((pct, i) => pct > 0 && (
                  <div
                    key={i}
                    style={{ width: `${pct}%`, backgroundColor: `rgb(${BUCKET_COLORS[i].join(",")})` }}
                    title={`${BUCKETS[i]}: ${pct.toFixed(2)}%`}
                  />
                ))}
              </div>
              <div className="mt-3 space-y-1">
                {report.distribution.map((pct, i) => pct > 0 && (
                  <div key={i} className="flex items-center gap-2 text-xs text-muted-foreground">
                    <span className="inline-block h-3 w-3 rounded-sm" style={{ backgroundColor: `rgb(${BUCKET_COLORS[i].join(",")})` }} />
                    {BUCKETS[i]}: {pct.toFixed(2)} %
                  </div>
                ))}
              </div>
            </div>
          </div>

          {/* Property detail */}
          <div className="overflow-x-auto">
            <table className="w-full text-sm">
              <thead>
                <tr className="border-y text-[11px] uppercase tracking-wide text-muted-foreground">
                  <th className="px-3 py-2 text-left font-semibold">Property</th>
                  <th className="px-3 py-2 text-right font-semibold">0-30</th>
                  <th className="px-3 py-2 text-right font-semibold">Over 30</th>
                  <th className="px-3 py-2 text-right font-semibold">Over 60</th>
                  <th className="px-3 py-2 text-right font-semibold">Over 90</th>
                  <th className="px-3 py-2 text-right font-semibold">Balance</th>
                </tr>
              </thead>
              <tbody>
                {report.rows.map((r) => (
                  <>
                    <tr key={r.key} className="border-b bg-muted/30">
                      <td className="px-3 py-1.5 font-semibold">
                        {r.label}
                        {r.collStatus && <div className="text-[11px] font-medium text-amber-700">Coll Status: {r.collStatus}</div>}
                      </td>
                      <td className="px-3 py-1.5 text-right tabular-nums font-semibold">{dash(r.buckets[0])}</td>
                      <td className="px-3 py-1.5 text-right tabular-nums font-semibold">{dash(r.buckets[1])}</td>
                      <td className="px-3 py-1.5 text-right tabular-nums font-semibold">{dash(r.buckets[2])}</td>
                      <td className="px-3 py-1.5 text-right tabular-nums font-semibold">{dash(r.buckets[3])}</td>
                      <td className="px-3 py-1.5 text-right tabular-nums font-semibold">{money(r.total)}</td>
                    </tr>
                    {[...r.byType.entries()].map(([label, t]) => (
                      <tr key={`${r.key}-${label}`} className="border-b">
                        <td className="px-3 py-1 pl-8 text-muted-foreground">{label}</td>
                        <td className="px-3 py-1 text-right tabular-nums">{dash(t.buckets[0])}</td>
                        <td className="px-3 py-1 text-right tabular-nums">{dash(t.buckets[1])}</td>
                        <td className="px-3 py-1 text-right tabular-nums">{dash(t.buckets[2])}</td>
                        <td className="px-3 py-1 text-right tabular-nums">{dash(t.buckets[3])}</td>
                        <td className="px-3 py-1 text-right tabular-nums">{money(t.total)}</td>
                      </tr>
                    ))}
                  </>
                ))}
              </tbody>
              <tfoot>
                <tr className="border-t-2 border-b font-bold">
                  <td className="px-3 py-2">Total:</td>
                  {report.totals.map((n, i) => <td key={i} className="px-3 py-2 text-right tabular-nums">{money(n)}</td>)}
                  <td className="px-3 py-2 text-right tabular-nums">{money(report.grandTotal)}</td>
                </tr>
                <tr className="border-b-2 font-semibold text-muted-foreground">
                  <td className="px-3 py-2">Property Count:</td>
                  {report.counts.map((n, i) => <td key={i} className="px-3 py-2 text-right tabular-nums">{n}</td>)}
                  <td />
                </tr>
              </tfoot>
            </table>
          </div>
        </ReportSheet>
      )}
    </div>
  );
}
