"use client";

import React, { useEffect, useMemo, useState } from "react";
import { useAuth } from "@/hooks/useAuth";
import { apiFetch, API_BASE } from "@/lib/api";
import { downloadCsv, emailReportPdf, downloadReportPdf } from "@/lib/export";

// Types for report selection
const REPORTS = [
  { key: "payments", label: "Payments between dates" },
  { key: "attendees", label: "Attendees per event (grouped by option)" },
  { key: "regTypes", label: "Registration type counts" },
  { key: "usage", label: "Ticket usage summary" },
  { key: "revenue", label: "Revenue summary (by method)" },
  { key: "revenueDetailed", label: "Revenue detailed" },
  { key: "regStatus", label: "Registration status breakdown" }
] as const;

type ReportKey = typeof REPORTS[number]["key"];

// Simple MultiSelect control used in filters
function MultiSelect({ options, value, onChange }: { options: { value: string; label: string }[]; value: string[]; onChange: (v: string[]) => void }) {
  const toggle = (v: string) => {
    const set = new Set(value);
    if (set.has(v)) set.delete(v); else set.add(v);
    onChange(Array.from(set));
  };
  return (
    <div className="flex flex-wrap gap-2">
      {options.map(opt => (
        <label key={opt.value} className={"px-2 py-1 text-xs rounded border cursor-pointer " + (value.includes(opt.value) ? "bg-indigo-600 text-white border-indigo-600" : "bg-white text-gray-800 border-gray-200") }>
          <input type="checkbox" className="hidden" checked={value.includes(opt.value)} onChange={() => toggle(opt.value)} />
          {opt.label}
        </label>
      ))}
    </div>
  );
}

export default function ReportsV2() {
  const { token, user } = useAuth();
  const role = user?.role || "user";
  const canView = role === "admin" || role === "supervisor";

  // Events list
  const [events, setEvents] = useState<any[]>([]);
  const [loadingEvents, setLoadingEvents] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [showPastEvents, setShowPastEvents] = useState(false);
  const [showCancelledEvents, setShowCancelledEvents] = useState(false);

  const filteredEvents = useMemo(() => {
    const now = new Date();

    return events.filter(ev => {
      const isPast = ev.endDate && new Date(ev.endDate) < now;
      const isCancelled = ev.status === "cancelled";

      if (!showPastEvents && isPast) return false;
      if (!showCancelledEvents && isCancelled) return false;

      return true;
    });
  }, [events, showPastEvents, showCancelledEvents]);


  useEffect(() => {
    (async () => {
      try {
        setLoadingEvents(true);
        const isAdmin = role === "admin";
        const isSupervisor = role === "supervisor";
        const evPath = (isAdmin ? "/api/events/admin/all" : "/api/events");
        const evs = await apiFetch<any[]>(evPath, (isAdmin || isSupervisor) ? { authToken: token || undefined } : undefined);
        setEvents(Array.isArray(evs) ? evs : []);
      } catch (e: any) {
        setError(e?.message || "Failed to load events");
      } finally {
        setLoadingEvents(false);
      }
    })();
  }, [token, role]);

  // General state for UI flow
  const [report, setReport] = useState<ReportKey>("payments");
  const [ready, setReady] = useState(false); // toggled by View button
  const [busy, setBusy] = useState(false);

  // Filters per report
  // Payments
  const [payFrom, setPayFrom] = useState<string>("");
  const [payTo, setPayTo] = useState<string>("");
  const [payEvents, setPayEvents] = useState<string[]>([]);

  // Attendees
  const [attEventId, setAttEventId] = useState<string>("");
  const [attIncludeCancelled, setAttIncludeCancelled] = useState(false);

  // Registration type counts
  const [regTypeEvents, setRegTypeEvents] = useState<string[]>([]);

  // Ticket usage summary
  const [usageEvents, setUsageEvents] = useState<string[]>([]);

  // Revenue summary
  const [revEvents, setRevEvents] = useState<string[]>([]);
  // Revenue detailed
  const [revDetEvents, setRevDetEvents] = useState<string[]>([]);

  // Registration status breakdown
  const [statusEvents, setStatusEvents] = useState<string[]>([]);
  const [statusIncludeCancelled, setStatusIncludeCancelled] = useState<boolean>(true);

  // Data stores
  const [paymentsByEvent, setPaymentsByEvent] = useState<Record<string, any[]>>({});
  const [registrationsByEvent, setRegistrationsByEvent] = useState<Record<string, any[]>>({});
  const [ticketUsage, setTicketUsage] = useState<Record<string, { used: number; unused: number }>>({});

  // Initialize default selections when events load
  useEffect(() => {
    if (filteredEvents.length === 0) return;
    const first1 = events.slice(0, 1).map((e: any) => e.id);
    if (payEvents.length === 0) setPayEvents(first1);
    if (regTypeEvents.length === 0) setRegTypeEvents(first1);
    if (usageEvents.length === 0) setUsageEvents(first1);
    if (revEvents.length === 0) setRevEvents(first1);
    if (revDetEvents.length === 0) setRevDetEvents(first1);
    if (statusEvents.length === 0) setStatusEvents(first1);
    if (!attEventId) setAttEventId(events[0].id);
  }, [filteredEvents]);

  // Loaders
  const loadPayments = async (eventIds: string[]) => {
    if (!token || !eventIds.length) return {} as Record<string, any[]>;
    const byEv: Record<string, any[]> = {};
    for (const id of eventIds) {
      try {
        const list = await apiFetch<any[]>(`/api/payments/event/${encodeURIComponent(id)}`, { authToken: token });
        byEv[id] = Array.isArray(list) ? list : [];
      } catch (e) {
        byEv[id] = [];
      }
    }
    return byEv;
  };

  const loadRegistrations = async (eventIds: string[]) => {
    if (!token || !eventIds.length) return {} as Record<string, any[]>;
    const byEv: Record<string, any[]> = {};
    for (const id of eventIds) {
      try {
        const list = await apiFetch<any[]>(`/api/registrations/event/${encodeURIComponent(id)}`, { authToken: token });
        byEv[id] = Array.isArray(list) ? list : [];
      } catch (e) {
        byEv[id] = [];
      }
    }
    return byEv;
  };

  const loadTicketUsage = async (eventIds: string[]) => {
    if (!token || !eventIds.length) return {} as Record<string, { used: number; unused: number }>;
    const result: Record<string, { used: number; unused: number }> = {};
    for (const id of eventIds) {
      try {
        const tickets = await apiFetch<any[]>(`/api/tickets/event/${encodeURIComponent(id)}`, { authToken: token });
        result[id] = { used: tickets.filter(t => t.isUsed).length, unused: tickets.filter(t => !t.isUsed).length };
      } catch (e) {
        result[id] = { used: 0, unused: 0 };
      }
    }
    return result;
  };

  // View handler according to selected report
  const onView = async () => {
    setBusy(true);
    setReady(false);
    try {
      if (report === "payments") {
        const byEv = await loadPayments(payEvents);
        setPaymentsByEvent(byEv);
      } else if (report === "attendees") {
        const byEv = await loadRegistrations([attEventId]);
        setRegistrationsByEvent(byEv);
      } else if (report === "regTypes") {
        const byEv = await loadRegistrations(regTypeEvents);
        setRegistrationsByEvent(byEv);
      } else if (report === "usage") {
        const usage = await loadTicketUsage(usageEvents);
        setTicketUsage(usage);
      } else if (report === "revenue") {
        const byEv = await loadPayments(revEvents);
        setPaymentsByEvent(byEv);
      } else if (report === "revenueDetailed") {
        const [byEvP, byEvR] = await Promise.all([
          loadPayments(revDetEvents),
          loadRegistrations(revDetEvents)
        ]);
        setPaymentsByEvent(byEvP);
        setRegistrationsByEvent(byEvR);
      } else if (report === "regStatus") {
        const byEv = await loadRegistrations(statusEvents);
        setRegistrationsByEvent(byEv);
      }
      setReady(true);
    } finally {
      setBusy(false);
    }
  };

  // Payment method label mapping
  const getPaymentMethod = (p: any) => {
    try {
      if (p?.externalId) return "Yoco Portal";
    } catch {}
    return p?.method || "Unknown";
  };

  // Derived rows for payments (apply date filter)
  const paymentRows = useMemo(() => {
    if (report !== "payments") return [] as any[];
    const df = payFrom ? new Date(payFrom).getTime() : null;
    const dt = payTo ? new Date(payTo).getTime() : null;
    const rows: { eventId: string; eventTitle: string; userName: string; userEmail?: string; amount: number; method: string; isDonation?: boolean; createdAt: string }[] = [];
    for (const evId of Object.keys(paymentsByEvent)) {
      const ev = filteredEvents.find(e => e.id === evId);
      const evTitle = ev?.title || evId;
      for (const p of paymentsByEvent[evId] || []) {
        const t = new Date(p.createdAt).getTime();
        if ((df && t < df) || (dt && t > dt)) continue;
        // Reporting rule: if donation, attribute to payer; otherwise attribute to the registration's assigned user
        const u = p.isDonation ? (p.user || {}) : ((p.registration?.user || p.user) || {});
        rows.push({
          eventId: evId,
          eventTitle: evTitle,
          userName: u?.name || u?.email || p.userId || "User",
          userEmail: u?.email,
          amount: p.amount,
          method: getPaymentMethod(p),
          isDonation: p.isDonation,
          createdAt: p.createdAt,
        });
      }
    }
    rows.sort((a,b) => (a.eventTitle.localeCompare(b.eventTitle) || (a.userName || '').localeCompare(b.userName || '') || new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()));
    return rows;
  }, [report, paymentsByEvent, payFrom, payTo, filteredEvents]);

  // Derived rows for revenue detailed (apply date filter)
  // Outstanding by registration respecting early-bird tiers and last payment lock-in
  const outstandingByReg = useMemo(() => {
    const map = new Map<string, number>();
    // Build quick index of payments per registration from all payments loaded per event
    const paidByReg = new Map<string, number>();
    const lastPaymentAtByReg = new Map<string, Date | null>();
    Object.keys(paymentsByEvent).forEach(evId => {
      (paymentsByEvent[evId] || []).forEach((p: any) => {
        if (p.registrationId) {
          const regId = p.registrationId;
          paidByReg.set(regId, (paidByReg.get(regId) || 0) + (p.amount || 0));
          const t = new Date(p.createdAt).getTime();
          const prev = lastPaymentAtByReg.get(regId)?.getTime() || -Infinity;
          if (t > prev) lastPaymentAtByReg.set(regId, new Date(t));
        }
      });
    });
    // Early-bird helper (reference = lastPaymentAt if any; else now)
    const effectiveUnit = (eo: any, referenceTime: any, atTime: Date) => {
      const base = Number(eo?.price || 0);
      const tiers = Array.isArray(eo?.earlyBirdTiers) ? eo.earlyBirdTiers.slice() : [];
      if (tiers.length === 0) return base;
      const t = atTime ? new Date(atTime) : new Date();
      const ref = referenceTime ? new Date(referenceTime) : t;
      const applicable = tiers
        .map((x: any) => ({ ...x, deadline: new Date(x.deadline) }))
        .filter((x: any) => (ref < x.deadline) && (t < x.deadline))
        .sort((a: any, b: any) => a.deadline.getTime() - b.deadline.getTime() || (a.order||0) - (b.order||0) || a.price - b.price);
      if (applicable.length === 0) return base;
      const price = Number(applicable[0].price);
      return (price >= 0) ? price : base;
    };
    const now = new Date();
    // For each registration in loaded events, compute due and outstanding with lock-in rule
    Object.keys(registrationsByEvent).forEach(evId => {
      (registrationsByEvent[evId] || []).forEach((r: any) => {
        const lastAt = lastPaymentAtByReg.get(r.id) || null;
        const dueNow = (r.registrationOptions || []).reduce((sum: number, ro: any) => sum + effectiveUnit(ro.eventOption, lastAt, now) * (ro.quantity || 0), 0);
        const paid = paidByReg.get(r.id) || 0;
        let outstanding = Math.max(dueNow - paid, 0);
        if (lastAt) {
          const dueAtLast = (r.registrationOptions || []).reduce((sum: number, ro: any) => sum + effectiveUnit(ro.eventOption, lastAt, lastAt) * (ro.quantity || 0), 0);
          if (paid >= dueAtLast) outstanding = 0;
        }
        map.set(r.id, outstanding);
      });
    });
    return map;
  }, [paymentsByEvent, registrationsByEvent]);

  // Build payment totals per registration (all payments, no date filter)
  const paidByReg = useMemo(() => {
    const map = new Map<string, number>();
    Object.keys(paymentsByEvent).forEach(evId => {
      (paymentsByEvent[evId] || []).forEach((p: any) => {
        if (p.registrationId) {
          map.set(p.registrationId, (map.get(p.registrationId) || 0) + (p.amount || 0));
        }
      });
    });
    return map;
  }, [paymentsByEvent]);

  const revDetRows = useMemo(() => {
    if (report !== "revenueDetailed") return [] as any[];
    const df = payFrom ? new Date(payFrom).getTime() : null;
    const dt = payTo ? new Date(payTo).getTime() : null;
    type Row = { eventId: string; eventTitle: string; userName: string; userEmail?: string; totalPaid: number; status?: string; registrationId: string; outstanding: number };
    const rows: Row[] = [];
    // Iterate registrations to include those with zero payments and show each once
    Object.keys(registrationsByEvent).forEach(evId => {
      const ev = filteredEvents.find(e => e.id === evId);
      const evTitle = ev?.title || evId;
      (registrationsByEvent[evId] || []).forEach((r: any) => {
        const created = r.createdAt ? new Date(r.createdAt).getTime() : null;
        if (df && created && created < df) return;
        if (dt && created && created > dt) return;
        const totalPaid = paidByReg.get(r.id) || 0;
        const outstanding = outstandingByReg.get(r.id) || 0;
        rows.push({
          eventId: evId,
          eventTitle: evTitle,
          userName: r.user?.name || r.userId || "User",
          userEmail: r.user?.email,
          totalPaid,
          status: r.status,
          registrationId: r.id,
          outstanding,
        });
      });
    });
    rows.sort((a,b) => (a.eventTitle.localeCompare(b.eventTitle) || (a.userName || '').localeCompare(b.userName || '')));
    return rows;
  }, [report, registrationsByEvent, filteredEvents, payFrom, payTo, paidByReg, outstandingByReg]);

  // Derived for attendees (layered) for a single event
  const attendeesLayer = useMemo(() => {
    if (report !== "attendees") return { eventTitle: "", options: [] as any[] };
    const regs = registrationsByEvent[attEventId] || [];
    const ev = filteredEvents.find(e => e.id === attEventId);
    // Option -> users
    const byOption = new Map<string, { optionName: string; users: { name: string; email?: string; status: string; qty: number }[] }>();
    for (const r of regs) {
      if (!attIncludeCancelled && r.status === "cancelled") continue;

      const baseUser = {
        name: r.user?.name || r.userId,
        email: r.user?.email,
        status: r.status,
      };

      const options = (r.registrationOptions || []);

      if (options.length === 0) {
        const key = "(No option)";
        if (!byOption.has(key)) byOption.set(key, { optionName: key, users: [] });

        byOption.get(key)!.users.push({
          ...baseUser,
          qty: 0
        });
      }
      else {
        for (const ro of options) {
          const key = ro.eventOption?.name || "Option";

          if (!byOption.has(key)) {
            byOption.set(key, { optionName: key, users: [] });
          }

          byOption.get(key)!.users.push({
            ...baseUser,
            qty: ro.quantity || 0
          });
        }
      }
    }
    const options = Array.from(byOption.values()).sort((a,b) => a.optionName.localeCompare(b.optionName));
    options.forEach(o => o.users.sort((a,b) => (a.name || '').localeCompare(b.name || '')));
    return { eventTitle: ev?.title || attEventId, options };
  }, [report, registrationsByEvent, attEventId, attIncludeCancelled, filteredEvents]);

  // Derived for reg type counts
  const regTypeRows = useMemo(() => {
    if (report !== "regTypes") return [] as { eventTitle: string; type: string; qty: number }[];
    const rows: { eventTitle: string; type: string; qty: number }[] = [];
    for (const evId of Object.keys(registrationsByEvent)) {
      const ev = filteredEvents.find(e => e.id === evId);
      const title = ev?.title || evId;
      const regs = registrationsByEvent[evId] || [];
      const counts: Record<string, number> = {};
      for (const r of regs) {
        if (r.status === "cancelled") continue;
        for (const ro of (r.registrationOptions || [])) {
          const name = ro.eventOption?.name || "Option";
          counts[name] = (counts[name] || 0) + (ro.quantity || 0);
        }
      }
      for (const type of Object.keys(counts)) rows.push({ eventTitle: title, type, qty: counts[type] });
    }
    rows.sort((a,b) => (a.eventTitle.localeCompare(b.eventTitle) || a.type.localeCompare(b.type)));
    return rows;
  }, [report, registrationsByEvent, filteredEvents]);

  // Export/print/email handlers per report
  const doExportCsv = () => {
    if (report === "payments") {
      return downloadCsv(`payments_${new Date().toISOString().slice(0,10)}`, paymentRows.map(r => ({
        Event: r.eventTitle,
        User: r.userName,
        Email: r.userEmail || "",
        Amount: r.amount,
        Method: r.method,
        Donation: r.isDonation ? "Yes" : "No",
        Date: new Date(r.createdAt).toLocaleString(),
      })));
    }
    if (report === "attendees") {
      const rows: any[] = [];
      attendeesLayer.options.forEach(opt => {
        opt.users.forEach((u: any) => rows.push({ Event: attendeesLayer.eventTitle, Option: opt.optionName, Name: u.name, Email: u.email || "", Status: u.status, Qty: u.Qty }));
      });
      return downloadCsv(`attendees_${attEventId}_${new Date().toISOString().slice(0,10)}`, rows);
    }
    if (report === "regTypes") {
      return downloadCsv(`registration_types_${new Date().toISOString().slice(0,10)}`, regTypeRows.map(r => ({ Event: r.eventTitle, Type: r.type, Quantity: r.qty })));
    }
    if (report === "usage") {
      const rows: any[] = [];
      Object.keys(ticketUsage).forEach(evId => {
        const ev = filteredEvents.find(e => e.id === evId);
        rows.push({ Event: ev?.title || evId, Used: ticketUsage[evId].used, Unused: ticketUsage[evId].unused });
      });
      return downloadCsv(`ticket_usage_${new Date().toISOString().slice(0,10)}`, rows);
    }
    if (report === "revenue") {
      const df = payFrom ? new Date(payFrom).getTime() : null;
      const dt = payTo ? new Date(payTo).getTime() : null;
      const rows: any[] = [];
      Object.keys(paymentsByEvent).forEach(evId => {
        const ev = filteredEvents.find(e => e.id === evId);
        const list = (paymentsByEvent[evId] || []).filter((p: any) => {
          const t = new Date(p.createdAt).getTime();
          return !(df && t < df) && !(dt && t > dt);
        });
        const byMethod: Record<string, number> = {};
        let total = 0;
        list.forEach((p: any) => { const m = getPaymentMethod(p); byMethod[m] = (byMethod[m] || 0) + (p.amount || 0); total += (p.amount || 0); });
        if (list.length === 0) rows.push({ Event: ev?.title || evId, Method: '-', Amount: 0, Total: 0 });
        Object.keys(byMethod).forEach(m => rows.push({ Event: ev?.title || evId, Method: m, Amount: byMethod[m], Total: total }));
      });
      return downloadCsv(`revenue_${new Date().toISOString().slice(0,10)}`, rows);
    }
    if (report === "revenueDetailed") {
      return downloadCsv(`revenue_detailed_${new Date().toISOString().slice(0,10)}`, revDetRows.map((r: any) => ({
        Event: r.eventTitle,
        Name: r.userName,
        Email: r.userEmail || "",
        Status: r.status || "",
        TotalPaid: r.totalPaid,
        Outstanding: r.outstanding,
        RegistrationId: r.registrationId
      })));
    }
    if (report === "regStatus") {
      const rows: any[] = [];
      Object.keys(registrationsByEvent).forEach(evId => {
        const ev = filteredEvents.find(e => e.id === evId);
        const regs = registrationsByEvent[evId] || [];
        const counts: Record<string, number> = {};
        const statuses = ["pending","partial_paid","paid","cancelled"];
        statuses.forEach(s => counts[s] = 0);
        regs.forEach((r: any) => {
          if (!statusIncludeCancelled && r.status === 'cancelled') return;
          counts[r.status] = (counts[r.status] || 0) + 1;
        });
        rows.push({ Event: ev?.title || evId, Pending: counts["pending"] || 0, Partial: counts["partial_paid"] || 0, Paid: counts["paid"] || 0, Cancelled: counts["cancelled"] || 0 });
      });
      return downloadCsv(`registration_status_${new Date().toISOString().slice(0,10)}`, rows);
    }
  };

  const doPrintPdf = async () => {
    if (!token) { alert('Please login to export PDF'); return; }
    try {
      if (report === "payments") {
        await downloadReportPdf(API_BASE, token, {
          title: 'Payments Report',
          kind: 'table',
          orientation: 'portrait',
          table: {
            columns: ["Event","User","Email","Amount","Method","Donation","Date"],
            rows: paymentRows.map(r => [r.eventTitle, r.userName, r.userEmail || "", `R ${Number(r.amount).toFixed(2)}`, r.method, r.isDonation ? "Yes" : "No", new Date(r.createdAt).toLocaleString()])
          }
        });
        return;
      }
      if (report === "attendees") {
        // Build layered sections: each option -> list of strings (user line)
        const sections = attendeesLayer.options.map(opt => ({
          title: opt.optionName,
          items: opt.users.map((u: any) => `${u.name}${u.email ? ` (${u.email})` : ''} — ${u.status}${typeof u.qty==='number' ? ` • Qty: ${u.qty}` : ''}`)
        }));
        await downloadReportPdf(API_BASE, token, {
          title: 'Attendees Report',
          kind: 'layered',
          orientation: 'portrait',
          layered: { header: `Event: ${attendeesLayer.eventTitle}`, sections }
        });
        return;
      }
      if (report === "regTypes") {
        await downloadReportPdf(API_BASE, token, {
          title: 'Registration Types Report',
          kind: 'table',
          orientation: 'portrait',
          table: {
            columns: ["Event","Type","Quantity"],
            rows: regTypeRows.map(r => [r.eventTitle, r.type, String(r.qty)])
          }
        });
        return;
      }
      if (report === "usage") {
        const rows: (string|number)[][] = [];
        Object.keys(ticketUsage).forEach(evId => {
          const ev = filteredEvents.find(e => e.id === evId);
          const tu = ticketUsage[evId];
          rows.push([ev?.title || evId, tu.used, tu.unused]);
        });
        await downloadReportPdf(API_BASE, token, {
          title: 'Ticket Usage Report',
          kind: 'table',
          orientation: 'portrait',
          table: { columns: ["Event","Used","Unused"], rows }
        });
        return;
      }
      if (report === "revenue") {
        const df = payFrom ? new Date(payFrom).getTime() : null;
        const dt = payTo ? new Date(payTo).getTime() : null;
        const rows: (string|number)[][] = [];
        Object.keys(paymentsByEvent).forEach(evId => {
          const ev = filteredEvents.find(e => e.id === evId);
          const list = (paymentsByEvent[evId] || []).filter((p: any) => {
            const t = new Date(p.createdAt).getTime();
            return !(df && t < df) && !(dt && t > dt);
          });
          const byMethod: Record<string, number> = {};
          let total = 0;
          list.forEach((p: any) => { const m = getPaymentMethod(p); byMethod[m] = (byMethod[m] || 0) + (p.amount || 0); total += (p.amount || 0); });
          if (Object.keys(byMethod).length === 0) rows.push([ev?.title || evId, '-', 0, 0]);
          Object.keys(byMethod).forEach(m => rows.push([ev?.title || evId, m, Number(byMethod[m].toFixed(2)), Number(total.toFixed(2))]));
        });
        await downloadReportPdf(API_BASE, token, {
          title: 'Revenue Summary',
          kind: 'table',
          orientation: 'portrait',
          table: { columns: ["Event","Method","Amount","Event Total"], rows }
        });
        return;
      }
      if (report === "revenueDetailed") {
        const rows: (string|number)[][] = (revDetRows as any[]).map(r => [
          r.eventTitle,
          r.userName,
          r.userEmail || "",
          r.status || "",
          Number((r.totalPaid || 0).toFixed(2)),
          Number((r.outstanding || 0).toFixed(2)),
          r.registrationId || "",
        ]);
        await downloadReportPdf(API_BASE, token, {
          title: 'Revenue Detailed',
          kind: 'table',
          orientation: 'landscape',
          table: { columns: ["Event","Name","Email","Status","Total paid","Outstanding","RegistrationId"], rows }
        });
        return;
      }
      if (report === "regStatus") {
        const rows: (string|number)[][] = [];
        Object.keys(registrationsByEvent).forEach(evId => {
          const ev = filteredEvents.find(e => e.id === evId);
          const regs = registrationsByEvent[evId] || [];
          const counts: Record<string, number> = { pending: 0, partial_paid: 0, paid: 0, cancelled: 0 };
          regs.forEach((r: any) => { if (!statusIncludeCancelled && r.status === 'cancelled') return; counts[r.status] = (counts[r.status] || 0) + 1; });
          rows.push([ev?.title || evId, counts.pending||0, counts.partial_paid||0, counts.paid||0, counts.cancelled||0]);
        });
        await downloadReportPdf(API_BASE, token, {
          title: 'Registration Status Breakdown',
          kind: 'table',
          table: { columns: ["Event","Pending","Partial","Paid","Cancelled"], rows }
        });
        return;
      }
    } catch (e: any) {
      alert(e?.message || 'Failed to generate PDF');
    }
  };

  const doEmail = async () => {
    if (!token) { alert('Please login to email PDF'); return; }
    const subject = {
      payments: "Payments Report",
      attendees: "Attendees Report",
      regTypes: "Registration Types Report",
      usage: "Ticket Usage Report",
      revenue: "Revenue Summary",
      revenueDetailed: "Revenue Detailed Report",
      regStatus: "Registration Status Breakdown",
    }[report];
    const body = "Report generated on " + new Date().toLocaleString();
    try {
      if (report === 'payments') {
        await emailReportPdf(API_BASE, token, {
          title: 'Payments Report', kind: 'table', orientation: 'portrait',
          table: { columns: ["Event","User","Email","Amount","Method","Donation","Date"], rows: paymentRows.map(r => [r.eventTitle, r.userName, r.userEmail || "", `R ${Number(r.amount).toFixed(2)}`, r.method, r.isDonation ? "Yes" : "No", new Date(r.createdAt).toLocaleString()]) },
          subject, body
        });
      } else if (report === 'attendees') {
        const sections = attendeesLayer.options.map(opt => ({ title: opt.optionName, items: opt.users.map((u: any) => `${u.name}${u.email ? ` (${u.email})` : ''} — ${u.status}${typeof u.qty==='number' ? ` • Qty: ${u.qty}` : ''}`) }));
        await emailReportPdf(API_BASE, token, { title: 'Attendees Report', kind: 'layered', orientation: 'portrait', layered: { header: `Event: ${attendeesLayer.eventTitle}`, sections }, subject, body });
      } else if (report === 'regTypes') {
        await emailReportPdf(API_BASE, token, { title: 'Registration Types Report', kind: 'table', orientation: 'portrait', table: { columns: ["Event","Type","Quantity"], rows: regTypeRows.map(r => [r.eventTitle, r.type, String(r.qty)]) }, subject, body });
      } else if (report === 'usage') {
        const rows: (string|number)[][] = [];
        Object.keys(ticketUsage).forEach(evId => { const ev = filteredEvents.find(e => e.id === evId); const tu = ticketUsage[evId]; rows.push([ev?.title || evId, tu.used, tu.unused]); });
        await emailReportPdf(API_BASE, token, { title: 'Ticket Usage Report', kind: 'table', orientation: 'portrait', table: { columns: ["Event","Used","Unused"], rows }, subject, body });
      } else if (report === 'revenue') {
        const df = payFrom ? new Date(payFrom).getTime() : null;
        const dt = payTo ? new Date(payTo).getTime() : null;
        const rows: (string|number)[][] = [];
        Object.keys(paymentsByEvent).forEach(evId => {
          const ev = filteredEvents.find(e => e.id === evId);
          const list = (paymentsByEvent[evId] || []).filter((p: any) => { const t = new Date(p.createdAt).getTime(); return !(df && t < df) && !(dt && t > dt); });
          const byMethod: Record<string, number> = {}; let total = 0;
          list.forEach((p: any) => { const m = getPaymentMethod(p); byMethod[m] = (byMethod[m] || 0) + (p.amount || 0); total += (p.amount || 0); });
          if (Object.keys(byMethod).length === 0) rows.push([ev?.title || evId, '-', 0, 0]);
          Object.keys(byMethod).forEach(m => rows.push([ev?.title || evId, m, Number(byMethod[m].toFixed(2)), Number(total.toFixed(2))]));
        });
        await emailReportPdf(API_BASE, token, { title: 'Revenue Summary', kind: 'table', orientation: 'portrait', table: { columns: ["Event","Method","Amount","Event Total"], rows }, subject, body });
      } else if (report === 'revenueDetailed') {
        const rows: (string|number)[][] = (revDetRows as any[]).map(r => [ r.eventTitle, r.userName, r.userEmail || "", r.status || "", Number((r.totalPaid || 0).toFixed(2)), Number((r.outstanding || 0).toFixed(2)), r.registrationId || "" ]);
        await emailReportPdf(API_BASE, token, { title: 'Revenue Detailed', kind: 'table', orientation: 'landscape', table: { columns: ["Event","Name","Email","Status","Total paid","Outstanding","RegistrationId"], rows }, subject, body });
      } else if (report === 'regStatus') {
        const rows: (string|number)[][] = [];
        Object.keys(registrationsByEvent).forEach(evId => { const ev = filteredEvents.find(e => e.id === evId); const regs = registrationsByEvent[evId] || []; const counts: Record<string, number> = { pending: 0, partial_paid: 0, paid: 0, cancelled: 0 }; regs.forEach((r: any) => { if (!statusIncludeCancelled && r.status === 'cancelled') return; counts[r.status] = (counts[r.status] || 0) + 1; }); rows.push([ev?.title || evId, counts.pending||0, counts.partial_paid||0, counts.paid||0, counts.cancelled||0]); });
        await emailReportPdf(API_BASE, token, { title: 'Registration Status Breakdown', kind: 'table', table: { columns: ["Event","Pending","Partial","Paid","Cancelled"], rows }, subject, body });
      }
      alert('Email sent with PDF attachment');
    } catch (e: any) {
      alert(e?.message || 'Failed to email PDF');
    }
  };

  // Rendering helpers
  function htmlTable(headers: string[], rows: (string | number)[][]) {
    const thead = `<tr>${headers.map(h => `<th>${escapeHtml(h)}</th>`).join("")}</tr>`;
    const tbody = rows.map(r => `<tr>${r.map(c => `<td>${escapeHtml(String(c ?? ""))}</td>`).join("")}</tr>`).join("");
    return `<table><thead>${thead}</thead><tbody>${tbody}</tbody></table>`;
  }
  function htmlLayered(eventTitle: string, options: { optionName: string; users: { name: string; email?: string; status: string; qty?: number }[] }[]) {
    const optHtml = options.map(opt => `
      <div class="layer-option">
        <div class="layer-option-title">${escapeHtml(opt.optionName)}</div>
        <ul class="layer-users">
          ${opt.users.map(u => `<li><span class="name">${escapeHtml(u.name || "")}</span>${u.email ? ` <span class="email">(${escapeHtml(u.email)})</span>` : ""} <span class="status">— ${escapeHtml(u.status)}</span>${typeof u.qty === 'number' ? ` <span class="status"> • Qty: ${u.qty}</span>` : ''}</li>`).join("")}
        </ul>
      </div>
    `).join("");
    return `
      <style>
        .layer-event{font-size:16px;font-weight:700;margin:4px 0 10px 0}
        .layer-option{margin:8px 0 12px 16px;page-break-inside:avoid}
        .layer-option-title{font-weight:600;margin-bottom:6px}
        .layer-users{list-style:disc;margin-left:20px}
        .layer-users li{margin:3px 0;font-size:12px}
        .layer-users .name{font-weight:500}
        .layer-users .email{color:#555}
        .layer-users .status{color:#666}
      </style>
      <div class="layer-event">${escapeHtml(eventTitle)}</div>
      ${optHtml}
    `;
  }
  function escapeHtml(s: string) {
    return s.replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;','\'':'&#39;'}[c] as string));
  }

  // UI
  return (
    <div>
      {!canView && (
        <div className="p-3 border rounded bg-yellow-50 text-yellow-800 text-sm mb-4">You need supervisor or admin access to view reports.</div>
      )}
      {error && <div className="p-3 mb-3 border rounded bg-red-50 text-red-700 text-sm">{error}</div>}

      {/* Step 1: select report */}
      <div className="border rounded-xl p-4 bg-white shadow-sm mb-6 space-y-3">
        <div className="text-sm font-medium">Select a report</div>
        <div className="flex flex-wrap gap-2">
          {REPORTS.map(r => (
            <label key={r.key} className={`px-3 py-2 text-sm rounded border cursor-pointer ${report===r.key? 'bg-indigo-600 text-white border-indigo-600' : 'bg-white text-gray-800 border-gray-200'}`}>
              <input type="radio" className="hidden" name="report" value={r.key} checked={report===r.key} onChange={() => { setReport(r.key); setReady(false); }} />
              {r.label}
            </label>
          ))}
        </div>
      </div>

      {/* Step 2: filters per report */}
      <div className="border rounded-xl p-4 bg-white shadow-sm mb-6 space-y-4">
        <div className="text-sm font-medium">Filters</div>
        <div className="flex gap-4 text-sm">
          <label className="flex items-center gap-2">
            <input
                type="checkbox"
                checked={showPastEvents}
                onChange={e => setShowPastEvents(e.target.checked)}
            />
            Show past events
          </label>

          <label className="flex items-center gap-2">
            <input
                type="checkbox"
                checked={showCancelledEvents}
                onChange={e => setShowCancelledEvents(e.target.checked)}
            />
            Show cancelled events
          </label>
        </div>
        {report === "payments" && (
          <div className="flex flex-wrap items-end gap-3">
            <div>
              <label className="block text-xs text-gray-600 mb-1">From</label>
              <input type="date" className="border rounded px-3 py-2 text-sm" value={payFrom} onChange={e => setPayFrom(e.target.value)} />
            </div>
            <div>
              <label className="block text-xs text-gray-600 mb-1">To</label>
              <input type="date" className="border rounded px-3 py-2 text-sm" value={payTo} onChange={e => setPayTo(e.target.value)} />
            </div>
            <div className="min-w-64 flex-1">
              <label className="block text-xs text-gray-600 mb-1">Events</label>
              <MultiSelect options={filteredEvents.map(ev => ({ value: ev.id, label: ev.title }))} value={payEvents} onChange={setPayEvents} />
            </div>
          </div>
        )}
        {report === "attendees" && (
          <div className="flex flex-wrap items-center gap-3">
            <label className="text-sm">
              <span className="text-gray-600 mr-2">Event</span>
              <select className="border rounded px-3 py-2 text-sm" value={attEventId} onChange={e => setAttEventId(e.target.value)}>
                {filteredEvents.map(ev => (<option key={ev.id} value={ev.id}>{ev.title}</option>))}
              </select>
            </label>
            <label className="flex items-center gap-2 text-sm">
              <input type="checkbox" checked={attIncludeCancelled} onChange={e => setAttIncludeCancelled(e.target.checked)} /> Include cancelled registrations
            </label>
          </div>
        )}
        {report === "regTypes" && (
          <div>
            <label className="block text-xs text-gray-600 mb-1">Events</label>
            <MultiSelect options={filteredEvents.map(ev => ({ value: ev.id, label: ev.title }))} value={regTypeEvents} onChange={setRegTypeEvents} />
          </div>
        )}
        {report === "usage" && (
          <div>
            <label className="block text-xs text-gray-600 mb-1">Events</label>
            <MultiSelect options={filteredEvents.map(ev => ({ value: ev.id, label: ev.title }))} value={usageEvents} onChange={setUsageEvents} />
          </div>
        )}
        {report === "revenue" && (
          <div className="flex flex-wrap items-end gap-3">
            <div>
              <label className="block text-xs text-gray-600 mb-1">From</label>
              <input type="date" className="border rounded px-3 py-2 text-sm" value={payFrom} onChange={e => setPayFrom(e.target.value)} />
            </div>
            <div>
              <label className="block text-xs text-gray-600 mb-1">To</label>
              <input type="date" className="border rounded px-3 py-2 text-sm" value={payTo} onChange={e => setPayTo(e.target.value)} />
            </div>
            <div className="min-w-64 flex-1">
              <label className="block text-xs text-gray-600 mb-1">Events</label>
              <MultiSelect options={filteredEvents.map(ev => ({ value: ev.id, label: ev.title }))} value={revEvents} onChange={setRevEvents} />
            </div>
          </div>
        )}
        {report === "revenueDetailed" && (
          <div className="flex flex-wrap items-end gap-3">
            <div>
              <label className="block text-xs text-gray-600 mb-1">From</label>
              <input type="date" className="border rounded px-3 py-2 text-sm" value={payFrom} onChange={e => setPayFrom(e.target.value)} />
            </div>
            <div>
              <label className="block text-xs text-gray-600 mb-1">To</label>
              <input type="date" className="border rounded px-3 py-2 text-sm" value={payTo} onChange={e => setPayTo(e.target.value)} />
            </div>
            <div className="min-w-64 flex-1">
              <label className="block text-xs text-gray-600 mb-1">Events</label>
              <MultiSelect options={filteredEvents.map(ev => ({ value: ev.id, label: ev.title }))} value={revDetEvents} onChange={setRevDetEvents} />
            </div>
          </div>
        )}
        {report === "regStatus" && (
          <div className="flex flex-wrap items-center gap-3">
            <div className="min-w-64 flex-1">
              <label className="block text-xs text-gray-600 mb-1">Events</label>
              <MultiSelect options={filteredEvents.map(ev => ({ value: ev.id, label: ev.title }))} value={statusEvents} onChange={setStatusEvents} />
            </div>
            <label className="flex items-center gap-2 text-sm">
              <input type="checkbox" checked={statusIncludeCancelled} onChange={e => setStatusIncludeCancelled(e.target.checked)} /> Include cancelled in counts
            </label>
          </div>
        )}
        <div>
          <button disabled={busy} onClick={onView} className="px-3 py-2 text-sm rounded bg-indigo-600 text-white hover:bg-indigo-700 disabled:opacity-50">{busy ? 'Loading…' : 'View report'}</button>
        </div>
      </div>

      {/* Step 3: results + actions */}
      {ready && (
        <div className="border rounded-xl p-4 bg-white shadow-sm mb-6">
          <div className="flex items-center justify-between mb-4">
            <div className="text-lg font-semibold">Results</div>
            <div className="flex gap-2">
              <button className="px-3 py-1.5 text-sm rounded border" onClick={doExportCsv}>Export CSV</button>
              <button className="px-3 py-1.5 text-sm rounded border" onClick={doPrintPdf}>Save as PDF</button>
              <button className="px-3 py-1.5 text-sm rounded border" onClick={doEmail}>Email</button>
            </div>
          </div>

          {report === "payments" && (
            <div>
              {paymentRows.length === 0 ? (
                <div className="text-sm text-gray-500">No payments match the selected filters.</div>
              ) : (
                <div className="space-y-5">
                  {Object.entries(paymentRows.reduce((acc: any, r: any) => {
                    acc[r.eventTitle] = acc[r.eventTitle] || [];
                    acc[r.eventTitle].push(r);
                    return acc;
                  }, {})).map(([eventTitle, rows]: any) => {
                    // group by user
                    const byUser: Record<string, any[]> = {};
                    rows.forEach((r: any) => {
                      const key = r.userName || 'User';
                      byUser[key] = byUser[key] || [];
                      byUser[key].push(r);
                    });
                    const eventTotal = rows.reduce((s: number, r: any) => s + (r.amount || 0), 0);
                    return (
                      <div key={eventTitle} className="border rounded-xl p-4 bg-gray-50">
                        <div className="flex items-center justify-between mb-2">
                          <div className="text-base font-semibold">{eventTitle}</div>
                          <div className="text-sm font-medium">Total: R {eventTotal.toFixed(2)}</div>
                        </div>
                        <div className="space-y-3">
                          {Object.keys(byUser).sort().map(userName => {
                            const list = byUser[userName];
                            const userTotal = list.reduce((s: number, r: any) => s + (r.amount || 0), 0);
                            const email = list.find((r: any) => r.userEmail)?.userEmail || '';
                            return (
                              <div key={userName} className="border rounded-lg p-3 bg-white">
                                <div className="flex items-center justify-between mb-2">
                                  <div className="font-medium">{userName} {email ? <span className="text-gray-600 font-normal">({email})</span> : null}</div>
                                  <div className="text-sm">Subtotal: R {userTotal.toFixed(2)}</div>
                                </div>
                                <ul className="text-sm text-gray-700 space-y-1">
                                  {list.map((r: any, i: number) => (
                                    <li key={i} className="flex items-center justify-between">
                                      <span>{r.method}{r.isDonation ? ' • Donation' : ''}</span>
                                      <span>R {Number(r.amount).toFixed(2)} • {new Date(r.createdAt).toLocaleString()}</span>
                                    </li>
                                  ))}
                                </ul>
                              </div>
                            );
                          })}
                        </div>
                      </div>
                    );
                  })}
                </div>
              )}
            </div>
          )}

          {report === "attendees" && (
            <div>
              <div className="text-sm text-gray-700 mb-3">Event: <span className="font-medium">{attendeesLayer.eventTitle}</span></div>
              {attendeesLayer.options.length === 0 ? (
                <div className="text-sm text-gray-500">No attendees for selected filters.</div>
              ) : (
                <div className="space-y-4">
                  {attendeesLayer.options.map(opt => (
                    <div key={opt.optionName} className="border rounded-lg p-3">
                      <div className="font-medium mb-2">{opt.optionName}</div>
                      <ul className="list-disc ml-5 space-y-1">
                        {opt.users.map((u: any, i: number) => (
                          <li key={opt.optionName + i} className="text-sm">
                            <span className="font-medium">{u.name}</span>
                            {u.email && <span className="text-gray-600"> ({u.email})</span>}
                            <span className="text-gray-600"> — {u.status}</span>
                            <span className="text-gray-700"> • Qty {u.qty}</span>
                          </li>
                        ))}
                      </ul>
                    </div>
                  ))}
                </div>
              )}
            </div>
          )}

          {report === "regTypes" && (
            <div className="overflow-auto">
              {regTypeRows.length === 0 ? (
                <div className="text-sm text-gray-500">No data available. Select events and view.</div>
              ) : (
                <table className="min-w-[520px]">
                  <thead>
                    <tr>
                      <th>Event</th>
                      <th>Registration Type</th>
                      <th>Quantity</th>
                    </tr>
                  </thead>
                  <tbody>
                    {regTypeRows.map((r, idx) => (
                      <tr key={idx}>
                        <td>{r.eventTitle}</td>
                        <td>{r.type}</td>
                        <td>{r.qty}</td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              )}
            </div>
          )}

          {report === "usage" && (
            <div className="overflow-auto">
              {Object.keys(ticketUsage).length === 0 ? (
                <div className="text-sm text-gray-500">No data available. Select events and view.</div>
              ) : (
                <table className="min-w-[420px]">
                  <thead>
                    <tr>
                      <th>Event</th>
                      <th>Used</th>
                      <th>Unused</th>
                    </tr>
                  </thead>
                  <tbody>
                    {Object.keys(ticketUsage).map(evId => {
                      const ev = events.find(e => e.id === evId);
                      const tu = ticketUsage[evId];
                      return (
                        <tr key={evId}>
                          <td>{ev?.title || evId}</td>
                          <td>{tu.used}</td>
                          <td>{tu.unused}</td>
                        </tr>
                      );
                    })}
                  </tbody>
                </table>
              )}
            </div>
          )}

          {report === "revenue" && (
            <div className="space-y-4">
              {Object.keys(paymentsByEvent).length === 0 ? (
                <div className="text-sm text-gray-500">No data available. Select events and view.</div>
              ) : (
                Object.keys(paymentsByEvent).map(evId => {
                  const ev = events.find(e => e.id === evId);
                  const df = payFrom ? new Date(payFrom).getTime() : null;
                  const dt = payTo ? new Date(payTo).getTime() : null;
                  const list = (paymentsByEvent[evId] || []).filter((p: any) => {
                    const t = new Date(p.createdAt).getTime();
                    return !(df && t < df) && !(dt && t > dt);
                  });
                  const byMethod: Record<string, number> = {};
                  let total = 0;
                  list.forEach((p: any) => { const m = getPaymentMethod(p); byMethod[m] = (byMethod[m] || 0) + (p.amount || 0); total += (p.amount || 0); });
                  return (
                    <div key={evId} className="border rounded-lg p-3">
                      <div className="flex items-center justify-between mb-2">
                        <div className="font-medium">{ev?.title || evId}</div>
                        <div className="text-sm">Total: R {total.toFixed(2)}</div>
                      </div>
                      {Object.keys(byMethod).length === 0 ? (
                        <div className="text-sm text-gray-500">No payments in date range.</div>
                      ) : (
                        <ul className="text-sm text-gray-700 space-y-1">
                          {Object.keys(byMethod).sort().map(m => (
                            <li key={m} className="flex items-center justify-between"><span>{m}</span><span>R {byMethod[m].toFixed(2)}</span></li>
                          ))}
                        </ul>
                      )}
                    </div>
                  );
                })
              )}
            </div>
          )}

          {report === "revenueDetailed" && (
            <div className="space-y-4">
              {Object.keys(registrationsByEvent).length === 0 ? (
                <div className="text-sm text-gray-500">No data available. Select events and view.</div>
              ) : (
                Object.keys(registrationsByEvent).map(evId => {
                  const ev = events.find(e => e.id === evId);
                  const rows = (revDetRows as any[]).filter(r => r.eventId === evId);
                  const total = rows.reduce((s, r) => s + (r.totalPaid || 0), 0);
                  const regIds = rows.map(r => r.registrationId);
                  const outstandingTotal = regIds.reduce((s, id) => s + (outstandingByReg.get(id) || 0), 0);
                  return (
                    <div key={evId} className="border rounded-lg p-3">
                      <div className="flex items-center justify-between mb-2">
                        <div className="font-medium">{ev?.title || evId}</div>
                        <div className="text-sm">Total: R {total.toFixed(2)}{` • Outstanding: R ${outstandingTotal.toFixed(2)}`}</div>
                      </div>
                      {rows.length === 0 ? (
                        <div className="text-sm text-gray-500">No registrations in date range.</div>
                      ) : (
                        <div className="overflow-auto">
                          <table className="min-w-[760px] text-sm">
                            <thead>
                              <tr>
                                <th className="text-left">Name</th>
                                <th className="text-left">Email</th>
                                <th className="text-left">Status</th>
                                <th className="text-left">Total paid</th>
                                <th className="text-left">Outstanding</th>
                                <th className="text-left">Registration</th>
                              </tr>
                            </thead>
                            <tbody>
                              {rows.map((r, idx) => (
                                <tr key={evId + idx}>
                                  <td>{r.userName}</td>
                                  <td>{r.userEmail || ''}</td>
                                  <td className="capitalize">{r.status || ''}</td>
                                  <td>R {Number(r.totalPaid || 0).toFixed(2)}</td>
                                  <td>R {Number(r.outstanding || 0).toFixed(2)}</td>
                                  <td className="font-mono text-xs">{r.registrationId || ''}</td>
                                </tr>
                              ))}
                            </tbody>
                          </table>
                        </div>
                      )}
                    </div>
                  );
                })
              )}
            </div>
          )}

          {report === "regStatus" && (
            <div className="overflow-auto">
              {Object.keys(registrationsByEvent).length === 0 ? (
                <div className="text-sm text-gray-500">No data available. Select events and view.</div>
              ) : (
                <table className="min-w-[560px]">
                  <thead>
                    <tr>
                      <th>Event</th>
                      <th>Pending</th>
                      <th>Partial</th>
                      <th>Paid</th>
                      <th>Cancelled</th>
                    </tr>
                  </thead>
                  <tbody>
                    {Object.keys(registrationsByEvent).map(evId => {
                      const ev = events.find(e => e.id === evId);
                      const regs = registrationsByEvent[evId] || [];
                      const counts: Record<string, number> = { pending: 0, partial_paid: 0, paid: 0, cancelled: 0 };
                      regs.forEach((r: any) => { if (!statusIncludeCancelled && r.status === 'cancelled') return; counts[r.status] = (counts[r.status] || 0) + 1; });
                      return (
                        <tr key={evId}>
                          <td>{ev?.title || evId}</td>
                          <td>{counts.pending || 0}</td>
                          <td>{counts.partial_paid || 0}</td>
                          <td>{counts.paid || 0}</td>
                          <td>{counts.cancelled || 0}</td>
                        </tr>
                      );
                    })}
                  </tbody>
                </table>
              )}
            </div>
          )}
        </div>
      )}
    </div>
  );
}
