import { useState, type ReactNode } from "react";
import { GripVertical, Loader2, Plus, Trash2 } from "lucide-react";

import {
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Button } from "@/components/ui/button";

import type {
  CreateFileUploadFieldDto,
  FileUploadSetting,
} from "@/types/fileUploadSettings";
import { getMinFiles } from "@/types/fileUploadSettings";
import { useMutation } from "@tanstack/react-query";
import { api } from "@/services/api";

export interface ManageFileUploadFieldsDialogProps {
  setting: FileUploadSetting;
  children: ReactNode;
}

/**
 * Local draft used by the editor — does NOT need to satisfy IFileUploadField
 * (which carries server-only props like createdAt). On save, we strip the
 * client-only `key` and post the rest as CreateFileUploadFieldDto[].
 */
interface DraftField extends CreateFileUploadFieldDto {
  key: string;
}

let draftCounter = 0;
const nextKey = () => `draft-${Date.now()}-${++draftCounter}`;

function makeEmptyDraft(idx: number): DraftField {
  return {
    key: nextKey(),
    fileKey: "",
    fileLabel: "",
    isRequired: false,
    isMultiple: false,
    maxFiles: 1,
    allowedExtensions: ["pdf"],
    maxSizeMb: 10,
    order: idx + 1,
  };
}

export default function ManageFileUploadFieldsDialog({
  setting,
  children,
}: ManageFileUploadFieldsDialogProps) {
  const [open, setOpen] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const seed = (): DraftField[] =>
    [...setting.fields]
      .sort((a, b) => (a.order ?? 0) - (b.order ?? 0))
      .map((f, idx) => ({
        key: f.id,
        fileKey: f.fileKey,
        fileLabel: f.fileLabel,
        helpText: f.helpText ?? undefined,
        isRequired: f.isRequired,
        isMultiple: f.isMultiple,
        maxFiles: f.maxFiles,
        allowedExtensions: f.allowedExtensions,
        maxSizeMb: f.maxSizeMb,
        order: f.order ?? idx + 1,
      }));

  const [fields, setFields] = useState<DraftField[]>(seed);

  const replaceMutation = useMutation(
    api.fileUploadSettings.replaceFields.mutationOptions(),
  );

  const update = (i: number, patch: Partial<DraftField>) =>
    setFields((prev) =>
      prev.map((f, idx) => {
        if (idx !== i) return f;
        const next = { ...f, ...patch };
        if (patch.isMultiple === false) next.maxFiles = 1;
        if (patch.isMultiple === true && next.maxFiles <= 1) next.maxFiles = 5;
        return next;
      }),
    );

  const updateExtensions = (i: number, raw: string) => {
    const list = raw
      .split(",")
      .map((s) => s.trim().toLowerCase().replace(/^\./, ""))
      .filter(Boolean);
    update(i, { allowedExtensions: list });
  };

  const remove = (i: number) =>
    setFields((prev) => prev.filter((_, idx) => idx !== i));

  const add = () =>
    setFields((prev) => [...prev, makeEmptyDraft(prev.length)]);

  const move = (i: number, dir: -1 | 1) =>
    setFields((prev) => {
      const next = [...prev];
      const target = i + dir;
      if (target < 0 || target >= next.length) return prev;
      const a = next[i] as DraftField;
      const b = next[target] as DraftField;
      next[i] = { ...b, order: i + 1 };
      next[target] = { ...a, order: target + 1 };
      return next;
    });

  const handleSave = () => {
    setError(null);

    const invalid = fields.findIndex(
      (f) =>
        !f.fileKey.trim() ||
        !f.fileLabel.trim() ||
        f.allowedExtensions.length === 0,
    );
    if (invalid >= 0) {
      setError(
        `Field ${invalid + 1} is missing file key, label, or extensions.`,
      );
      return;
    }

    const payload: CreateFileUploadFieldDto[] = fields.map((f, idx) => ({
      fileKey: f.fileKey.trim(),
      fileLabel: f.fileLabel.trim(),
      helpText: f.helpText?.trim() || undefined,
      isRequired: f.isRequired,
      isMultiple: f.isMultiple,
      maxFiles: f.isMultiple ? Math.max(1, f.maxFiles) : 1,
      allowedExtensions: f.allowedExtensions,
      maxSizeMb: f.maxSizeMb,
      order: idx + 1,
    }));

    replaceMutation.mutate(
      { id: setting.id, fields: payload },
      {
        onSuccess: () => setOpen(false),
        onError: (err) =>
          setError(
            err instanceof Error
              ? err.message
              : "Failed to save fields. Try again.",
          ),
      },
    );
  };

  return (
    <Dialog
      open={open}
      onOpenChange={(next) => {
        setOpen(next);
        if (next) {
          setFields(seed());
          setError(null);
        }
      }}
    >
      <DialogTrigger asChild>{children}</DialogTrigger>

      <DialogContent className="max-h-[90vh] overflow-y-auto sm:max-w-5xl rounded-3xl">
        <DialogHeader>
          <DialogTitle className="text-2xl font-bold">
            Manage Fields · {setting.label}
          </DialogTitle>
          <DialogDescription>
            Add, edit, reorder, or remove upload fields for{" "}
            <span className="font-mono text-slate-700">{setting.code}</span>.
          </DialogDescription>
        </DialogHeader>

        <div className="space-y-3 py-4">
          <div className="flex items-center justify-between">
            <p className="text-sm text-slate-500">
              {fields.length} field{fields.length === 1 ? "" : "s"}
            </p>
            <button
              type="button"
              onClick={add}
              className="inline-flex items-center gap-1.5 rounded-xl bg-[#10B981] px-3 py-1.5 text-xs font-medium text-white transition hover:bg-[#10B981]/90"
            >
              <Plus className="h-3.5 w-3.5" />
              Add Field
            </button>
          </div>

          {fields.length === 0 ? (
            <div className="rounded-2xl border border-dashed border-slate-200 p-8 text-center text-sm text-slate-500">
              No fields yet. Click{" "}
              <span className="font-medium">Add Field</span> to start.
            </div>
          ) : (
            <div className="space-y-3">
              {fields.map((f, i) => (
                <FieldEditor
                  key={f.key}
                  field={f}
                  index={i}
                  total={fields.length}
                  onChange={(patch) => update(i, patch)}
                  onChangeExtensions={(raw) => updateExtensions(i, raw)}
                  onMove={(dir) => move(i, dir)}
                  onRemove={() => remove(i)}
                />
              ))}
            </div>
          )}
        </div>

        {error ? (
          <p className="rounded-xl bg-red-50 px-3 py-2 text-sm text-red-700">
            {error}
          </p>
        ) : null}

        <div className="mt-2 flex justify-end gap-3 border-t border-slate-100 pt-3">
          <DialogClose asChild>
            <Button variant="outline" disabled={replaceMutation.isPending}>
              Cancel
            </Button>
          </DialogClose>
          <Button
            type="button"
            onClick={handleSave}
            disabled={replaceMutation.isPending}
            className="bg-[#10B981] text-white hover:bg-[#10B981]/90 disabled:opacity-60"
          >
            {replaceMutation.isPending ? (
              <Loader2 className="h-4 w-4 animate-spin" />
            ) : (
              "Save Fields"
            )}
          </Button>
        </div>
      </DialogContent>
    </Dialog>
  );
}

function FieldEditor({
  field,
  index,
  total,
  onChange,
  onChangeExtensions,
  onMove,
  onRemove,
}: {
  field: DraftField;
  index: number;
  total: number;
  onChange: (patch: Partial<DraftField>) => void;
  onChangeExtensions: (raw: string) => void;
  onMove: (dir: -1 | 1) => void;
  onRemove: () => void;
}) {
  const minFiles = getMinFiles(field);
  const effectiveMax = field.isMultiple ? field.maxFiles : 1;

  return (
    <div className="rounded-2xl border border-slate-200 bg-white p-4">
      <div className="mb-3 flex items-center justify-between">
        <div className="flex items-center gap-2 text-slate-400">
          <GripVertical className="h-4 w-4" />
          <div className="flex flex-col">
            <button
              type="button"
              onClick={() => onMove(-1)}
              aria-label="Move up"
              disabled={index === 0}
              className="text-[10px] leading-none text-slate-400 transition hover:text-[#10B981] disabled:opacity-30"
            >
              ▲
            </button>
            <button
              type="button"
              onClick={() => onMove(1)}
              aria-label="Move down"
              disabled={index === total - 1}
              className="text-[10px] leading-none text-slate-400 transition hover:text-[#10B981] disabled:opacity-30"
            >
              ▼
            </button>
          </div>
          <span className="text-xs font-semibold uppercase tracking-wide text-[#10B981]">
            Field {index + 1}
          </span>
          <span className="rounded-full bg-slate-100 px-2 py-0.5 text-[10px] font-medium text-slate-600">
            min {minFiles} · max {effectiveMax}
          </span>
        </div>
        <button
          type="button"
          onClick={onRemove}
          aria-label="Remove field"
          className="rounded-lg p-1 text-red-500 transition hover:bg-red-50"
        >
          <Trash2 className="h-4 w-4" />
        </button>
      </div>

      <div className="grid gap-3 md:grid-cols-4">
        <div className="space-y-1.5">
          <Label className="text-xs">File Key *</Label>
          <Input
            value={field.fileKey}
            onChange={(e) => onChange({ fileKey: e.target.value })}
            placeholder="supporting_doc"
            className="font-mono"
          />
        </div>

        <div className="space-y-1.5 md:col-span-2">
          <Label className="text-xs">File Label *</Label>
          <Input
            value={field.fileLabel}
            onChange={(e) => onChange({ fileLabel: e.target.value })}
            placeholder="Supporting Document"
          />
        </div>

        <div className="space-y-1.5">
          <Label className="text-xs">Max Size (MB)</Label>
          <Input
            type="number"
            min={1}
            value={field.maxSizeMb}
            onChange={(e) =>
              onChange({ maxSizeMb: Number(e.target.value) })
            }
          />
        </div>

        <div className="space-y-1.5 md:col-span-2">
          <Label className="text-xs">Allowed Extensions</Label>
          <Input
            value={field.allowedExtensions.join(", ")}
            onChange={(e) => onChangeExtensions(e.target.value)}
            placeholder="pdf, docx, jpg"
            className="font-mono"
          />
          <p className="text-xs text-slate-500">
            Comma-separated, no leading dot.
          </p>
        </div>

        <div className="space-y-1.5">
          <Label className="text-xs">Max Files</Label>
          <Input
            type="number"
            min={1}
            max={50}
            value={field.maxFiles}
            disabled={!field.isMultiple}
            onChange={(e) =>
              onChange({ maxFiles: Number(e.target.value) })
            }
            className={!field.isMultiple ? "bg-slate-50 text-slate-400" : ""}
          />
          {!field.isMultiple ? (
            <p className="text-xs text-slate-400">
              Locked to 1 when single-file.
            </p>
          ) : null}
        </div>

        <div className="flex flex-col gap-2 md:flex-row md:items-end md:gap-4">
          <label className="flex items-center gap-2 text-sm text-slate-700">
            <input
              type="checkbox"
              checked={field.isRequired}
              onChange={(e) =>
                onChange({ isRequired: e.target.checked })
              }
              className="h-4 w-4 rounded border-slate-300 text-[#10B981] focus:ring-[#10B981]/20"
            />
            Required
          </label>
          <label className="flex items-center gap-2 text-sm text-slate-700">
            <input
              type="checkbox"
              checked={field.isMultiple}
              onChange={(e) =>
                onChange({ isMultiple: e.target.checked })
              }
              className="h-4 w-4 rounded border-slate-300 text-[#10B981] focus:ring-[#10B981]/20"
            />
            Multiple
          </label>
        </div>

        <div className="space-y-1.5 md:col-span-4">
          <Label className="text-xs">Help Text (optional)</Label>
          <Input
            value={field.helpText ?? ""}
            onChange={(e) => onChange({ helpText: e.target.value })}
            placeholder="e.g. PDF or photo of the original document."
          />
        </div>
      </div>
    </div>
  );
}
