﻿"use client";

import { useCallback, useEffect, useMemo, useState } from "react";
import toast, { Toaster } from "react-hot-toast";
import { API, resolveImageUrl } from "@/lib/api";
import type { AdminField, AdminResource } from "@/lib/adminResources";
import { RichTextEditor } from "./RichTextEditor";
import { TablePagination } from "./TablePagination";

/* Rich Text Editor */

function FieldEditor({ field, row }: { field: AdminField; row?: Row }) {
  const [html, setHtml] = useState(() => formValue(row?.[field.name] ?? null, "editor"));

  return (
    <label className="md:col-span-2">
      <span className="mb-1.5 block text-[11.5px] font-black text-[var(--color-ink)]">{field.label}</span>
      <input name={field.name} type="hidden" value={html} />
      <RichTextEditor
        value={html}
        onChange={(val: string) => setHtml(val)}
        placeholder={`Write ${field.label.toLowerCase()}...`}
      />
    </label>
  );
}

type Row = Record<string, string | number | null>;
type ModalState =
  | { type: "none" }
  | { type: "create" }
  | { type: "edit"; row: Row }
  | { type: "delete"; row: Row }
  | { type: "reply"; row: Row };

export function AdminResourceManager({ resource }: { resource: AdminResource }) {
  const [rows, setRows] = useState<Row[]>([]);
  const [loading, setLoading] = useState(true);
  const [message, setMessage] = useState("");
  const [query, setQuery] = useState("");
  const [filters, setFilters] = useState<Record<string, string>>({});
  const [modal, setModal] = useState<ModalState>({ type: "none" });
  const [page, setPage] = useState(1);
  const PAGE_SIZE = 10;

  const token = useMemo(() => {
    if (typeof window === "undefined") return "";
    return localStorage.getItem("ngo_admin_token") || "";
  }, []);

  const createFields = resource.createFields || [];
  const editFields = resource.editFields || resource.createFields || [];
  const canCreate = resource.canCreate ?? createFields.length > 0;
  const canEdit = resource.canEdit ?? editFields.length > 0;
  const canDelete = resource.canDelete ?? resource.key !== "settings";

  const filteredRows = rows.filter((row) => {
    const matchesSearch = Object.values(row).some((value) => String(value ?? "").toLowerCase().includes(query.toLowerCase()));
    const matchesFilters = Object.entries(filters).every(([key, val]) => !val || String(row[key] ?? "").toLowerCase() === val.toLowerCase());
    return matchesSearch && matchesFilters;
  });

  const totalPages = Math.max(1, Math.ceil(filteredRows.length / PAGE_SIZE));
  const safePage = Math.min(page, totalPages);
  const pageStart = (safePage - 1) * PAGE_SIZE;
  const pageRows = filteredRows.slice(pageStart, pageStart + PAGE_SIZE);

  const changeQuery = (value: string) => { setQuery(value); setPage(1); };
  const changeFilter = (name: string, value: string) => { setFilters((prev) => ({ ...prev, [name]: value })); setPage(1); };

  const filterOptions = useMemo(() => {
    const opts: Record<string, string[]> = {};
    if (resource.filters) {
      for (const f of resource.filters) {
        if (f.options.length > 0) {
          opts[f.name] = f.options;
        } else {
          const vals = new Set(rows.map((r) => String(r[f.name] ?? "")).filter(Boolean));
          opts[f.name] = [...vals].sort();
        }
      }
    }
    return opts;
  }, [resource.filters, rows]);

  const loadRows = useCallback(async () => {
    setLoading(true);
    const res = await fetch(`${API}${resource.endpoint}`, {
      headers: { Authorization: `Bearer ${token}` },
    });
    if (!res.ok) throw new Error("Failed to load data");
    setRows(await res.json());
    setLoading(false);
  }, [resource.endpoint, token]);

  useEffect(() => {
    const timer = window.setTimeout(() => {
      loadRows().catch(() => {
        setMessage("Could not load this section.");
        setLoading(false);
      });
    }, 0);
    return () => window.clearTimeout(timer);
  }, [loadRows]);

  const saveRecord = async (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    setMessage("");

    const fd = new FormData(event.currentTarget);
    const hasFile = [...fd.values()].some((v) => v instanceof File && (v as File).size > 0);

    if (hasFile) {
      const editing = modal.type === "edit";
      const id = editing ? rowId(modal.row) : "";
      const res = await fetch(`${API}${resource.endpoint}${editing ? `/${id}` : ""}`, {
        method: editing ? "PUT" : "POST",
        headers: { Authorization: `Bearer ${token}` },
        body: fd,
      });
      if (!res.ok) {
        setMessage(editing ? "Update failed. Check fields and permissions." : "Create failed. Check fields and permissions.");
        return;
      }
      setModal({ type: "none" });
      setMessage(editing ? "Record updated." : "Record created.");
      await loadRows();
      return;
    }

    const body = normalizeBody(Object.fromEntries(fd.entries()));
    const editing = modal.type === "edit";
    const id = editing ? rowId(modal.row) : "";
    const res = await fetch(`${API}${resource.endpoint}${editing ? `/${id}` : ""}`, {
      method: editing ? "PUT" : "POST",
      headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
      body: JSON.stringify(body),
    });

    if (!res.ok) {
      setMessage(editing ? "Update failed. Check fields and permissions." : "Create failed. Check fields and permissions.");
      return;
    }

    setModal({ type: "none" });
    setMessage(editing ? "Record updated." : "Record created.");
    await loadRows();
  };

  const deleteRecord = async () => {
    if (modal.type !== "delete") return;
    const res = await fetch(`${API}${resource.endpoint}/${rowId(modal.row)}`, {
      method: "DELETE",
      headers: { Authorization: `Bearer ${token}` },
    });
    if (!res.ok) {
      setMessage("Delete failed. This record may be protected or linked.");
      return;
    }
    setModal({ type: "none" });
    setMessage("Record deleted.");
    await loadRows();
  };

  const updateStatus = async (row: Row, next: string) => {
    if (!resource.statusField) return;
    const field = resource.statusField;
    const id = rowId(row);
    const current = String(row[field] ?? "").toLowerCase();
    const nextValue = String(next).toLowerCase();
    if (nextValue === current) return;
    setMessage("");
    const isApproval = field === "approval_status";
    let url = `${API}/admin/${resource.key}/${id}/status`;
    let body: Record<string, unknown> = { [field]: next };
    if (isApproval && nextValue === "approved") {
      url = `${API}/admin/${resource.key}/${id}/approve`;
      body = {};
    }
    try {
      const res = await fetch(url, {
        method: "PATCH",
        headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
        body: JSON.stringify(body),
      });
      if (!res.ok) {
        const err = await res.json().catch(() => ({}));
        toast.error(err.message || "Status update failed.");
        return;
      }
      toast.success(isApproval && nextValue === "approved" ? `${singular(resource.title)} approved. ID card link emailed.` : `${singular(resource.title)} marked ${labelize(next)}.`);
      await loadRows();
    } catch {
      toast.error("Network error. Please try again.");
    }
  };

  const sendReply = async (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    setMessage("");

    const fd = new FormData(event.currentTarget);
    const reply = fd.get("reply") as string;
    if (!reply || reply.trim().length < 1) {
      setMessage("Reply message is required.");
      return;
    }

    if (modal.type !== "reply") return;
    const id = rowId(modal.row);
    const res = await fetch(`${API}/admin/enquiries/${id}/reply`, {
      method: "POST",
      headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
      body: JSON.stringify({ reply }),
    });

    if (!res.ok) {
      setMessage("Failed to send reply.");
      toast.error("Failed to send reply.");
      return;
    }

    setModal({ type: "none" });
    setMessage("Reply sent successfully.");
    toast.success("Reply sent successfully.");
    await loadRows();
  };

  const exportDonations = async () => {
    const res = await fetch(`${API}/admin/donations/export`, {
      headers: { Authorization: `Bearer ${token}` },
    });
    if (!res.ok) {
      setMessage("Export failed.");
      return;
    }
    const blob = await res.blob();
    const url = URL.createObjectURL(blob);
    const link = document.createElement("a");
    link.href = url;
    link.download = "donations.xlsx";
    link.click();
    URL.revokeObjectURL(url);
  };

  const exportBeneficiaries = async () => {
    const params = new URLSearchParams();
    if (filters.status) params.set("status", filters.status);
    const res = await fetch(`${API}/admin/reports/beneficiaries?${params}`, {
      headers: { Authorization: `Bearer ${token}` },
    });
    if (!res.ok) {
      setMessage("Export failed.");
      return;
    }
    const blob = await res.blob();
    const url = URL.createObjectURL(blob);
    const link = document.createElement("a");
    link.href = url;
    link.download = "beneficiaries_report.xlsx";
    link.click();
    URL.revokeObjectURL(url);
  };

  return (
    <div className="mx-auto max-w-7xl">
      <div className="mb-5 flex flex-wrap items-end justify-between gap-3">
        <div>
          <p className="text-[10px] font-black uppercase tracking-[0.14em] text-[var(--color-primary)]">Management</p>
          <h1 className="mt-0.5 text-xl font-black text-[var(--color-ink)] md:text-2xl">{resource.title}</h1>
          <p className="mt-1 max-w-2xl text-[11.5px] font-medium text-[var(--color-ink-mute)]">{resource.description}</p>
        </div>
        <div className="flex flex-wrap gap-2">
          {resource.key === "donations" ? (
            <button onClick={exportDonations} className="rounded-md border border-[var(--color-hairline)] bg-white px-3 py-1.5 text-[11.5px] font-black text-[var(--color-ink)] shadow-sm transition hover:border-[var(--color-primary)] hover:text-[var(--color-primary)]">
              Export Excel
            </button>
          ) : null}
          {resource.key === "beneficiaries" ? (
            <button onClick={exportBeneficiaries} className="rounded-md border border-[var(--color-hairline)] bg-white px-3 py-1.5 text-[11.5px] font-black text-[var(--color-ink)] shadow-sm transition hover:border-[var(--color-primary)] hover:text-[var(--color-primary)]">
              Export Excel
            </button>
          ) : null}
          {canCreate ? (
            <button onClick={() => setModal({ type: "create" })} className="rounded-md bg-gradient-to-r from-[var(--color-primary)] to-[var(--color-primary-deep)] px-3 py-1.5 text-[11.5px] font-black text-white shadow-[0_8px_18px_rgba(93,111,241,0.28)] transition hover:brightness-105">
              Add New
            </button>
          ) : null}
        </div>
      </div>

      {message ? <p className="mb-4 rounded-md border border-[var(--color-hairline)] bg-[var(--color-canvas-soft)] p-3 text-[11.5px] font-bold text-[var(--color-ink)]">{message}</p> : null}

      <section className="overflow-hidden rounded-xl border border-[var(--color-hairline)] bg-white shadow-sm">
        <div className="flex flex-wrap items-center justify-between gap-3 border-b border-[var(--color-hairline)] bg-gradient-to-r from-[var(--color-canvas-soft)] to-[var(--color-canvas-soft)] px-4 py-3">
          <div className="flex items-center gap-2.5">
            <span className="grid h-7 w-7 place-items-center rounded-md bg-gradient-to-br from-[var(--color-primary)] to-[var(--color-primary-deep)] text-[12px] font-black text-white shadow-[0_4px_10px_rgba(91,98,240,0.3)]">
              {singular(resource.title)[0].toUpperCase()}
            </span>
            <div>
              <p className="text-[12px] font-black text-[var(--color-ink)]">{filteredRows.length} records</p>
              <p className="text-[10px] font-semibold text-[var(--color-ink-mute)]">Search, edit and manage this section.</p>
            </div>
          </div>
          <div className="flex items-center gap-2.5 overflow-x-auto">
            {resource.filters ? resource.filters.map((f) => (
              <select
                key={f.name}
                value={filters[f.name] || ""}
                onChange={(e) => changeFilter(f.name, e.target.value)}
                className="h-9 min-w-[130px] flex-1 rounded-md border border-[var(--color-hairline)] bg-white px-2.5 text-[11.5px] font-bold text-[var(--color-ink)] shadow-sm outline-none transition focus:border-[var(--color-primary)] focus:ring-2 focus:ring-[var(--color-primary)]/15"
              >
                <option value="">{f.label}</option>
                {(filterOptions[f.name] || []).map((opt) => (
                  <option key={opt} value={opt}>{opt}</option>
                ))}
              </select>
            )) : null}
            <label className="relative flex h-9 min-w-[160px] flex-1 items-center lg:max-w-sm">
              <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" className="pointer-events-none absolute left-3 text-[var(--color-ink-mute)]">
                <circle cx="11" cy="11" r="8" />
                <line x1="21" y1="21" x2="16.65" y2="16.65" />
              </svg>
              <input
                value={query}
                onChange={(event) => changeQuery(event.target.value)}
                className="h-9 w-full rounded-md border border-[var(--color-hairline)] bg-white pl-8 pr-3 text-[12px] font-semibold text-[var(--color-ink)] shadow-sm outline-none transition placeholder:text-[var(--color-ink-mute)] focus:border-[var(--color-primary)] focus:ring-2 focus:ring-[var(--color-primary)]/15"
                placeholder={`Search ${resource.title.toLowerCase()}...`}
              />
            </label>
          </div>
        </div>

        <div className="overflow-x-auto">
          <table className="w-full min-w-[1020px] border-collapse text-left text-[12px]">
            <thead>
              <tr className="border-b border-[var(--color-hairline)] bg-[var(--color-canvas-soft)]">
                {resource.columns.map((column) => (
                  <th key={column} className="px-4 py-3 text-[10px] font-black uppercase tracking-[0.08em] text-[var(--color-ink-mute)]">{labelize(column)}</th>
                ))}
                <th className="px-4 py-3 text-right text-[10px] font-black uppercase tracking-[0.08em] text-[var(--color-ink-mute)]">Actions</th>
              </tr>
            </thead>
            <tbody>
              {loading ? (
                <tr><td className="px-4 py-6 text-[12px] font-bold text-[var(--color-ink-mute)]" colSpan={resource.columns.length + 1}>Loading records...</td></tr>
              ) : filteredRows.length ? pageRows.map((row, index) => (
                <tr key={`${rowId(row) || index}`} className="group border-b border-[var(--color-hairline)] bg-white align-middle transition last:border-b-0 hover:bg-[var(--color-canvas-soft)]">
                  {resource.columns.map((column) => (
                    <td key={column} className="max-w-[260px] px-4 py-3 text-[12px] font-medium text-[var(--color-ink)]">
                      {resource.statusEditable && column === resource.statusField ? (
                        <StatusSelect value={formatValue(row[column])} options={resource.statusOptions || [formatValue(row[column])]} onChange={(next) => updateStatus(row, next)} />
                      ) : column === resource.statusField || column === "status" || column === "approval_status" || column === "payment_status" || column === "event_status" ? (
                        <StatusPill value={formatValue(row[column])} />
                      ) : isImageColumn(column) ? (
                        <ImageThumb url={resolveImageUrl(row[column] == null ? "" : String(row[column]))} />
                      ) : formatValue(row[column])}
                    </td>
                  ))}
                  <td className="px-4 py-3">
                    <div className="flex justify-end gap-1.5">
                      {resource.key === "enquiries" ? (
                        <button onClick={() => setModal({ type: "reply", row })} className="flex items-center gap-1.5 rounded-md border border-[var(--color-hairline)] bg-white px-2.5 py-1.5 text-[11px] font-black text-[var(--color-ink)] transition hover:border-[var(--color-primary-deep)] hover:bg-[var(--color-canvas-soft)] hover:text-[var(--color-primary-deep)]">Reply</button>
                      ) : null}
                      {canEdit ? <button onClick={() => setModal({ type: "edit", row })} className="flex items-center gap-1.5 rounded-md border border-[var(--color-hairline)] bg-white px-2.5 py-1.5 text-[11px] font-black text-[var(--color-ink)] transition hover:border-[var(--color-primary)] hover:bg-[var(--color-canvas-soft)] hover:text-[var(--color-primary)]">
                        <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" /><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" /></svg>
                        Edit
                      </button> : null}
                      {canDelete ? <button onClick={() => setModal({ type: "delete", row })} className="flex items-center gap-1.5 rounded-md bg-[var(--color-canvas-soft)] px-2.5 py-1.5 text-[11px] font-black text-[var(--color-ink)] transition hover:bg-[var(--color-ink)] hover:text-white">
                        <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><polyline points="3 6 5 6 21 6" /><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" /></svg>
                        Delete
                      </button> : null}
                    </div>
                  </td>
                </tr>
              )) : (
                <tr><td className="px-4 py-12 text-center">
                  <div className="flex flex-col items-center gap-2">
                    <span className="grid h-11 w-11 place-items-center rounded-full bg-[var(--color-canvas-soft)] text-[var(--color-ink-mute)]">
                      <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="11" cy="11" r="8" /><line x1="21" y1="21" x2="16.65" y2="16.65" /></svg>
                    </span>
                    <p className="text-[12.5px] font-black text-[var(--color-ink)]">No records found</p>
                    <p className="text-[11px] font-semibold text-[var(--color-ink-mute)]">{query || Object.values(filters).some(Boolean) ? "Try adjusting your search or filters." : "There are no " + resource.title.toLowerCase() + " yet."}</p>
                  </div>
                </td></tr>
              )}
            </tbody>
          </table>
        </div>

        <TablePagination total={filteredRows.length} page={page} pageSize={PAGE_SIZE} onChange={setPage} label={resource.title.toLowerCase()} />
      </section>

      {modal.type === "create" ? (
        <RecordModal
          title={`Add ${singular(resource.title)}`}
          fields={createFields}
          onClose={() => setModal({ type: "none" })}
          onSubmit={saveRecord}
        />
      ) : null}

      {modal.type === "edit" ? (
        <RecordModal
          title={`Edit ${singular(resource.title)}`}
          fields={editFields}
          row={modal.row}
          onClose={() => setModal({ type: "none" })}
          onSubmit={saveRecord}
        />
      ) : null}

      {modal.type === "delete" ? (
        <ConfirmModal
          title={`Delete ${singular(resource.title)}`}
          text={`This will permanently delete record ${rowId(modal.row)} from ${resource.title}.`}
          onClose={() => setModal({ type: "none" })}
          onConfirm={deleteRecord}
        />
      ) : null}

      {modal.type === "reply" ? (
        <ReplyModal
          row={modal.row}
          onClose={() => setModal({ type: "none" })}
          onSubmit={sendReply}
        />
      ) : null}

      <Toaster position="top-center" toastOptions={{ duration: 4000, style: { background: "#333", color: "#fff", fontSize: "14px", fontWeight: 600 } }} />
    </div>
  );
}

function RecordModal({
  title,
  fields,
  row,
  onClose,
  onSubmit,
}: {
  title: string;
  fields: AdminField[];
  row?: Row;
  onClose: () => void;
  onSubmit: (event: React.FormEvent<HTMLFormElement>) => void;
}) {
  return (
    <div className="fixed inset-0 z-[80] grid place-items-center bg-black/45 p-4 backdrop-blur-sm">
      <section className="max-h-[88vh] w-full max-w-3xl overflow-hidden rounded-md bg-white shadow-2xl">
        <header className="flex items-center justify-between border-b border-[var(--color-hairline)] px-4 py-3">
          <div>
            <p className="text-[10px] font-black uppercase tracking-[0.14em] text-[var(--color-primary)]">Record form</p>
            <h2 className="mt-0.5 text-[13.5px] font-black text-[var(--color-ink)]">{title}</h2>
          </div>
          <button onClick={onClose} className="rounded-md border border-[var(--color-hairline)] px-2.5 py-1.5 text-[11.5px] font-black text-[var(--color-ink)] transition hover:border-[var(--color-primary)] hover:text-[var(--color-primary)]">Close</button>
        </header>
        <form onSubmit={onSubmit} className="max-h-[70vh] overflow-y-auto p-4">
          <div className="grid gap-3 md:grid-cols-2">
            {fields.map((field) => <Field key={field.name} field={field} row={row} />)}
          </div>
          <div className="mt-4 flex justify-end gap-3 border-t border-[var(--color-hairline)] pt-4">
            <button type="button" onClick={onClose} className="rounded-md border border-[var(--color-hairline)] px-3 py-2 text-[11.5px] font-black text-[var(--color-ink)] transition hover:border-[var(--color-primary)] hover:text-[var(--color-primary)]">Cancel</button>
            <button className="rounded-md bg-gradient-to-r from-[var(--color-primary)] to-[var(--color-primary-deep)] px-4 py-2 text-[11.5px] font-black text-white shadow-[0_8px_18px_rgba(93,111,241,0.28)] transition hover:brightness-105">Save changes</button>
          </div>
        </form>
      </section>
    </div>
  );
}

function ConfirmModal({
  title,
  text,
  onClose,
  onConfirm,
}: {
  title: string;
  text: string;
  onClose: () => void;
  onConfirm: () => void;
}) {
  return (
    <div className="fixed inset-0 z-[80] grid place-items-center bg-black/45 p-4 backdrop-blur-sm">
      <section className="w-full max-w-md rounded-md bg-white p-4 shadow-2xl">
        <p className="text-[10px] font-black uppercase tracking-[0.14em] text-[var(--color-ink)]">Confirm delete</p>
        <h2 className="mt-1.5 text-[13.5px] font-black text-[var(--color-ink)]">{title}</h2>
        <p className="mt-2 text-[12px] leading-6 text-[var(--color-ink-mute)]">{text}</p>
        <div className="mt-4 flex justify-end gap-2">
          <button onClick={onClose} className="rounded-md border border-[var(--color-hairline)] px-3 py-2 text-[11.5px] font-black text-[var(--color-ink)] transition hover:border-[var(--color-primary)] hover:text-[var(--color-primary)]">Cancel</button>
          <button onClick={onConfirm} className="rounded-md bg-[var(--color-ink)] px-3 py-2 text-[11.5px] font-black text-white transition hover:bg-[var(--color-ink)]">Delete</button>
        </div>
      </section>
    </div>
  );
}

function Field({ field, row }: { field: AdminField; row?: Row }) {
  const value = field.type === "password" ? "" : formValue(row?.[field.name] ?? null, field.type);
  const className = field.type === "textarea" || field.type === "editor" ? "md:col-span-2" : "";

  if (field.type === "image") {
    return <ImageFieldUpload field={field} row={row} />;
  }

  if (field.type === "images") {
    return <ImagesFieldUpload field={field} row={row} />;
  }

  if (field.type === "editor") {
    return <FieldEditor field={field} row={row} />;
  }

  if (field.type === "textarea") {
    return (
      <label className={className}>
        <span className="mb-1.5 block text-[11.5px] font-black text-[var(--color-ink)]">{field.label}</span>
        <textarea name={field.name} required={field.required} defaultValue={value} className="min-h-24 w-full rounded-md border border-[var(--color-hairline)] bg-[var(--color-canvas-soft)] px-2.5 py-2 text-[12px] font-medium outline-none focus:border-[var(--color-primary)] focus:ring-4 focus:ring-[var(--color-primary)]/10" />
      </label>
    );
  }

  if (field.type === "select") {
    return (
      <label className={className}>
        <span className="mb-1.5 block text-[11.5px] font-black text-[var(--color-ink)]">{field.label}</span>
        <select name={field.name} required={field.required} className="w-full rounded-md border border-[var(--color-hairline)] bg-[var(--color-canvas-soft)] px-2.5 py-2 text-[12px] font-medium outline-none focus:border-[var(--color-primary)] focus:ring-4 focus:ring-[var(--color-primary)]/10" defaultValue={value || field.options?.[0]}>
          {field.options?.map((option) => <option key={option}>{option}</option>)}
        </select>
      </label>
    );
  }

  const inputType = field.type === "tel" ? "tel" : field.type || "text";

  return (
    <label className={className}>
      <span className="mb-1.5 block text-[11.5px] font-black text-[var(--color-ink)]">{field.label}</span>
      <input name={field.name} type={inputType} required={field.required} defaultValue={value} className="w-full rounded-md border border-[var(--color-hairline)] bg-[var(--color-canvas-soft)] px-2.5 py-2 text-[12px] font-medium outline-none focus:border-[var(--color-primary)] focus:ring-4 focus:ring-[var(--color-primary)]/10" />
    </label>
  );
}

function ImageThumb({ url }: { url: string }) {
  return url ? <img src={url} alt="" className="h-10 w-10 rounded-md object-cover border border-[var(--color-hairline)]" /> : <span className="text-[11px] font-medium text-[var(--color-ink-mute)]">No image</span>;
}

function ImageFieldUpload({ field, row }: { field: AdminField; row?: Row }) {
  const initialUrl = (row?.[field.name] as string) || "";
  const [preview, setPreview] = useState(initialUrl);

  const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (file) setPreview(URL.createObjectURL(file));
  };

  return (
    <label className="md:col-span-2">
      <span className="mb-1.5 block text-[11.5px] font-black text-[var(--color-ink)]">{field.label}</span>
      <input name={field.name} type="hidden" value={preview} />
      <div className="flex items-center gap-3">
        {resolveImageUrl(preview) ? (
          <img src={resolveImageUrl(preview)} alt="" className="h-14 w-14 rounded-md object-cover border border-[var(--color-hairline)]" />
        ) : (
          <div className="flex h-14 w-14 items-center justify-center rounded-md border border-dashed border-[var(--color-hairline)] text-[10px] font-bold text-[var(--color-ink-mute)]">No img</div>
        )}
        <div className="flex-1">
          <label className="flex cursor-pointer items-center justify-center gap-2 rounded-md border border-[var(--color-hairline)] bg-[var(--color-canvas-soft)] px-3 py-2 text-[11.5px] font-bold text-[var(--color-ink)] transition hover:bg-[var(--color-canvas-soft)]">
            Choose file
            <input name="file" type="file" accept="image/*" className="hidden" onChange={handleFileChange} />
          </label>
          {preview && <p className="mt-1 text-[10px] text-[var(--color-ink-mute)] truncate max-w-[300px]">{preview}</p>}
        </div>
      </div>
    </label>
  );
}

function ImagesFieldUpload({ field, row }: { field: AdminField; row?: Row }) {
  const initialImages: string[] = (() => {
    const val = row?.[field.name];
    if (!val) return [];
    if (typeof val === "string") {
      try { return JSON.parse(val); } catch { return []; }
    }
    if (Array.isArray(val)) return val;
    return [];
  })();

  const [images, setImages] = useState<string[]>(initialImages);
  const [previews, setPreviews] = useState<string[]>([]);

  const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const files = e.target.files;
    if (!files) return;
    const newPreviews = Array.from(files).map((f) => URL.createObjectURL(f));
    setPreviews((prev) => [...prev, ...newPreviews]);
  };

  const removeImage = (index: number) => {
    setImages((prev) => prev.filter((_, i) => i !== index));
  };

  const removePreview = (index: number) => {
    setPreviews((prev) => prev.filter((_, i) => i !== index));
  };

  return (
    <label className="md:col-span-2">
      <span className="mb-1.5 block text-[11.5px] font-black text-[var(--color-ink)]">{field.label}</span>
      <input name={field.name} type="hidden" value={JSON.stringify(images)} />
      <div className="grid grid-cols-3 gap-2">
        {images.filter(Boolean).map((url, i) => (
          <div key={`existing-${i}`} className="relative group">
            <img src={resolveImageUrl(url)} alt="" className="h-20 w-full rounded-md object-cover border border-[var(--color-hairline)]" />
            <button
              type="button"
              onClick={() => removeImage(i)}
              className="absolute -top-1.5 -right-1.5 flex h-5 w-5 items-center justify-center rounded-full bg-[var(--color-ink)] text-white text-[10px] font-bold opacity-0 group-hover:opacity-100 transition"
            >
              &times;
            </button>
          </div>
        ))}
        {previews.map((url, i) => (
          <div key={`preview-${i}`} className="relative group">
            <img src={resolveImageUrl(url)} alt="" className="h-20 w-full rounded-md object-cover border border-[var(--color-hairline)]" />
            <button
              type="button"
              onClick={() => removePreview(i)}
              className="absolute -top-1.5 -right-1.5 flex h-5 w-5 items-center justify-center rounded-full bg-[var(--color-ink)] text-white text-[10px] font-bold opacity-0 group-hover:opacity-100 transition"
            >
              &times;
            </button>
          </div>
        ))}
        <label className="flex h-20 cursor-pointer items-center justify-center rounded-md border-2 border-dashed border-[var(--color-hairline)] text-[10px] font-bold text-[var(--color-ink-mute)] transition hover:border-[var(--color-primary)] hover:text-[var(--color-primary)]">
          + Add Images
          <input type="file" accept="image/*" multiple className="hidden" onChange={handleFileChange} />
        </label>
      </div>
      <p className="mt-1.5 text-[10px] text-[var(--color-ink-mute)]">Upload multiple images for this campaign gallery. New files upload on save.</p>
    </label>
  );
}

function ReplyModal({
  row,
  onClose,
  onSubmit,
}: {
  row: Row;
  onClose: () => void;
  onSubmit: (event: React.FormEvent<HTMLFormElement>) => void;
}) {
  const [replyText, setReplyText] = useState("");

  return (
    <div className="fixed inset-0 z-[80] grid place-items-center bg-black/45 p-4 backdrop-blur-sm">
      <section className="w-full max-w-2xl rounded-md bg-white shadow-2xl">
        <header className="flex items-center justify-between border-b border-[var(--color-hairline)] px-4 py-3">
          <div>
            <p className="text-[10px] font-black uppercase tracking-[0.14em] text-[var(--color-primary-deep)]">Reply to enquiry</p>
            <h2 className="mt-0.5 text-[13.5px] font-black text-[var(--color-ink)]">Reply to {row.name}</h2>
          </div>
          <button onClick={onClose} className="rounded-md border border-[var(--color-hairline)] px-2.5 py-1.5 text-[11.5px] font-black text-[var(--color-ink)] transition hover:border-[var(--color-primary)] hover:text-[var(--color-primary)]">Close</button>
        </header>
        <form onSubmit={onSubmit} className="p-4">
          <div className="mb-3 rounded-md border border-[var(--color-hairline)] bg-[var(--color-canvas-soft)] p-3">
            <p className="text-[10px] font-black uppercase tracking-wider text-[var(--color-ink-mute)] mb-1">Original message from {row.name}</p>
            <p className="text-[12px] font-medium text-[var(--color-ink)] whitespace-pre-wrap">{String(row.message || "")}</p>
            <p className="mt-1.5 text-[10px] text-[var(--color-ink-mute)]">Email: {String(row.email || "")} | Phone: {String(row.phone || "")}</p>
          </div>
          <label>
            <span className="mb-1.5 block text-[11.5px] font-black text-[var(--color-ink)]">Your Reply *</span>
            <textarea
              name="reply"
              required
              value={replyText}
              onChange={(e) => setReplyText(e.target.value)}
              placeholder="Type your reply here..."
              className="min-h-28 w-full rounded-md border border-[var(--color-hairline)] bg-[var(--color-canvas-soft)] px-2.5 py-2 text-[12px] font-medium outline-none focus:border-[var(--color-primary-deep)] focus:ring-4 focus:ring-[var(--color-primary-deep)]/10"
            />
          </label>
          <div className="mt-4 flex justify-end gap-2 border-t border-[var(--color-hairline)] pt-4">
            <button type="button" onClick={onClose} className="rounded-md border border-[var(--color-hairline)] px-3 py-2 text-[11.5px] font-black text-[var(--color-ink)] transition hover:border-[var(--color-primary)] hover:text-[var(--color-primary)]">Cancel</button>
            <button type="submit" disabled={!replyText.trim()} className="rounded-md bg-gradient-to-r from-[var(--color-primary-deep)] to-[var(--color-primary-deep)] px-4 py-2 text-[11.5px] font-black text-white shadow-[0_8px_18px_rgba(26,107,74,0.28)] disabled:opacity-50 transition hover:brightness-105">Send Reply</button>
          </div>
        </form>
      </section>
    </div>
  );
}

const statusDisplayMap: Record<string, string> = {
  created: "Pending",
  paid: "Success",
  failed: "Failed",
  refunded: "Refunded",
  pending: "Pending",
  approved: "Approved",
  rejected: "Rejected",
  active: "Active",
  inactive: "Inactive",
  replied: "Replied",
  closed: "Closed",
  draft: "Draft",
  published: "Published",
  upcoming: "Upcoming",
  ongoing: "Ongoing",
  completed: "Completed",
  cancelled: "Cancelled",
  free: "Free",
  suspended: "Suspended",
  expired: "Expired",
  lifetime: "Lifetime",
  sent: "Sent",
  scheduled: "Scheduled",
};

function StatusPill({ value }: { value: string }) {
  const normalized = value.toLowerCase();
  const display = statusDisplayMap[normalized] || value;
  const tone = normalized.includes("pending") || normalized.includes("created") || normalized.includes("scheduled")
    ? "bg-[var(--color-canvas-soft)] text-[#b45309]"
    : normalized.includes("failed") || normalized.includes("rejected") || normalized.includes("inactive") || normalized.includes("cancelled") || normalized.includes("suspended") || normalized.includes("expired")
      ? "bg-[var(--color-canvas-soft)] text-[var(--color-ink)]"
      : normalized.includes("paid") || normalized.includes("success") || normalized.includes("approved") || normalized.includes("active") || normalized.includes("published") || normalized.includes("completed") || normalized.includes("replied") || normalized.includes("sent") || normalized.includes("lifetime")
        ? "bg-[var(--color-canvas-soft)] text-[var(--color-primary-deep)]"
        : normalized.includes("refunded")
          ? "bg-[var(--color-canvas-soft)] text-[var(--color-primary-deep)]"
          : "bg-[var(--color-canvas-soft)] text-[var(--color-ink-mute)]";

  return <span className={`inline-flex rounded-full px-3 py-1 text-xs font-black ${tone}`}>{display}</span>;
}

function StatusSelect({ value, options, onChange }: { value: string; options: string[]; onChange: (next: string) => void }) {
  const normalized = value.toLowerCase();
  const tone = normalized.includes("pending")
    ? "border-[var(--color-hairline-cool)] bg-[var(--color-canvas-soft)] text-[#b45309]"
    : normalized.includes("approved") || normalized.includes("active") || normalized.includes("published")
      ? "border-[var(--color-canvas-soft)] bg-[var(--color-canvas-soft)] text-[var(--color-primary-deep)]"
      : normalized.includes("rejected") || normalized.includes("inactive") || normalized.includes("draft")
        ? "border-[var(--color-hairline)] bg-[var(--color-canvas-soft)] text-[var(--color-ink)]"
        : "border-[var(--color-hairline)] bg-white text-[var(--color-ink)]";

  return (
    <div className="relative inline-block">
      <select
        value={normalized}
        onChange={(e) => onChange(e.target.value)}
        className={`h-8 cursor-pointer appearance-none rounded-full border py-0 pl-3 pr-8 text-[11px] font-black shadow-sm outline-none transition ${tone} hover:brightness-[1.02]`}
      >
        {options.map((option) => (
          <option key={option} value={option.toLowerCase()}>{statusDisplayMap[option.toLowerCase()] || labelize(option)}</option>
        ))}
      </select>
      <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" className="pointer-events-none absolute right-2.5 top-1/2 -translate-y-1/2 text-current opacity-70">
        <polyline points="6 9 12 15 18 9" />
      </svg>
    </div>
  );
}

function rowId(row: Row) {
  return row.id || row.receipt_no || "";
}

function normalizeBody(body: Record<string, FormDataEntryValue>) {
  return Object.fromEntries(
    Object.entries(body).map(([key, value]) => [key, value === "" ? null : value])
  );
}

function singular(value: string) {
  if (value.endsWith("ies")) return value.slice(0, -3) + "y";
  if (value.endsWith("s")) return value.slice(0, -1);
  return value;
}

function labelize(value: string) {
  return value.replaceAll("_", " ").replace(/\b\w/g, (char) => char.toUpperCase());
}

function formValue(value: Row[string], type?: AdminField["type"]) {
  if (value === null || value === undefined) return "";
  const text = String(value);
  if (type === "date" && /^\d{4}-\d{2}-\d{2}/.test(text)) return text.slice(0, 10);
  if (type === "datetime-local" && /^\d{4}-\d{2}-\d{2}/.test(text)) return text.slice(0, 16);
  return text;
}

function formatValue(value: Row[string]) {
  if (value === null || value === undefined || value === "") return "-";
  const text = String(value);
  if (/^\d{4}-\d{2}-\d{2}T/.test(text)) return new Date(text).toLocaleString("en-IN");
  return text.length > 140 ? `${text.slice(0, 140)}...` : text;
}

function isImageColumn(column: string) {
  return column.includes("img") || column.includes("image") || column.includes("photo");
}
