"use client";
import React, { useEffect, useState, useMemo, useRef, useCallback } from "react";
import { useRouter } from "next/navigation";
import toast from "react-hot-toast";
import { getAuditAssignReport } from "@/lib/api/previous-nc.api";
import type { AuditAssignReportResponse } from "@/lib/api/previous-nc.api";
import {
  getAuditDetailReport,
  getAuditReportFileUrl,
  getAllAuditDetailRows,
} from "@/lib/api/audit-report.api";
import type {
  AuditDetailResponse,
  AuditDetailRow,
  AuditDetailFilters,
} from "@/lib/api/audit-report.api";

import {
  ArrowLeft,
  RotateCw,
  Download,
  FileSpreadsheet,
  X as XIcon,
  AlertTriangle,
  Filter as FilterIcon,
  Hourglass,
  UserCheck,
  Calendar,
  ClipboardList,
  FileCheck2,
  FileX2,
  ChevronLeft,
  ChevronRight,
  ArrowUpDown,
  ExternalLink,
  Clock,
} from "lucide-react";

// ═══════════════════════════════════════════════════════════════════════
// Design Tokens (identical to AuditAssignReportPage / NcReportPage)
// ═══════════════════════════════════════════════════════════════════════
const TOKENS = {
  brand: "#0f766e",
  brandLight: "#14b8a6",
  ink: "#0b1220",
  ink2: "#1f2937",
  ink3: "#475569",
  ink4: "#64748b",
  ink5: "#94a3b8",
  line: "#e2e8f0",
  line2: "#f1f5f9",
  bg: "#f6f8fa",
  surface: "#ffffff",
  success: "#16a34a",
  warning: "#f59e0b",
  danger: "#dc2626",
  info: "#2563eb",
  qrs: "#6366f1",
  tqs: "#06b6d4",
  initial: "#8b5cf6",
  surveillance: "#0891b2",
  reassessment: "#db2777",
  rSm: "6px",
  rMd: "10px",
  rLg: "14px",
  shadow: "0 1px 3px rgba(15,23,42,0.06), 0 8px 24px rgba(15,23,42,0.04)",
  shadowLg: "0 4px 12px rgba(15,23,42,0.08), 0 16px 40px rgba(15,23,42,0.06)",
};

const monthName = (m: number) =>
  ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"][m] || "";
const fmt = (n: number) => (n ?? 0).toLocaleString();
const todayStr = () =>
  new Date().toLocaleDateString("en-GB", { day: "2-digit", month: "long", year: "numeric" });
const fmtDate = (d: string | null) => {
  if (!d) return "—";
  const dt = new Date(d);
  if (isNaN(dt.getTime())) return d;
  return dt.toLocaleDateString("en-GB", { day: "2-digit", month: "short", year: "numeric" });
};

const TYPE_COLOR: Record<string, string> = {
  Initial: TOKENS.initial,
  Surveillance: TOKENS.surveillance,
  Recertification: TOKENS.reassessment,
};
const SOURCE_COLOR: Record<string, string> = { QRS: TOKENS.qrs, TQS: TOKENS.tqs };

// ═══════════════════════════════════════════════════════════════════════
// Page
// ═══════════════════════════════════════════════════════════════════════
export default function AuditDetailReportPage() {
  const router = useRouter();
  const reportRef = useRef<HTMLDivElement>(null);

  // table data
  const [data, setData] = useState<AuditDetailResponse | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [exporting, setExporting] = useState(false);

  // dropdown options come from the assign report (same source as the screenshot)
  const [optionsData, setOptionsData] = useState<AuditAssignReportResponse | null>(null);

  // filters
  const [filterSource, setFilterSource] = useState<string>("");
  const [filterAuditor, setFilterAuditor] = useState<string>("");
  const [filterYear, setFilterYear] = useState<string>("");
  const [filterMonthNum, setFilterMonthNum] = useState<string>("");
  const [filterType, setFilterType] = useState<string>("");
  const [filterStatus, setFilterStatus] = useState<string>("");
  const [search, setSearch] = useState<string>("");

  // paging / sort
  const [page, setPage] = useState(1);
  const [sort, setSort] = useState<AuditDetailFilters["sort"]>("audit_date");
  const [dir, setDir] = useState<"asc" | "desc">("desc");
  const LIMIT = 50;

  const hasActiveFilter = !!(
    filterSource || filterAuditor || filterYear || filterMonthNum || filterType || filterStatus || search
  );

  // current filter object sent to the backend
  const currentFilters: AuditDetailFilters = useMemo(
    () => ({
      source: (filterSource || undefined) as AuditDetailFilters["source"],
      auditor: filterAuditor || undefined,
      year: filterYear || undefined,
      month: filterMonthNum ? String(parseInt(filterMonthNum, 10)) : undefined,
      audit_type: (filterType || undefined) as AuditDetailFilters["audit_type"],
      report_status: (filterStatus || undefined) as AuditDetailFilters["report_status"],
      search: search || undefined,
      sort,
      dir,
    }),
    [filterSource, filterAuditor, filterYear, filterMonthNum, filterType, filterStatus, search, sort, dir],
  );

  // load dropdown options once
  useEffect(() => {
    getAuditAssignReport()
      .then(setOptionsData)
      .catch(() => {/* dropdowns will just be sparse */});
  }, []);

  // load table whenever filters/page change
  const fetchData = useCallback(async () => {
    setLoading(true);
    setError(null);
    try {
      const res = await getAuditDetailReport({ ...currentFilters, page, limit: LIMIT });
      setData(res);
    } catch (err: any) {
      setError(err?.message ?? "Failed to load detail report");
    } finally {
      setLoading(false);
    }
  }, [currentFilters, page]);

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

  // reset to page 1 when any filter changes
  useEffect(() => { setPage(1); }, [filterSource, filterAuditor, filterYear, filterMonthNum, filterType, filterStatus, search, sort, dir]);

  // ── dropdown option lists (cascading auditor by source) ──
  const filterOptions = useMemo(() => {
    const rows = optionsData?.rows ?? [];
    const sources = Array.from(new Set(rows.map((r) => r.source))).sort();
    const auditorsAll = rows
      .filter((r) => !filterSource || r.source === filterSource)
      .map((r) => r.auditor);
    const auditors = Array.from(new Set(auditorsAll)).sort((a, b) => a.localeCompare(b));
    const months = new Set<string>();
    rows.forEach((r) => r.months.forEach((m) => m.month && months.add(m.month)));
    const years = Array.from(new Set(Array.from(months).map((m) => m.split("-")[0])))
      .filter((y) => /^\d{4}$/.test(y))
      .sort((a, b) => Number(b) - Number(a));
    const monthNums = Array.from({ length: 12 }, (_, i) => String(i + 1).padStart(2, "0"));
    return { sources, auditors, years, monthNums };
  }, [optionsData, filterSource]);

  // clear auditor if it falls outside the (cascaded) list
  useEffect(() => {
    if (filterAuditor && !filterOptions.auditors.includes(filterAuditor)) {
      setFilterAuditor("");
    }
  }, [filterOptions.auditors, filterAuditor]);

  const clearFilters = () => {
    setFilterSource(""); setFilterAuditor(""); setFilterYear("");
    setFilterMonthNum(""); setFilterType(""); setFilterStatus(""); setSearch("");
  };

  const toggleSort = (key: NonNullable<AuditDetailFilters["sort"]>) => {
    if (sort === key) setDir((d) => (d === "asc" ? "desc" : "asc"));
    else { setSort(key); setDir("asc"); }
  };

  // ── open a stage file ──
  const openFile = async (row: AuditDetailRow, stage: 1 | 2) => {
    const uploaded = stage === 1 ? row.stage1_uploaded : row.stage2_uploaded;
    if (!uploaded) return;
    try {
      if (row.source === "QRS & TQS") {
        const { fetchAuditDocumentBlob } = await import("@/lib/api/audit-documents.api");
        const docType = stage === 1 ? "stage1_report" : "stage2_report";
        const url = await fetchAuditDocumentBlob(row.record_id, docType);
        window.open(url, "_blank", "noopener");
      } else {
        const { url } = await getAuditReportFileUrl({ source: row.source, table: row.table, id: row.record_id, stage });
        window.open(url, "_blank", "noopener");
      }
    } catch (err: any) {
      toast.error(err?.message ?? "Could not open file");
    }
  };

  // ── exports (fetch ALL filtered rows first) ──
  const exportExcel = async () => {
    setExporting(true);
    try {
      const rows = await getAllAuditDetailRows(currentFilters);
      const XLSX = await import("xlsx");
      const sheet = rows.map((r) => ({
        Client: r.client_name ?? "",
        "Audit Date": r.audit_date ?? "",
        Type: r.audit_type,
        "Stage 1": r.stage1_uploaded ? "Uploaded" : "Missing",
        "Stage 1 File": r.stage1_path ?? "",
        "Stage 2": r.stage2_uploaded ? "Uploaded" : "Missing",
        "Stage 2 File": r.stage2_path ?? "",
        Auditor: r.auditor,
        Database: r.source,
        "Age (days)": r.age_days ?? "",
      }));
      const ws = XLSX.utils.json_to_sheet(sheet);
      const wb = XLSX.utils.book_new();
      XLSX.utils.book_append_sheet(wb, ws, "Audit Detail");
      XLSX.writeFile(wb, `audit-detail-${new Date().toISOString().slice(0, 10)}.xlsx`);
      toast.success(`Exported ${rows.length} rows`);
    } catch (err: any) {
      toast.error(err?.message ?? "Excel export failed");
    } finally {
      setExporting(false);
    }
  };

  const exportPdf = async () => {
    setExporting(true);
    try {
      const rows = await getAllAuditDetailRows(currentFilters);
      const win = window.open("", "_blank");
      if (!win) { toast.error("Popup blocked — allow popups to export PDF"); return; }
      const rowsHtml = rows
        .map(
          (r) => `<tr>
            <td>${escapeHtml(r.client_name ?? "")}</td>
            <td>${fmtDate(r.audit_date)}</td>
            <td>${r.audit_type}</td>
            <td style="color:${r.stage1_uploaded ? "#16a34a" : "#dc2626"}">${r.stage1_uploaded ? "Uploaded" : "Missing"}</td>
            <td style="color:${r.stage2_uploaded ? "#16a34a" : "#dc2626"}">${r.stage2_uploaded ? "Uploaded" : "Missing"}</td>
            <td>${escapeHtml(r.auditor)}</td>
            <td>${r.source}</td>
            <td>${r.age_days ?? ""}</td>
          </tr>`,
        )
        .join("");
      win.document.write(`<!doctype html><html><head><title>Audit Detail Report</title>
        <style>
          body{font-family:Arial,Helvetica,sans-serif;color:#1f2937;padding:24px;}
          h1{font-size:18px;margin:0 0 4px;}
          .meta{font-size:11px;color:#64748b;margin:0 0 16px;}
          table{width:100%;border-collapse:collapse;font-size:11px;}
          th,td{border:1px solid #e2e8f0;padding:6px 8px;text-align:left;}
          th{background:#f1f5f9;font-size:10px;text-transform:uppercase;letter-spacing:.04em;color:#475569;}
        </style></head><body>
        <h1>Audit Detail Report</h1>
        <p class="meta">Generated ${todayStr()} · ${rows.length} audit events${hasActiveFilter ? " · filtered" : ""}</p>
        <table><thead><tr>
          <th>Client</th><th>Audit Date</th><th>Type</th><th>Stage 1</th><th>Stage 2</th><th>Auditor</th><th>DB</th><th>Age</th>
        </tr></thead><tbody>${rowsHtml}</tbody></table>
        </body></html>`);
      win.document.close();
      win.focus();
      setTimeout(() => win.print(), 400);
      toast.success(`Prepared ${rows.length} rows for PDF`);
    } catch (err: any) {
      toast.error(err?.message ?? "PDF export failed");
    } finally {
      setExporting(false);
    }
  };

  if (loading && !data) return <LoadingState />;
  if (error && !data) return <ErrorState message={error} onRetry={fetchData} />;

  const summary = data?.summary;
  const aging = data?.aging;
  const rows = data?.rows ?? [];
  const total = data?.total ?? 0;
  const totalPages = data?.totalPages ?? 1;

  return (
    <div style={{ background: TOKENS.bg, minHeight: "100vh", padding: "28px 24px 60px", fontFamily: "Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif", color: TOKENS.ink }}>
      {/* Action bar */}
      <div className="no-print" style={{ maxWidth: 1320, margin: "0 auto 16px", display: "flex", justifyContent: "space-between", alignItems: "center", gap: 12, flexWrap: "wrap" }}>
        <button
          onClick={() => router.back()}
          style={{ display: "inline-flex", alignItems: "center", gap: 6, padding: "9px 16px", background: TOKENS.surface, border: `1px solid ${TOKENS.line}`, borderRadius: TOKENS.rMd, cursor: "pointer", fontSize: 13, fontWeight: 600, color: TOKENS.ink3, boxShadow: TOKENS.shadow }}
        >
          <ArrowLeft size={14} strokeWidth={2.2} /> Back to NCs
        </button>
        <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
          <ActionButton onClick={fetchData} variant="secondary"><RotateCw size={14} strokeWidth={2.2} />Refresh</ActionButton>
          <ActionButton onClick={exportExcel} variant="secondary" disabled={exporting}>
            {exporting ? <Hourglass size={14} strokeWidth={2.2} /> : <FileSpreadsheet size={14} strokeWidth={2.2} />}Excel
          </ActionButton>
          <ActionButton onClick={exportPdf} variant="primary" disabled={exporting}>
            {exporting ? <><Hourglass size={14} strokeWidth={2.2} />Working…</> : <><Download size={14} strokeWidth={2.2} />PDF</>}
          </ActionButton>
        </div>
      </div>

      {/* Filter bar (matches the assign report screenshot) */}
      <div className="no-print" style={{ maxWidth: 1320, margin: "0 auto 20px" }}>
        <div style={{ background: TOKENS.surface, border: `1px solid ${TOKENS.line}`, borderRadius: TOKENS.rLg, padding: "14px 18px", boxShadow: TOKENS.shadow, display: "flex", alignItems: "flex-end", gap: 12, flexWrap: "wrap" }}>
          <div style={{ display: "flex", alignItems: "center", gap: 8, paddingBottom: 8, marginRight: 4 }}>
            <span style={{ display: "inline-flex", alignItems: "center", justifyContent: "center", width: 26, height: 26, borderRadius: TOKENS.rSm, background: `${TOKENS.brand}12`, color: TOKENS.brand }}>
              <FilterIcon size={13} strokeWidth={2} />
            </span>
            <span style={{ fontSize: 11, fontWeight: 800, textTransform: "uppercase", letterSpacing: "0.08em", color: TOKENS.ink3 }}>Filters</span>
          </div>

          <FilterSelect label="Database" value={filterSource} onChange={setFilterSource} placeholder="All Databases"
            options={filterOptions.sources.map((o) => ({ value: o, label: o }))} minWidth={150} />
          <FilterSelect label="Auditor" value={filterAuditor} onChange={setFilterAuditor}
            placeholder={`All Auditors${filterOptions.auditors.length ? ` (${filterOptions.auditors.length})` : ""}`}
            options={filterOptions.auditors.map((o) => ({ value: o, label: o }))} minWidth={220}
            leadingIcon={<UserCheck size={11} strokeWidth={2.4} />} />
          <FilterSelect label="Year" value={filterYear} onChange={setFilterYear} placeholder="All Years"
            options={filterOptions.years.map((o) => ({ value: o, label: o }))} minWidth={110}
            leadingIcon={<Calendar size={11} strokeWidth={2.4} />} />
          <FilterSelect label="Month" value={filterMonthNum} onChange={setFilterMonthNum} placeholder="All Months"
            options={filterOptions.monthNums.map((o) => ({ value: o, label: monthName(parseInt(o, 10) - 1) }))} minWidth={130}
            leadingIcon={<Calendar size={11} strokeWidth={2.4} />} />
          <FilterSelect label="Audit Type" value={filterType} onChange={setFilterType} placeholder="All Types"
            options={[{ value: "Initial", label: "Initial" }, { value: "Surveillance", label: "Surveillance" }, { value: "Recertification", label: "Recertification" }]} minWidth={150}
            leadingIcon={<ClipboardList size={11} strokeWidth={2.4} />} />
          <FilterSelect label="Report Status" value={filterStatus} onChange={setFilterStatus} placeholder="Any Status"
            options={[
              { value: "s1_missing", label: "Stage 1 missing" },
              { value: "s2_missing", label: "Stage 2 missing" },
              { value: "s1_uploaded", label: "Stage 1 uploaded" },
              { value: "s2_uploaded", label: "Stage 2 uploaded" },
            ]} minWidth={160} />

          {hasActiveFilter && (
            <button onClick={clearFilters}
              style={{ padding: "8px 14px", background: "transparent", border: `1px solid ${TOKENS.line}`, borderRadius: TOKENS.rSm, cursor: "pointer", fontSize: 12, fontWeight: 700, color: TOKENS.ink3, height: 36, display: "inline-flex", alignItems: "center", gap: 5 }}>
              <XIcon size={11} strokeWidth={2.5} />Clear
            </button>
          )}

          <div style={{ marginLeft: "auto", display: "flex", alignItems: "center", gap: 6, paddingBottom: 8 }}>
            <span style={{ fontSize: 10, fontWeight: 700, color: TOKENS.ink5, textTransform: "uppercase", letterSpacing: 0.5 }}>Audits</span>
            <span style={{ fontSize: 13, fontFamily: "'IBM Plex Mono', monospace", fontWeight: 800, color: hasActiveFilter ? TOKENS.brand : TOKENS.ink2 }}>{fmt(total)}</span>
          </div>
        </div>
      </div>

      {/* Report body */}
      <div ref={reportRef} style={{ maxWidth: 1320, margin: "0 auto", display: "grid", gap: 28 }}>
        {/* Summary + aging */}
        <section style={{ background: TOKENS.surface, border: `1px solid ${TOKENS.line}`, borderRadius: TOKENS.rLg, padding: "22px 24px", boxShadow: TOKENS.shadow }}>
          <SectionHeader number="01" title="Summary" description={`Audit events${hasActiveFilter ? " (filtered)" : ""} · generated ${todayStr()}`} />
          <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 14, marginBottom: 22 }}>
            <KpiCard label="Total audits" value={fmt(summary?.total_audits ?? 0)} icon={<ClipboardList size={14} strokeWidth={2} />} accent={TOKENS.brand} />
            <KpiCard label="Stage 1 missing" value={fmt(summary?.stage1_missing ?? 0)} icon={<FileX2 size={14} strokeWidth={2} />} accent={TOKENS.warning} />
            <KpiCard label="Stage 2 missing" value={fmt(summary?.stage2_missing ?? 0)} icon={<FileX2 size={14} strokeWidth={2} />} accent={TOKENS.danger} />
          </div>

          <SectionHeader number="02" title="Aging analysis" description="Days since the audit date while the Stage 2 report is still outstanding." />
          <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 14 }}>
            <AgingCard label="0–30 days" hint="Current" value={aging?.bucket_0_30 ?? 0} color={TOKENS.success} />
            <AgingCard label="31–60 days" hint="Needs attention" value={aging?.bucket_31_60 ?? 0} color={TOKENS.warning} />
            <AgingCard label="61–90 days" hint="Overdue" value={aging?.bucket_61_90 ?? 0} color="#ea580c" />
            <AgingCard label="90+ days" hint="Critical" value={aging?.bucket_90_plus ?? 0} color={TOKENS.danger} />
          </div>
        </section>

        {/* Data table */}
        <section style={{ background: TOKENS.surface, border: `1px solid ${TOKENS.line}`, borderRadius: TOKENS.rLg, boxShadow: TOKENS.shadow, overflow: "hidden" }}>
          <div style={{ padding: "18px 24px 12px" }}>
            <SectionHeader number="03" title="Audit detail" description="One row per audit event. Click an uploaded stage to open its report file." />
          </div>

          {rows.length === 0 ? (
            <div style={{ padding: "8px 24px 28px" }}>
              <EmptyState icon={<ClipboardList size={20} strokeWidth={2} />} title="No audits match" text="Try clearing or changing the filters above." />
            </div>
          ) : (
            <div style={{ overflowX: "auto" }}>
              <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 12.5 }}>
                <thead>
                  <tr style={{ background: TOKENS.line2 }}>
                    <Th onClick={() => toggleSort("client")} active={sort === "client"} dir={dir}>Client</Th>
                    <Th onClick={() => toggleSort("audit_date")} active={sort === "audit_date"} dir={dir}>Audit Date</Th>
                    <Th onClick={() => toggleSort("type")} active={sort === "type"} dir={dir}>Type</Th>
                    <Th center>Stage 1</Th>
                    <Th center>Stage 2</Th>
                    <Th onClick={() => toggleSort("auditor")} active={sort === "auditor"} dir={dir}>Auditor</Th>
                    <Th center>DB</Th>
                    <Th center>Age</Th>
                  </tr>
                </thead>
                <tbody>
                  {rows.map((r, i) => (
                    <tr key={`${r.source}-${r.table}-${r.record_id}-${r.audit_type}`}
                      style={{ borderTop: `1px solid ${TOKENS.line2}`, borderLeft: `3px solid ${SOURCE_COLOR[r.source] || TOKENS.ink5}`, background: i % 2 ? "#fcfdfe" : TOKENS.surface }}>
                      <td style={tdCss}>
                        <span style={{ fontWeight: 600, color: TOKENS.ink2 }}>{r.client_name || "—"}</span>
                      </td>
                      <td style={tdCss}>{fmtDate(r.audit_date)}</td>
                      <td style={tdCss}>
                        <span style={{ display: "inline-flex", alignItems: "center", gap: 5, padding: "2px 9px", borderRadius: 999, fontSize: 11, fontWeight: 700, background: `${TYPE_COLOR[r.audit_type]}15`, color: TYPE_COLOR[r.audit_type] }}>
                          {r.audit_type}
                        </span>
                      </td>
                      <td style={{ ...tdCss, textAlign: "center" }}><StageCell uploaded={r.stage1_uploaded} onOpen={() => openFile(r, 1)} /></td>
                      <td style={{ ...tdCss, textAlign: "center" }}><StageCell uploaded={r.stage2_uploaded} onOpen={() => openFile(r, 2)} /></td>
                      <td style={tdCss}>{r.auditor}</td>
                      <td style={{ ...tdCss, textAlign: "center" }}>
                        <span style={{ padding: "2px 8px", borderRadius: 999, fontSize: 11, fontWeight: 700, background: `${SOURCE_COLOR[r.source]}15`, color: SOURCE_COLOR[r.source] }}>{r.source}</span>
                      </td>
                      <td style={{ ...tdCss, textAlign: "center" }}>
                        {r.age_days == null ? "—" : (
                          <span style={{ display: "inline-flex", alignItems: "center", gap: 4, color: ageColor(r.age_days, r.stage2_uploaded) }}>
                            <Clock size={11} strokeWidth={2.2} />{r.age_days}d
                          </span>
                        )}
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          )}

          {/* Pagination */}
          {rows.length > 0 && (
            <div className="no-print" style={{ display: "flex", justifyContent: "space-between", alignItems: "center", padding: "14px 24px", borderTop: `1px solid ${TOKENS.line2}` }}>
              <span style={{ fontSize: 12, color: TOKENS.ink4 }}>
                Page {data?.page ?? 1} of {totalPages} · {fmt(total)} audits
              </span>
              <div style={{ display: "flex", gap: 8 }}>
                <PagerButton disabled={(data?.page ?? 1) <= 1} onClick={() => setPage((p) => Math.max(1, p - 1))}><ChevronLeft size={14} strokeWidth={2.4} />Prev</PagerButton>
                <PagerButton disabled={(data?.page ?? 1) >= totalPages} onClick={() => setPage((p) => Math.min(totalPages, p + 1))}>Next<ChevronRight size={14} strokeWidth={2.4} /></PagerButton>
              </div>
            </div>
          )}
        </section>
      </div>
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════════
// helpers + sub-components
// ═══════════════════════════════════════════════════════════════════════
function escapeHtml(s: string): string {
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
}
function ageColor(days: number, stage2Uploaded: boolean): string {
  if (stage2Uploaded) return TOKENS.ink4;
  if (days <= 30) return TOKENS.success;
  if (days <= 60) return TOKENS.warning;
  if (days <= 90) return "#ea580c";
  return TOKENS.danger;
}

const tdCss: React.CSSProperties = { padding: "11px 14px", color: TOKENS.ink3, verticalAlign: "middle", whiteSpace: "nowrap" };

function Th({ children, onClick, active, dir, center }: { children: React.ReactNode; onClick?: () => void; active?: boolean; dir?: "asc" | "desc"; center?: boolean; }) {
  return (
    <th onClick={onClick}
      style={{ padding: "11px 14px", textAlign: center ? "center" : "left", fontSize: 10.5, fontWeight: 800, textTransform: "uppercase", letterSpacing: "0.05em", color: active ? TOKENS.brand : TOKENS.ink4, cursor: onClick ? "pointer" : "default", userSelect: "none", whiteSpace: "nowrap" }}>
      <span style={{ display: "inline-flex", alignItems: "center", gap: 4, justifyContent: center ? "center" : "flex-start" }}>
        {children}
        {onClick && <ArrowUpDown size={11} strokeWidth={2.2} style={{ opacity: active ? 1 : 0.4 }} />}
      </span>
    </th>
  );
}

function StageCell({ uploaded, onOpen }: { uploaded: boolean; onOpen: () => void }) {
  if (uploaded) {
    return (
      <button onClick={onOpen} title="Open report"
        style={{ display: "inline-flex", alignItems: "center", gap: 5, padding: "4px 10px", borderRadius: TOKENS.rSm, border: `1px solid ${TOKENS.success}40`, background: `${TOKENS.success}14`, color: TOKENS.success, fontSize: 11, fontWeight: 700, cursor: "pointer" }}>
        <FileCheck2 size={12} strokeWidth={2.2} />Uploaded<ExternalLink size={10} strokeWidth={2.2} style={{ opacity: 0.7 }} />
      </button>
    );
  }
  return (
    <span style={{ display: "inline-flex", alignItems: "center", gap: 5, padding: "4px 10px", borderRadius: TOKENS.rSm, border: `1px solid ${TOKENS.danger}33`, background: `${TOKENS.danger}10`, color: TOKENS.danger, fontSize: 11, fontWeight: 700 }}>
      <FileX2 size={12} strokeWidth={2.2} />Missing
    </span>
  );
}

function AgingCard({ label, hint, value, color }: { label: string; hint: string; value: number; color: string }) {
  return (
    <div style={{ background: TOKENS.surface, border: `1px solid ${TOKENS.line}`, borderRadius: TOKENS.rMd, padding: "14px 16px", position: "relative", overflow: "hidden" }}>
      <div style={{ position: "absolute", top: 0, left: 0, right: 0, height: 3, background: color }} />
      <div style={{ fontSize: 11, fontWeight: 700, color: TOKENS.ink4, textTransform: "uppercase", letterSpacing: "0.05em" }}>{label}</div>
      <div style={{ fontSize: 26, fontWeight: 800, fontFamily: "'IBM Plex Mono', monospace", color: TOKENS.ink, lineHeight: 1.1, marginTop: 6 }}>{fmt(value)}</div>
      <div style={{ fontSize: 11, fontWeight: 600, color, marginTop: 2 }}>{hint}</div>
    </div>
  );
}

function FilterSelect({ label, value, onChange, options, placeholder, minWidth = 140, leadingIcon }: { label: string; value: string; onChange: (v: string) => void; options: { value: string; label: string }[]; placeholder: string; minWidth?: number; leadingIcon?: React.ReactNode; }) {
  const isActive = !!value;
  return (
    <label style={{ display: "inline-flex", flexDirection: "column", gap: 4 }}>
      <span style={{ fontSize: 9, fontWeight: 800, color: TOKENS.ink5, textTransform: "uppercase", letterSpacing: "0.08em", display: "inline-flex", alignItems: "center", gap: 4 }}>
        {leadingIcon && <span style={{ color: isActive ? TOKENS.brand : TOKENS.ink5, display: "inline-flex" }}>{leadingIcon}</span>}
        {label}
      </span>
      <select value={value} onChange={(e) => onChange(e.target.value)}
        style={{ appearance: "none", WebkitAppearance: "none", MozAppearance: "none", backgroundColor: isActive ? `${TOKENS.brand}10` : TOKENS.surface, backgroundImage: `url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='${encodeURIComponent(isActive ? TOKENS.brand : TOKENS.ink4)}' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>")`, backgroundRepeat: "no-repeat", backgroundPosition: "right 9px center", backgroundSize: "12px 12px", border: `1px solid ${isActive ? TOKENS.brand : TOKENS.line}`, borderRadius: TOKENS.rSm, padding: "8px 30px 8px 11px", fontSize: 12, fontWeight: 600, color: isActive ? TOKENS.brand : TOKENS.ink2, cursor: "pointer", minWidth, maxWidth: 280, height: 36, outline: "none", textOverflow: "ellipsis", transition: "all 0.15s" }}>
        <option value="">{placeholder}</option>
        {options.length === 0 && <option disabled value="">— no options —</option>}
        {options.map((opt) => (<option key={opt.value} value={opt.value}>{opt.label}</option>))}
      </select>
    </label>
  );
}

function ActionButton({ children, onClick, variant, disabled }: { children: React.ReactNode; onClick: () => void; variant: "primary" | "secondary"; disabled?: boolean; }) {
  const isPrimary = variant === "primary";
  return (
    <button onClick={onClick} disabled={disabled}
      style={{ display: "inline-flex", alignItems: "center", gap: 6, padding: "9px 18px", background: isPrimary ? `linear-gradient(135deg, ${TOKENS.brand} 0%, ${TOKENS.brandLight} 100%)` : TOKENS.surface, color: isPrimary ? "#fff" : TOKENS.ink2, border: isPrimary ? "none" : `1px solid ${TOKENS.line}`, borderRadius: TOKENS.rMd, fontSize: 13, fontWeight: 700, cursor: disabled ? "wait" : "pointer", boxShadow: isPrimary ? "0 4px 12px rgba(15,118,110,0.25)" : TOKENS.shadow, opacity: disabled ? 0.6 : 1, whiteSpace: "nowrap" }}>
      {children}
    </button>
  );
}

function PagerButton({ children, onClick, disabled }: { children: React.ReactNode; onClick: () => void; disabled?: boolean }) {
  return (
    <button onClick={onClick} disabled={disabled}
      style={{ display: "inline-flex", alignItems: "center", gap: 4, padding: "7px 12px", background: TOKENS.surface, border: `1px solid ${TOKENS.line}`, borderRadius: TOKENS.rSm, fontSize: 12, fontWeight: 700, color: disabled ? TOKENS.ink5 : TOKENS.ink3, cursor: disabled ? "not-allowed" : "pointer", opacity: disabled ? 0.5 : 1 }}>
      {children}
    </button>
  );
}

function SectionHeader({ number, title, description, accent }: { number: string; title: string; description: string; accent?: string; }) {
  return (
    <div style={{ marginBottom: 18 }}>
      <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 4 }}>
        <span style={{ display: "inline-flex", alignItems: "center", justifyContent: "center", width: 28, height: 28, background: accent ?? TOKENS.brand, color: "#fff", borderRadius: TOKENS.rSm, fontSize: 11, fontWeight: 800, fontFamily: "'IBM Plex Mono', monospace" }}>{number}</span>
        <h2 style={{ margin: 0, fontSize: 17, fontWeight: 800, color: TOKENS.ink, letterSpacing: "-0.01em" }}>{title}</h2>
      </div>
      <p style={{ margin: "0 0 0 40px", fontSize: 12, color: TOKENS.ink4, lineHeight: 1.4 }}>{description}</p>
    </div>
  );
}

function KpiCard({ label, value, icon, accent }: { label: string; value: string; icon: React.ReactNode; accent: string; }) {
  return (
    <div style={{ background: TOKENS.surface, border: `1px solid ${TOKENS.line}`, borderRadius: TOKENS.rMd, padding: "16px 18px", position: "relative", overflow: "hidden", minHeight: 92, display: "flex", flexDirection: "column", justifyContent: "space-between" }}>
      <div style={{ position: "absolute", top: 0, left: 0, right: 0, height: 3, background: accent }} />
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start" }}>
        <span style={{ fontSize: 11, fontWeight: 700, color: TOKENS.ink4, textTransform: "uppercase", letterSpacing: "0.06em" }}>{label}</span>
        <span style={{ color: accent, display: "inline-flex" }}>{icon}</span>
      </div>
      <div style={{ fontSize: 26, fontWeight: 800, fontFamily: "'IBM Plex Mono', monospace", lineHeight: 1.05, color: TOKENS.ink }}>{value}</div>
    </div>
  );
}

function EmptyState({ icon, title, text }: { icon: React.ReactNode; title: string; text: string; }) {
  return (
    <div style={{ background: TOKENS.surface, border: `1px dashed ${TOKENS.line}`, borderRadius: TOKENS.rMd, padding: "28px 20px", textAlign: "center" }}>
      <div style={{ width: 44, height: 44, borderRadius: "50%", background: TOKENS.ink5, color: "#fff", display: "inline-flex", alignItems: "center", justifyContent: "center", marginBottom: 10 }}>{icon}</div>
      <div style={{ fontSize: 14, fontWeight: 700, color: TOKENS.ink2 }}>{title}</div>
      <div style={{ fontSize: 12, color: TOKENS.ink4, marginTop: 4 }}>{text}</div>
    </div>
  );
}

function LoadingState() {
  return (
    <div style={{ minHeight: "60vh", display: "flex", alignItems: "center", justifyContent: "center", background: TOKENS.bg }}>
      <div style={{ textAlign: "center" }}>
        <div style={{ width: 44, height: 44, border: `3px solid ${TOKENS.line}`, borderTopColor: TOKENS.brand, borderRadius: "50%", animation: "ad-spin 0.7s linear infinite", margin: "0 auto 14px" }} />
        <div style={{ fontSize: 13, fontWeight: 600, color: TOKENS.ink3 }}>Loading detail report</div>
        <style>{`@keyframes ad-spin { to { transform: rotate(360deg); } }`}</style>
      </div>
    </div>
  );
}

function ErrorState({ message, onRetry }: { message: string; onRetry: () => void }) {
  return (
    <div style={{ minHeight: "60vh", display: "flex", alignItems: "center", justifyContent: "center", padding: 40 }}>
      <div style={{ maxWidth: 400, textAlign: "center", background: TOKENS.surface, padding: 32, borderRadius: TOKENS.rLg, boxShadow: TOKENS.shadow }}>
        <AlertTriangle size={36} strokeWidth={2} color={TOKENS.danger} style={{ marginBottom: 8 }} />
        <h3 style={{ margin: "0 0 6px", color: TOKENS.ink, fontSize: 16 }}>Failed to load report</h3>
        <p style={{ color: TOKENS.ink4, fontSize: 13, margin: "0 0 16px" }}>{message}</p>
        <ActionButton onClick={onRetry} variant="primary">Try again</ActionButton>
      </div>
    </div>
  );
}
