// @File: src/pages/admin/cms/CmsAuditLog.tsx
// @Desc: Admin CMS page at /admin/cms/audit-log. Renders paginated
//        admin audit-log rows from /api/v1/admin/audit-logs with
//        filters for date range, action type, and email search, plus a
//        CSV export of the currently-filtered view.
import { FormEvent, useCallback, useEffect, useMemo, useState } from "react";
import {
  AlertCircle,
  ClipboardList,
  Download,
  Loader2,
  RefreshCw,
  Search,
} from "lucide-react";
import CmsLayout from "./CmsLayout";
import { PageHeader, SectionCard } from "./ui";
import { AuditLogItem, listAuditLogs } from "@/lib/adminApi";

// Human-friendly labels for known action codes. Unknown codes fall back to
// the raw identifier so that a backend adding a new action doesn't break
// rendering; it just shows up verbatim until the frontend is updated.
const ACTION_LABELS: Record<string, string> = {
  "auth.login": "Login",
  "auth.password_reset_completed": "Password reset (self)",
  "admin.user.role_changed": "Role changed",
  "admin.user.active_changed": "Account enabled/disabled",
  "admin.user.password_reset": "Password reset (admin)",
  "admin.cache.toggled": "Cache toggled",
  "admin.cache.cleared": "Cache cleared",
};

const ACTION_TONES: Record<string, string> = {
  "auth.login": "bg-sky-50 text-sky-700 border-sky-200",
  "auth.password_reset_completed": "bg-amber-50 text-amber-700 border-amber-200",
  "admin.user.role_changed": "bg-emerald-50 text-emerald-700 border-emerald-200",
  "admin.user.active_changed": "bg-slate-100 text-slate-700 border-slate-200",
  "admin.user.password_reset": "bg-red-50 text-red-700 border-red-200",
  "admin.cache.toggled": "bg-indigo-50 text-indigo-700 border-indigo-200",
  "admin.cache.cleared": "bg-violet-50 text-violet-700 border-violet-200",
};

function formatDate(value: string | null): string {
  if (!value) return "—";
  try {
    return new Date(value).toLocaleString();
  } catch {
    return value;
  }
}

function formatDetails(raw: string | null): string {
  if (!raw) return "";
  try {
    const parsed = JSON.parse(raw) as Record<string, unknown>;
    return Object.entries(parsed)
      .map(([k, v]) => `${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`)
      .join(", ");
  } catch {
    return raw;
  }
}

function toIsoStart(date: string): string | undefined {
  if (!date) return undefined;
  // Treat the input (YYYY-MM-DD) as a local-midnight instant, then convert
  // to an ISO-8601 string the backend's _parse_iso_datetime can consume.
  const dt = new Date(`${date}T00:00:00`);
  if (Number.isNaN(dt.getTime())) return undefined;
  return dt.toISOString();
}

function toIsoEnd(date: string): string | undefined {
  if (!date) return undefined;
  // Make the end date inclusive by bumping to next day's midnight.
  const dt = new Date(`${date}T00:00:00`);
  if (Number.isNaN(dt.getTime())) return undefined;
  dt.setDate(dt.getDate() + 1);
  return dt.toISOString();
}

function csvEscape(value: string): string {
  if (value.includes('"') || value.includes(",") || value.includes("\n")) {
    return `"${value.replace(/"/g, '""')}"`;
  }
  return value;
}

function rowsToCsv(rows: AuditLogItem[]): string {
  const header = [
    "timestamp",
    "action",
    "admin_email",
    "target_email",
    "ip_address",
    "details",
  ];
  const lines = [header.join(",")];
  rows.forEach((r) => {
    lines.push(
      [
        r.created_at ?? "",
        r.action ?? "",
        r.admin_email ?? "",
        r.target_email ?? "",
        r.ip_address ?? "",
        formatDetails(r.details),
      ]
        .map((v) => csvEscape(String(v)))
        .join(","),
    );
  });
  return lines.join("\n");
}

export default function CmsAuditLog() {
  const [rows, setRows] = useState<AuditLogItem[]>([]);
  const [total, setTotal] = useState(0);
  const [knownActions, setKnownActions] = useState<string[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  // Filters
  const [search, setSearch] = useState("");
  const [action, setAction] = useState<string>("");
  const [startDate, setStartDate] = useState<string>("");
  const [endDate, setEndDate] = useState<string>("");
  const [page, setPage] = useState(1);
  const pageSize = 50;

  const loadLogs = useCallback(async () => {
    setLoading(true);
    setError(null);
    try {
      const data = await listAuditLogs({
        search: search.trim() || undefined,
        action: action || undefined,
        start: toIsoStart(startDate),
        end: toIsoEnd(endDate),
        page,
        pageSize,
      });
      setRows(data.items);
      setTotal(data.total);
      if (data.known_actions?.length) {
        setKnownActions(data.known_actions);
      }
    } catch (err) {
      setError(err instanceof Error ? err.message : "Failed to load audit log.");
      setRows([]);
      setTotal(0);
    } finally {
      setLoading(false);
    }
  }, [action, endDate, page, search, startDate]);

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

  const totalPages = useMemo(() => Math.max(1, Math.ceil(total / pageSize)), [total]);

  function handleSearchSubmit(e: FormEvent) {
    e.preventDefault();
    setPage(1);
    loadLogs();
  }

  function resetFilters() {
    setSearch("");
    setAction("");
    setStartDate("");
    setEndDate("");
    setPage(1);
  }

  function handleExportCsv() {
    if (rows.length === 0) return;
    const csv = rowsToCsv(rows);
    const blob = new Blob([csv], { type: "text/csv;charset=utf-8" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    const stamp = new Date().toISOString().replace(/[:.]/g, "-");
    a.download = `audit-log-${stamp}.csv`;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
  }

  const filterActive = Boolean(search || action || startDate || endDate);

  return (
    <CmsLayout>
      <PageHeader
        title="Audit Log"
        description="Every sensitive admin action — logins, role changes, account toggles, and password resets — is recorded here."
      />

      {error && (
        <div
          role="alert"
          className="mb-4 rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 flex items-start gap-2"
        >
          <AlertCircle className="w-4 h-4 mt-0.5 shrink-0" />
          <span>{error}</span>
        </div>
      )}

      <SectionCard
        title="Events"
        description={`${total} matching event${total === 1 ? "" : "s"}`}
        actions={
          <div className="flex gap-2">
            <button
              type="button"
              onClick={handleExportCsv}
              disabled={rows.length === 0}
              className="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-semibold rounded-lg border border-slate-300 text-slate-700 hover:bg-slate-50 disabled:opacity-40"
            >
              <Download className="w-3.5 h-3.5" /> Export CSV
            </button>
            <button
              type="button"
              onClick={loadLogs}
              className="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-semibold rounded-lg border border-slate-300 text-slate-700 hover:bg-slate-50"
            >
              <RefreshCw className="w-3.5 h-3.5" /> Refresh
            </button>
          </div>
        }
      >
        <form
          onSubmit={handleSearchSubmit}
          className="flex flex-wrap items-end gap-2 pb-2"
        >
          <div className="flex-1 min-w-[220px]">
            <label className="block text-[11px] font-semibold text-slate-500 uppercase tracking-wide mb-1">
              Search email
            </label>
            <div className="relative">
              <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400" />
              <input
                type="search"
                value={search}
                onChange={(e) => setSearch(e.target.value)}
                placeholder="Admin or target email"
                className="w-full rounded-lg border border-slate-300 pl-9 pr-3 py-2 text-sm focus:border-[#0b6e3e] focus:ring-2 focus:ring-[#0b6e3e]/20 outline-none"
              />
            </div>
          </div>
          <div>
            <label className="block text-[11px] font-semibold text-slate-500 uppercase tracking-wide mb-1">
              Action
            </label>
            <select
              value={action}
              onChange={(e) => {
                setAction(e.target.value);
                setPage(1);
              }}
              className="rounded-lg border border-slate-300 px-3 py-2 text-sm focus:border-[#0b6e3e] focus:ring-2 focus:ring-[#0b6e3e]/20 outline-none"
            >
              <option value="">All actions</option>
              {knownActions.map((a) => (
                <option key={a} value={a}>
                  {ACTION_LABELS[a] ?? a}
                </option>
              ))}
            </select>
          </div>
          <div>
            <label className="block text-[11px] font-semibold text-slate-500 uppercase tracking-wide mb-1">
              Start date
            </label>
            <input
              type="date"
              value={startDate}
              onChange={(e) => {
                setStartDate(e.target.value);
                setPage(1);
              }}
              className="rounded-lg border border-slate-300 px-3 py-2 text-sm focus:border-[#0b6e3e] focus:ring-2 focus:ring-[#0b6e3e]/20 outline-none"
            />
          </div>
          <div>
            <label className="block text-[11px] font-semibold text-slate-500 uppercase tracking-wide mb-1">
              End date
            </label>
            <input
              type="date"
              value={endDate}
              onChange={(e) => {
                setEndDate(e.target.value);
                setPage(1);
              }}
              className="rounded-lg border border-slate-300 px-3 py-2 text-sm focus:border-[#0b6e3e] focus:ring-2 focus:ring-[#0b6e3e]/20 outline-none"
            />
          </div>
          <div className="flex gap-2">
            <button
              type="submit"
              className="px-4 py-2 text-sm font-semibold rounded-lg bg-[#0b6e3e] text-white hover:bg-[#084e2c]"
            >
              Apply
            </button>
            {filterActive && (
              <button
                type="button"
                onClick={resetFilters}
                className="px-3 py-2 text-sm text-slate-600 hover:text-slate-900"
              >
                Clear
              </button>
            )}
          </div>
        </form>

        <div className="overflow-x-auto">
          <table className="w-full text-sm">
            <thead>
              <tr className="text-left text-xs font-semibold text-slate-500 uppercase tracking-wide border-b border-slate-200">
                <th className="py-3 pr-4">When</th>
                <th className="py-3 pr-4">Action</th>
                <th className="py-3 pr-4">Admin</th>
                <th className="py-3 pr-4">Target user</th>
                <th className="py-3 pr-4">IP</th>
                <th className="py-3 pr-4">Details</th>
              </tr>
            </thead>
            <tbody>
              {loading && rows.length === 0 ? (
                <tr>
                  <td colSpan={6} className="py-10 text-center text-slate-500">
                    <Loader2 className="w-5 h-5 animate-spin inline mr-2" />
                    Loading audit log…
                  </td>
                </tr>
              ) : rows.length === 0 ? (
                <tr>
                  <td colSpan={6} className="py-10 text-center text-slate-500">
                    <ClipboardList className="w-6 h-6 inline mr-2 text-slate-400" />
                    No events match your filters yet.
                  </td>
                </tr>
              ) : (
                rows.map((row) => {
                  const label = ACTION_LABELS[row.action] ?? row.action;
                  const tone =
                    ACTION_TONES[row.action] ?? "bg-slate-50 text-slate-600 border-slate-200";
                  const sameActor =
                    row.admin_email && row.target_email && row.admin_email === row.target_email;
                  return (
                    <tr key={row.id} className="border-b border-slate-100 last:border-0 align-top">
                      <td className="py-3 pr-4 text-xs text-slate-600 whitespace-nowrap">
                        {formatDate(row.created_at)}
                      </td>
                      <td className="py-3 pr-4">
                        <span
                          className={`inline-flex items-center px-2 py-0.5 rounded-full border text-[11px] font-semibold ${tone}`}
                        >
                          {label}
                        </span>
                      </td>
                      <td className="py-3 pr-4 text-xs text-slate-700">
                        {row.admin_email ?? "—"}
                      </td>
                      <td className="py-3 pr-4 text-xs text-slate-700">
                        {sameActor ? (
                          <span className="italic text-slate-500">(self)</span>
                        ) : (
                          row.target_email ?? "—"
                        )}
                      </td>
                      <td className="py-3 pr-4 text-xs text-slate-500 font-mono">
                        {row.ip_address ?? "—"}
                      </td>
                      <td className="py-3 pr-4 text-xs text-slate-600 max-w-xs break-words">
                        {formatDetails(row.details) || "—"}
                      </td>
                    </tr>
                  );
                })
              )}
            </tbody>
          </table>
        </div>

        {totalPages > 1 && (
          <div className="flex items-center justify-between pt-4">
            <div className="text-xs text-slate-500">
              Page {page} of {totalPages}
            </div>
            <div className="flex gap-2">
              <button
                type="button"
                disabled={page <= 1 || loading}
                onClick={() => setPage((p) => Math.max(1, p - 1))}
                className="px-3 py-1.5 text-xs font-semibold rounded-lg border border-slate-300 text-slate-700 hover:bg-slate-50 disabled:opacity-40"
              >
                Previous
              </button>
              <button
                type="button"
                disabled={page >= totalPages || loading}
                onClick={() => setPage((p) => Math.min(totalPages, p + 1))}
                className="px-3 py-1.5 text-xs font-semibold rounded-lg border border-slate-300 text-slate-700 hover:bg-slate-50 disabled:opacity-40"
              >
                Next
              </button>
            </div>
          </div>
        )}
      </SectionCard>
    </CmsLayout>
  );
}