"use client";

import { useEffect, useMemo, useState } from "react";
import useSWR from "swr";
import { Download, Pencil, Plus, Printer, Search, Trash2 } from "lucide-react";
import { toast } from "sonner";
import { fetcher, apiSend, ApiError } from "@/lib/api";
import { cetakHtml } from "@/lib/cetak";
import { unduhCsv } from "@/lib/sekretariat";
import { useSession } from "@/lib/use-session";
import { PageHeader } from "@/components/page-header";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Select } from "@/components/ui/select";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { Textarea } from "@/components/ui/textarea";
import { cn, formatDateShort } from "@/lib/utils";

export type CrudRow = Record<string, unknown>;

export interface CrudOption {
  value: string;
  label: string;
}

export interface CrudField {
  name: string;
  label: string;
  type?:
    | "text"
    | "textarea"
    | "number"
    | "date"
    | "select"
    | "checkbox"
    | "url"
    | "email"
    | "password";
  options?: CrudOption[];
  /** Opsi dinamis dari prop `options` milik SimpleCrud. */
  optionsKey?: string;
  required?: boolean;
  placeholder?: string;
  help?: string;
  defaultValue?: string | boolean;
  full?: boolean;
  editOnly?: boolean;
  createOnly?: boolean;
}

export interface CrudColumn {
  name: string;
  label: string;
  render?: (row: CrudRow) => React.ReactNode;
  className?: string;
  hideOnMobile?: boolean;
}

export interface CrudEkspor {
  fileName: string;
  header: string[];
  values: (row: CrudRow) => (string | number)[];
}

export interface SimpleCrudProps {
  title: string;
  description?: string;
  entity?: string;
  endpoint?: string;
  fields: CrudField[];
  columns: CrudColumn[];
  query?: Record<string, string | number | undefined>;
  searchFields?: string[];
  searchPlaceholder?: string;
  createLabel?: string;
  emptyText?: string;
  headerExtra?: React.ReactNode;
  toolbar?: React.ReactNode;
  transform?: (values: Record<string, unknown>) => Record<string, unknown>;
  rowActions?: (row: CrudRow, helpers: { refresh: () => void; edit: (row: CrudRow) => void }) => React.ReactNode;
  onSaved?: () => void;
  canWrite?: boolean;
  filter?: (row: CrudRow) => boolean;
  showNo?: boolean;
  selectable?: boolean;
  actionOnLeft?: boolean;
  options?: Record<string, CrudOption[]>;
  ekspor?: CrudEkspor;
  cetak?: (rows: CrudRow[]) => { judul: string; isi: string };
}

function isoDate(value: unknown) {
  if (!value) return "";
  return String(value).slice(0, 10);
}

function defaultValue(field: CrudField): string | boolean {
  if (field.defaultValue !== undefined) return field.defaultValue;
  if (field.type === "checkbox") return true;
  if (field.type === "select" && field.options?.length) return field.options[0].value;
  return "";
}

export function SimpleCrud({
  title,
  description,
  entity,
  endpoint,
  fields,
  columns,
  query,
  searchFields,
  searchPlaceholder = "Cari data…",
  createLabel = "Tambah",
  emptyText = "Belum ada data.",
  headerExtra,
  toolbar,
  transform,
  rowActions,
  onSaved,
  canWrite,
  filter,
  showNo,
  selectable,
  actionOnLeft,
  options,
  ekspor,
  cetak,
}: SimpleCrudProps) {
  const { user } = useSession();
  const writable = canWrite ?? user?.role === "ADMIN";
  const base = endpoint ?? `/master/${entity}`;
  const params = new URLSearchParams();
  if (query) {
    for (const [key, value] of Object.entries(query)) {
      if (value !== undefined && value !== "") params.set(key, String(value));
    }
  }
  const url = params.toString() ? `${base}?${params.toString()}` : base;
  const { data, isLoading, mutate } = useSWR<CrudRow[]>(url, fetcher);
  const rows = useMemo(() => data ?? [], [data]);

  const [kata, setKata] = useState("");
  const [formOpen, setFormOpen] = useState(false);
  const [editing, setEditing] = useState<CrudRow | null>(null);
  const [form, setForm] = useState<Record<string, string | boolean>>({});
  const [saving, setSaving] = useState(false);
  const [deleteTarget, setDeleteTarget] = useState<CrudRow | null>(null);
  const [deleting, setDeleting] = useState(false);
  const [selected, setSelected] = useState<(string | number)[]>([]);
  const [bulkOpen, setBulkOpen] = useState(false);
  const [bulkDeleting, setBulkDeleting] = useState(false);

  const keys = searchFields ?? columns.map((column) => column.name);
  const filtered = rows.filter((row) => {
    if (filter && !filter(row)) return false;
    if (!kata.trim()) return true;
    return keys.some((key) =>
      String(row[key] ?? "")
        .toLowerCase()
        .includes(kata.trim().toLowerCase()),
    );
  });

  useEffect(() => {
    setSelected([]);
  }, [kata, data]);

  const nomorKolom = Boolean(showNo || selectable);
  const bolehPilih = Boolean(selectable && writable);
  const semuaTerpilih = filtered.length > 0 && selected.length === filtered.length;

  function toggleSemua() {
    setSelected(semuaTerpilih ? [] : filtered.map((row) => row.id as string | number));
  }

  function toggleSatu(id: string | number) {
    setSelected((current) =>
      current.includes(id) ? current.filter((item) => item !== id) : [...current, id],
    );
  }

  function jalankanEkspor() {
    if (!ekspor) return;
    if (!filtered.length) {
      toast.error("Tidak ada data untuk diekspor");
      return;
    }
    unduhCsv(
      `${ekspor.fileName}-${new Date().toISOString().slice(0, 10)}.csv`,
      ekspor.header,
      filtered.map((row) => ekspor.values(row).map((value) => String(value ?? ""))),
    );
    toast.success(`${filtered.length} data diekspor`);
  }

  function jalankanCetak() {
    if (!cetak) return;
    if (!filtered.length) {
      toast.error("Tidak ada data untuk dicetak");
      return;
    }
    const { judul, isi } = cetak(filtered);
    if (!cetakHtml(judul, isi)) {
      toast.error("Izinkan pop-up untuk mencetak");
    }
  }

  function openCreate() {
    const initial: Record<string, string | boolean> = {};
    for (const field of fields) {
      if (!field.editOnly) initial[field.name] = defaultValue(field);
    }
    if (query) {
      for (const [key, value] of Object.entries(query)) {
        if (value !== undefined) initial[key] = String(value);
      }
    }
    if (rowActions) {
      // biarkan query terisi juga untuk field tersembunyi
    }
    setEditing(null);
    setForm({ ...initial });
    setFormOpen(true);
  }

  function openEdit(row: CrudRow) {
    const values: Record<string, string | boolean> = {};
    for (const field of fields) {
      if (field.createOnly) continue;
      const raw = row[field.name];
      if (field.type === "date") values[field.name] = isoDate(raw);
      else if (field.type === "checkbox") values[field.name] = Boolean(raw);
      else values[field.name] = raw === null || raw === undefined ? "" : String(raw);
    }
    setEditing(row);
    setForm(values);
    setFormOpen(true);
  }

  async function save(event: React.FormEvent<HTMLFormElement>) {
    event.preventDefault();
    setSaving(true);
    try {
      const payload: Record<string, unknown> = { ...form };
      for (const field of fields) {
        if (field.type === "checkbox") payload[field.name] = Boolean(form[field.name]);
        if ((field.editOnly && !editing) || (field.createOnly && editing)) {
          delete payload[field.name];
        }
      }
      const body = transform ? transform(payload) : payload;
      if (editing) {
        await apiSend(`${base}/${editing.id}`, "PATCH", body);
        toast.success(`${title} diperbarui`);
      } else {
        await apiSend(base, "POST", body);
        toast.success(`${title} ditambahkan`);
      }
      setFormOpen(false);
      await mutate();
      onSaved?.();
    } catch (error) {
      toast.error(error instanceof ApiError ? error.message : "Gagal menyimpan data");
    } finally {
      setSaving(false);
    }
  }

  async function remove() {
    if (!deleteTarget) return;
    setDeleting(true);
    try {
      await apiSend(`${base}/${deleteTarget.id}`, "DELETE");
      toast.success("Data dihapus");
      setDeleteTarget(null);
      await mutate();
      onSaved?.();
    } catch (error) {
      toast.error(error instanceof ApiError ? error.message : "Gagal menghapus data");
    } finally {
      setDeleting(false);
    }
  }

  async function hapusTerpilih() {
    if (!selected.length) return;
    setBulkDeleting(true);
    const hasil = await Promise.allSettled(
      selected.map((id) => apiSend(`${base}/${id}`, "DELETE")),
    );
    const berhasil = hasil.filter((item) => item.status === "fulfilled").length;
    const gagal = hasil.length - berhasil;
    if (berhasil) toast.success(`${berhasil} data dihapus`);
    if (gagal) toast.error(`${gagal} data gagal dihapus`);
    setSelected([]);
    setBulkOpen(false);
    await mutate();
    onSaved?.();
    setBulkDeleting(false);
  }

  function display(column: CrudColumn, row: CrudRow) {
    if (column.render) return column.render(row);
    const value = row[column.name];
    if (value === null || value === undefined || value === "") return <span className="text-muted-foreground">-</span>;
    if (typeof value === "boolean") return <Badge variant={value ? "success" : "secondary"}>{value ? "Ya" : "Tidak"}</Badge>;
    if (/^\d{4}-\d{2}-\d{2}T/.test(String(value))) return formatDateShort(String(value));
    return String(value);
  }

  const kolomTotal = columns.length + 1 + (bolehPilih ? 1 : 0) + (nomorKolom ? 1 : 0);

  function aksiCell(row: CrudRow) {
    return (
      <TableCell>
        <div
          className={cn(
            "flex items-center gap-1",
            actionOnLeft ? "justify-start" : "justify-end",
          )}
        >
          {rowActions?.(row, { refresh: () => mutate(), edit: openEdit })}
          {writable && (
            <>
              <button
                type="button"
                onClick={() => openEdit(row)}
                className="rounded-md p-1.5 text-muted-foreground hover:bg-muted hover:text-foreground"
                aria-label="Ubah"
              >
                <Pencil className="h-4 w-4" />
              </button>
              <button
                type="button"
                onClick={() => setDeleteTarget(row)}
                className="rounded-md p-1.5 text-muted-foreground hover:bg-destructive/10 hover:text-destructive"
                aria-label="Hapus"
              >
                <Trash2 className="h-4 w-4" />
              </button>
            </>
          )}
        </div>
      </TableCell>
    );
  }

  return (
    <div className="space-y-5">
      <PageHeader
        title={title}
        description={description}
        action={
          <div className="flex items-center gap-2">
            {headerExtra}
            {writable && (
              <Button onClick={openCreate} size="sm">
                <Plus className="h-4 w-4" /> {createLabel}
              </Button>
            )}
          </div>
        }
      />

      <Card>
        <CardContent className="space-y-4 p-4 md:p-5">
          <div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
            <div className="relative w-full sm:max-w-xs">
              <Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
              <Input
                value={kata}
                onChange={(event) => setKata(event.target.value)}
                placeholder={searchPlaceholder}
                className="pl-9"
              />
            </div>
            <div className="flex flex-wrap items-center gap-2">
              {bolehPilih && (
                <Button
                  variant="outline"
                  size="sm"
                  disabled={!selected.length}
                  onClick={() => setBulkOpen(true)}
                >
                  <Trash2 className="h-4 w-4" /> Hapus{selected.length ? ` (${selected.length})` : ""}
                </Button>
              )}
              {ekspor && (
                <Button variant="outline" size="sm" onClick={jalankanEkspor}>
                  <Download className="h-4 w-4" /> Ekspor
                </Button>
              )}
              {cetak && (
                <Button variant="outline" size="sm" onClick={jalankanCetak}>
                  <Printer className="h-4 w-4" /> Cetak
                </Button>
              )}
              {toolbar}
              <span className="text-xs text-muted-foreground">{filtered.length} data</span>
            </div>
          </div>

          <div className="overflow-x-auto rounded-lg border border-border">
            <Table>
              <TableHeader>
                <TableRow>
                  {bolehPilih && (
                    <TableHead className="w-10">
                      <input
                        type="checkbox"
                        checked={semuaTerpilih}
                        onChange={toggleSemua}
                        aria-label="Pilih semua data"
                        className="h-4 w-4 accent-[hsl(var(--primary))]"
                      />
                    </TableHead>
                  )}
                  {nomorKolom && <TableHead className="w-14">No</TableHead>}
                  {actionOnLeft && <TableHead className="w-32">Aksi</TableHead>}
                  {columns.map((column) => (
                    <TableHead
                      key={column.name}
                      className={cn(column.hideOnMobile && "hidden md:table-cell", column.className)}
                    >
                      {column.label}
                    </TableHead>
                  ))}
                  {!actionOnLeft && <TableHead className="w-24 text-right">Aksi</TableHead>}
                </TableRow>
              </TableHeader>
              <TableBody>
                {isLoading && (
                  <TableRow>
                    <TableCell colSpan={kolomTotal} className="py-8 text-center text-sm text-muted-foreground">
                      Memuat data…
                    </TableCell>
                  </TableRow>
                )}
                {!isLoading &&
                  filtered.map((row, index) => (
                    <TableRow key={String(row.id)}>
                      {bolehPilih && (
                        <TableCell>
                          <input
                            type="checkbox"
                            checked={selected.includes(row.id as string | number)}
                            onChange={() => toggleSatu(row.id as string | number)}
                            aria-label={`Pilih ${String(row[columns[0]?.name] ?? row.id)}`}
                            className="h-4 w-4 accent-[hsl(var(--primary))]"
                          />
                        </TableCell>
                      )}
                      {nomorKolom && (
                        <TableCell className="text-muted-foreground">{index + 1}</TableCell>
                      )}
                      {actionOnLeft && aksiCell(row)}
                      {columns.map((column) => (
                        <TableCell
                          key={column.name}
                          className={cn(column.hideOnMobile && "hidden md:table-cell", column.className)}
                        >
                          {display(column, row)}
                        </TableCell>
                      ))}
                      {!actionOnLeft && aksiCell(row)}
                    </TableRow>
                  ))}
                {!isLoading && !filtered.length && (
                  <TableRow>
                    <TableCell colSpan={kolomTotal} className="py-8 text-center text-sm text-muted-foreground">
                      {emptyText}
                    </TableCell>
                  </TableRow>
                )}
              </TableBody>
            </Table>
          </div>
        </CardContent>
      </Card>

      <Dialog open={formOpen} onOpenChange={setFormOpen}>
        <DialogContent className="max-h-[90vh] overflow-y-auto sm:max-w-2xl">
          <DialogHeader>
            <DialogTitle>{editing ? `Ubah ${title}` : `Tambah ${title}`}</DialogTitle>
            <DialogDescription>Lengkapi data di bawah lalu simpan.</DialogDescription>
          </DialogHeader>
          <form onSubmit={save} className="space-y-4">
            <div className="grid gap-4 sm:grid-cols-2">
              {fields.map((field) => {
                if (field.editOnly && !editing) return null;
                if (field.createOnly && editing) return null;
                const value = form[field.name];
                return (
                  <div key={field.name} className={cn("space-y-1.5", (field.full || field.type === "textarea") && "sm:col-span-2")}>
                    <Label htmlFor={`field-${field.name}`}>{field.label}</Label>
                    {field.type === "textarea" ? (
                      <Textarea
                        id={`field-${field.name}`}
                        value={String(value ?? "")}
                        onChange={(event) => setForm((current) => ({ ...current, [field.name]: event.target.value }))}
                        placeholder={field.placeholder}
                        rows={4}
                        required={field.required}
                      />
                    ) : field.type === "select" ? (
                      <Select
                        id={`field-${field.name}`}
                        value={String(value ?? "")}
                        onChange={(event) => setForm((current) => ({ ...current, [field.name]: event.target.value }))}
                        required={field.required}
                      >
                        {!(field.optionsKey ? options?.[field.optionsKey] : field.options)?.some((option) => option.value === String(value ?? "")) && (
                          <option value="">— Pilih —</option>
                        )}
                        {(field.optionsKey ? options?.[field.optionsKey] : field.options)?.map((option) => (
                          <option key={option.value} value={option.value}>
                            {option.label}
                          </option>
                        ))}
                      </Select>
                    ) : field.type === "checkbox" ? (
                      <label className="flex h-10 items-center gap-2 rounded-lg border border-input bg-card px-3 text-sm">
                        <input
                          id={`field-${field.name}`}
                          type="checkbox"
                          checked={Boolean(value)}
                          onChange={(event) => setForm((current) => ({ ...current, [field.name]: event.target.checked }))}
                          className="h-4 w-4 accent-[hsl(var(--primary))]"
                        />
                        {field.help ?? "Aktif"}
                      </label>
                    ) : (
                      <Input
                        id={`field-${field.name}`}
                        type={
                          field.type === "number"
                            ? "number"
                            : field.type === "date"
                              ? "date"
                              : field.type === "url" || field.type === "email" || field.type === "password"
                                ? field.type
                                : "text"
                        }
                        step={field.type === "number" ? "any" : undefined}
                        value={String(value ?? "")}
                        onChange={(event) => setForm((current) => ({ ...current, [field.name]: event.target.value }))}
                        placeholder={field.placeholder}
                        required={field.required}
                      />
                    )}
                    {field.help && field.type !== "checkbox" && (
                      <p className="text-[11px] text-muted-foreground">{field.help}</p>
                    )}
                  </div>
                );
              })}
            </div>
            <DialogFooter>
              <Button type="button" variant="outline" onClick={() => setFormOpen(false)}>
                Batal
              </Button>
              <Button type="submit" disabled={saving}>
                {saving ? "Menyimpan…" : "Simpan"}
              </Button>
            </DialogFooter>
          </form>
        </DialogContent>
      </Dialog>

      <Dialog open={Boolean(deleteTarget)} onOpenChange={(open) => !open && setDeleteTarget(null)}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>Hapus data?</DialogTitle>
            <DialogDescription>
              Data yang dihapus tidak dapat dikembalikan.
            </DialogDescription>
          </DialogHeader>
          <DialogFooter>
            <Button variant="outline" onClick={() => setDeleteTarget(null)}>
              Batal
            </Button>
            <Button variant="destructive" onClick={remove} disabled={deleting}>
              {deleting ? "Menghapus…" : "Hapus"}
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
      <Dialog open={bulkOpen} onOpenChange={(open) => !open && setBulkOpen(false)}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>Hapus {selected.length} data terpilih?</DialogTitle>
            <DialogDescription>
              Data yang dihapus tidak dapat dikembalikan.
            </DialogDescription>
          </DialogHeader>
          <DialogFooter>
            <Button variant="outline" onClick={() => setBulkOpen(false)}>
              Batal
            </Button>
            <Button variant="destructive" onClick={hapusTerpilih} disabled={bulkDeleting}>
              {bulkDeleting ? "Menghapus…" : "Hapus"}
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </div>
  );
}
