import { useEffect, useMemo, useState, type ReactNode } from "react";
import {
  Plus,
  Pencil,
  Trash2,
  Search,
  Download,
  Printer,
  ArrowUpDown,
  ChevronLeft,
  ChevronRight,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { Badge } from "@/components/ui/badge";
import {
  Dialog,
  DialogContent,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog";
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
} from "@/components/ui/alert-dialog";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";
import { Card } from "@/components/ui/card";
import {
  useDeleteRow,
  useRows,
  useSaveRow,
  money,
  toCSV,
  downloadCSV,
  type Row,
} from "@/lib/crud";
import { FileUploadField, AttachmentCell } from "@/components/FileUploadField";

export type Field = {
  name: string;
  label: string;
  type?:
    | "text"
    | "number"
    | "date"
    | "month"
    | "money"
    | "textarea"
    | "select"
    | "reference"
    | "file"
    | "image";
  options?: string[];
  refTable?: string;
  refLabel?: string;
  required?: boolean;
  hideInTable?: boolean;
  computed?: (row: Row) => number;
  virtual?: boolean;
};

const PAGE_SIZE = 10;

export function formatMonth(v: string) {
  const m = /^(\d{4})-(\d{2})/.exec(v);
  if (!m) return v;
  const d = new Date(Number(m[1]), Number(m[2]) - 1, 1);
  return d.toLocaleDateString(undefined, { month: "long", year: "numeric" });
}

function RefSelect({
  field,
  value,
  onChange,
}: {
  field: Field;
  value: string;
  onChange: (v: string) => void;
}) {
  const { data = [] } = useRows(field.refTable!);
  return (
    <Select value={value || ""} onValueChange={onChange}>
      <SelectTrigger>
        <SelectValue placeholder={`Select ${field.label.toLowerCase()}`} />
      </SelectTrigger>
      <SelectContent>
        {data.map((r) => (
          <SelectItem key={r["id"]} value={r["id"]}>
            {String(r[field.refLabel ?? "name"] ?? "Untitled")}
          </SelectItem>
        ))}
      </SelectContent>
    </Select>
  );
}

function RefCell({ field, id }: { field: Field; id: string | null }) {
  const { data = [] } = useRows(field.refTable!);
  if (!id) return <span className="text-muted-foreground">—</span>;
  const row = data.find((r) => r["id"] === id);
  return <>{row ? String(row[field.refLabel ?? "name"] ?? "—") : "—"}</>;
}

export function ResourcePage({
  title,
  description,
  table,
  fields,
  searchField = "name",
  filter,
  actions,
  rowActions,
}: {
  title: string;
  description?: string;
  table: string;
  fields: Field[];
  searchField?: string;
  filter?: (row: Row) => boolean;
  actions?: ReactNode;
  rowActions?: (row: Row) => ReactNode;
}) {
  const { data = [], isLoading } = useRows(table);
  const save = useSaveRow(table);
  const remove = useDeleteRow(table);
  const [open, setOpen] = useState(false);
  const [draft, setDraft] = useState<Row>({});
  const [query, setQuery] = useState("");
  const [sortBy, setSortBy] = useState<string | null>(null);
  const [sortAsc, setSortAsc] = useState(true);
  const [page, setPage] = useState(1);
  const [pendingDelete, setPendingDelete] = useState<Row | null>(null);
  const [errors, setErrors] = useState<Record<string, string>>({});

  const filtered = useMemo(() => {
    const base = filter ? data.filter(filter) : data;
    if (!query.trim()) return base;
    const q = query.toLowerCase();
    return base.filter((r) =>
      Object.values(r).some((v) => String(v ?? "").toLowerCase().includes(q)),
    );
  }, [data, query, filter]);

  const rows = useMemo(() => {
    if (!sortBy) return filtered;
    const sorted = [...filtered].sort((a, b) => {
      const av = a[sortBy] ?? "";
      const bv = b[sortBy] ?? "";
      if (typeof av === "number" && typeof bv === "number") return av - bv;
      return String(av).localeCompare(String(bv));
    });
    return sortAsc ? sorted : sorted.reverse();
  }, [filtered, sortBy, sortAsc]);

  useEffect(() => setPage(1), [query, sortBy, sortAsc]);

  const pageCount = Math.max(1, Math.ceil(rows.length / PAGE_SIZE));
  const pageRows = rows.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE);

  function startNew() {
    const base: Row = {};
    for (const f of fields) if (f.type === "select" && f.options?.[0]) base[f.name] = f.options[0];
    setErrors({});
    setDraft(base);
    setOpen(true);
  }

  function startEdit(row: Row) {
    setErrors({});
    setDraft({ ...row });
    setOpen(true);
  }

  function submit() {
    const nextErrors: Record<string, string> = {};
    for (const f of fields) {
      const v = draft[f.name];
      if (f.required && (v === undefined || v === null || String(v).trim() === "")) {
        nextErrors[f.name] = `${f.label} is required`;
      }
      if ((f.type === "number" || f.type === "money") && v !== undefined && v !== null && v !== "") {
        if (Number.isNaN(Number(v))) nextErrors[f.name] = "Enter a valid number";
        else if (Number(v) < 0) nextErrors[f.name] = "Cannot be negative";
      }
    }
    setErrors(nextErrors);
    if (Object.keys(nextErrors).length) return;

    const payload: Row = { id: draft["id"] };
    for (const f of fields) {
      if (f.virtual) continue;
      if (f.computed) {
        payload[f.name] = f.computed(draft);
        continue;
      }
      let v = draft[f.name];
      if (v === "" || v === undefined) v = null;
      if ((f.type === "number" || f.type === "money") && v !== null) v = Number(v);
      payload[f.name] = v;
    }
    if (!payload["id"]) delete payload["id"];
    save.mutate(payload, { onSuccess: () => setOpen(false) });
  }

  const tableFields = fields.filter((f) => !f.hideInTable);

  function exportCSV() {
    downloadCSV(`${table}.csv`, toCSV(rows, tableFields));
  }

  return (
    <div className="space-y-6">
      <div className="flex flex-wrap items-end justify-between gap-3 print:hidden">
        <div>
          <h1 className="font-display text-2xl font-bold tracking-tight">{title}</h1>
          {description && <p className="text-sm text-muted-foreground">{description}</p>}
        </div>
        <div className="flex flex-wrap items-center gap-2">
          {actions}
          <Button variant="outline" size="sm" onClick={exportCSV}>

            <Download className="mr-1 size-4" /> Export
          </Button>
          <Button variant="outline" size="sm" onClick={() => window.print()}>
            <Printer className="mr-1 size-4" /> Print
          </Button>
          <Dialog open={open} onOpenChange={setOpen}>
            <DialogTrigger asChild>
              <Button onClick={startNew}>
                <Plus className="mr-1 size-4" /> New
              </Button>
            </DialogTrigger>
            <DialogContent className="max-h-[85vh] overflow-y-auto sm:max-w-lg">
              <DialogHeader>
                <DialogTitle>{draft["id"] ? `Edit ${title}` : `Add ${title}`}</DialogTitle>
              </DialogHeader>
              <div className="grid gap-4 py-2">
                {fields.map((f) => (
                  <div key={f.name} className="grid gap-2">
                    <Label htmlFor={f.name}>
                      {f.label}
                      {f.required && <span className="ml-1 text-destructive">*</span>}
                    </Label>
                    {f.computed ? (
                      <Input
                        id={f.name}
                        readOnly
                        className="bg-muted font-medium"
                        value={
                          f.type === "money"
                            ? money(f.computed(draft))
                            : String(f.computed(draft))
                        }
                      />
                    ) : f.type === "textarea" ? (
                      <Textarea
                        id={f.name}
                        value={draft[f.name] ?? ""}
                        onChange={(e) => setDraft({ ...draft, [f.name]: e.target.value })}
                      />
                    ) : f.type === "select" ? (
                      <Select
                        value={draft[f.name] ?? ""}
                        onValueChange={(v) => setDraft({ ...draft, [f.name]: v })}
                      >
                        <SelectTrigger id={f.name}>
                          <SelectValue placeholder="Select" />
                        </SelectTrigger>
                        <SelectContent>
                          {(f.options ?? []).map((o) => (
                            <SelectItem key={o} value={o}>
                              {o}
                            </SelectItem>
                          ))}
                        </SelectContent>
                      </Select>
                    ) : f.type === "reference" ? (
                      <RefSelect
                        field={f}
                        value={draft[f.name] ?? ""}
                        onChange={(v) => setDraft({ ...draft, [f.name]: v })}
                      />
                    ) : f.type === "file" || f.type === "image" ? (
                      <FileUploadField
                        kind={f.type === "image" ? "image" : "file"}
                        value={draft[f.name] ?? ""}
                        onChange={(v) => setDraft({ ...draft, [f.name]: v })}
                      />
                    ) : (
                      <Input
                        id={f.name}
                        type={
                          f.type === "date"
                            ? "date"
                            : f.type === "month"
                              ? "month"
                              : f.type === "number" || f.type === "money"
                                ? "number"
                                : "text"
                        }
                        value={draft[f.name] ?? ""}
                        onChange={(e) => setDraft({ ...draft, [f.name]: e.target.value })}
                      />
                    )}
                    {errors[f.name] && (
                      <p className="text-xs text-destructive">{errors[f.name]}</p>
                    )}
                  </div>
                ))}
              </div>
              <DialogFooter>
                <Button variant="outline" onClick={() => setOpen(false)}>
                  Cancel
                </Button>
                <Button onClick={submit} disabled={save.isPending}>
                  {save.isPending ? "Saving..." : "Save"}
                </Button>
              </DialogFooter>
            </DialogContent>
          </Dialog>
        </div>
      </div>

      <Card className="overflow-hidden p-0">
        <div className="flex items-center gap-2 border-b px-4 py-3 print:hidden">
          <Search className="size-4 text-muted-foreground" />
          <input
            className="w-full bg-transparent text-sm outline-none placeholder:text-muted-foreground"
            placeholder={`Search ${title.toLowerCase()}...`}
            value={query}
            onChange={(e) => setQuery(e.target.value)}
          />
          <span className="text-xs whitespace-nowrap text-muted-foreground">
            {rows.length}&nbsp;record{rows.length === 1 ? "" : "s"}
          </span>
        </div>
        <div className="overflow-x-auto">
          <Table>
            <TableHeader>
              <TableRow>
                {tableFields.map((f) => (
                  <TableHead key={f.name}>
                    <button
                      type="button"
                      className="inline-flex items-center gap-1 hover:text-foreground"
                      onClick={() => {
                        if (sortBy === f.name) setSortAsc(!sortAsc);
                        else {
                          setSortBy(f.name);
                          setSortAsc(true);
                        }
                      }}
                    >
                      {f.label}
                      <ArrowUpDown className="size-3 opacity-50" />
                    </button>
                  </TableHead>
                ))}
                <TableHead className="text-right print:hidden">Actions</TableHead>
              </TableRow>
            </TableHeader>
            <TableBody>
              {isLoading && (
                <TableRow>
                  <TableCell
                    colSpan={tableFields.length + 1}
                    className="py-10 text-center text-muted-foreground"
                  >
                    Loading...
                  </TableCell>
                </TableRow>
              )}
              {!isLoading && rows.length === 0 && (
                <TableRow>
                  <TableCell
                    colSpan={tableFields.length + 1}
                    className="py-10 text-center text-muted-foreground"
                  >
                    {query
                      ? "No records match your search."
                      : "Nothing here yet. Use the New button to add your first record."}
                  </TableCell>
                </TableRow>
              )}
              {pageRows.map((row) => (
                <TableRow key={row["id"]}>
                  {tableFields.map((f) => (
                    <TableCell key={f.name}>
                      {f.type === "file" || f.type === "image" ? (
                        <AttachmentCell value={row[f.name] ?? null} />
                      ) : f.type === "reference" ? (
                        <RefCell field={f} id={row[f.name] ?? null} />
                      ) : f.virtual && f.computed ? (
                        f.type === "money" ? money(f.computed(row)) : String(f.computed(row))
                      ) : f.type === "money" ? (
                        money(row[f.name])
                      ) : f.type === "select" ? (
                        <Badge variant="secondary">{String(row[f.name] ?? "—")}</Badge>
                      ) : f.type === "month" ? (
                        row[f.name] ? formatMonth(String(row[f.name])) : "—"
                      ) : (
                        String(row[f.name] ?? "—")
                      )}
                    </TableCell>
                  ))}
                  <TableCell className="text-right whitespace-nowrap print:hidden">
                    {rowActions?.(row)}
                    <Button variant="ghost" size="icon" onClick={() => startEdit(row)}>
                      <Pencil className="size-4" />
                    </Button>
                    <Button variant="ghost" size="icon" onClick={() => setPendingDelete(row)}>
                      <Trash2 className="size-4 text-destructive" />
                    </Button>
                  </TableCell>
                </TableRow>
              ))}
            </TableBody>
          </Table>
        </div>
        {pageCount > 1 && (
          <div className="flex items-center justify-between border-t px-4 py-3 text-sm print:hidden">
            <span className="text-muted-foreground">
              Page {page} of {pageCount}
            </span>
            <div className="flex gap-2">
              <Button
                variant="outline"
                size="sm"
                disabled={page === 1}
                onClick={() => setPage((p) => p - 1)}
              >
                <ChevronLeft className="size-4" />
              </Button>
              <Button
                variant="outline"
                size="sm"
                disabled={page === pageCount}
                onClick={() => setPage((p) => p + 1)}
              >
                <ChevronRight className="size-4" />
              </Button>
            </div>
          </div>
        )}
      </Card>

      <AlertDialog open={!!pendingDelete} onOpenChange={(o) => !o && setPendingDelete(null)}>
        <AlertDialogContent>
          <AlertDialogHeader>
            <AlertDialogTitle>Delete this record?</AlertDialogTitle>
            <AlertDialogDescription>
              This permanently removes the record from {title.toLowerCase()}. This cannot be
              undone.
            </AlertDialogDescription>
          </AlertDialogHeader>
          <AlertDialogFooter>
            <AlertDialogCancel>Cancel</AlertDialogCancel>
            <AlertDialogAction
              onClick={() => {
                if (pendingDelete) remove.mutate(pendingDelete["id"]);
                setPendingDelete(null);
              }}
            >
              Delete
            </AlertDialogAction>
          </AlertDialogFooter>
        </AlertDialogContent>

      </AlertDialog>
      <span className="sr-only">{searchField}</span>
    </div>
  );
}
