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 {
  CreateDropdownOptionDto,
  DropdownSetting,
} from "@/types/dropdownSettings";
import { useMutation } from "@tanstack/react-query";
import { api } from "@/services/api";

export interface ManageDropdownOptionsDialogProps {
  setting: DropdownSetting;
  children?: ReactNode;
  open?: boolean;
  onOpenChange?: (open: boolean) => void;
}

/**
 * Local draft used by the editor — uses a stable client-only `key` so React
 * keys remain stable across reorders. On save we strip `key` and POST the
 * remainder as CreateDropdownOptionDto[].
 */
interface DraftOption extends CreateDropdownOptionDto {
  key: string;
}

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

function makeEmptyDraft(idx: number): DraftOption {
  return {
    key: nextKey(),
    value: "",
    label: "",
    disabled: false,
    order: idx + 1,
    meta: {},
  };
}

export default function ManageDropdownOptionsDialog({
  setting,
  children,
  open: openProp,
  onOpenChange,
}: ManageDropdownOptionsDialogProps) {
  const isControlled = openProp !== undefined;
  const [internalOpen, setInternalOpen] = useState(false);
  const open = isControlled ? openProp : internalOpen;
  const setOpen = (next: boolean) => {
    if (!isControlled) setInternalOpen(next);
    onOpenChange?.(next);
  };
  const [error, setError] = useState<string | null>(null);

  const seed = (): DraftOption[] =>
    [...(setting.children ?? [])]
      .sort((a, b) => (a.order ?? 0) - (b.order ?? 0))
      .map((o, idx) => ({
        key: o.id,
        value: o.value,
        label: o.label,
        note: o.note ?? undefined,
        disabled: o.disabled,
        order: o.order ?? idx + 1,
        meta: {
          ...(o.meta?.icon ? { icon: o.meta.icon } : {}),
          ...(o.meta?.color ? { color: o.meta.color } : {}),
          ...(o.meta?.badge ? { badge: o.meta.badge } : {}),
        },
      }));

  const [options, setOptions] = useState<DraftOption[]>(seed);

  const replaceMutation = useMutation(
    api.dropdownSettings.replaceOptions.mutationOptions(),
  );

  const update = (i: number, patch: Partial<DraftOption>) =>
    setOptions((prev) =>
      prev.map((o, idx) => (idx === i ? { ...o, ...patch } : o)),
    );

  const updateMeta = (
    i: number,
    patch: Partial<NonNullable<DraftOption["meta"]>>,
  ) =>
    setOptions((prev) =>
      prev.map((o, idx) =>
        idx === i ? { ...o, meta: { ...(o.meta ?? {}), ...patch } } : o,
      ),
    );

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

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

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

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

    const invalid = options.findIndex(
      (o) => !o.label.trim() || !o.value.trim(),
    );
    if (invalid >= 0) {
      setError(`Option ${invalid + 1} is missing a label or value.`);
      return;
    }

    const payload: CreateDropdownOptionDto[] = options.map((o, idx) => {
      const meta: NonNullable<CreateDropdownOptionDto["meta"]> = {};
      if (o.meta?.icon?.trim()) meta.icon = o.meta.icon.trim();
      if (o.meta?.color?.trim()) meta.color = o.meta.color.trim();
      if (o.meta?.badge?.trim()) meta.badge = o.meta.badge.trim();

      return {
        value: o.value.trim(),
        label: o.label.trim(),
        note: o.note?.trim() || undefined,
        disabled: o.disabled ?? false,
        order: idx + 1,
        ...(Object.keys(meta).length > 0 ? { meta } : {}),
      };
    });

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

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

      <DialogContent className="max-h-[90vh] overflow-y-auto sm:max-w-4xl rounded-3xl">
        <DialogHeader>
          <DialogTitle className="text-2xl font-bold">
            Manage Options · {setting.label}
          </DialogTitle>
          <DialogDescription>
            Add, edit, reorder, or remove options 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">
              {options.length} option{options.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 Option
            </button>
          </div>

          {options.length === 0 ? (
            <div className="rounded-2xl border border-dashed border-slate-200 p-8 text-center text-sm text-slate-500">
              No options yet. Click{" "}
              <span className="font-medium">Add Option</span> to start.
            </div>
          ) : (
            <div className="space-y-2">
              {options.map((opt, i) => (
                <div
                  key={opt.key}
                  className="grid gap-2 rounded-2xl border border-slate-200 bg-white p-3 md:grid-cols-[auto_1fr_1fr_1fr_auto_auto_auto]"
                >
                  <div className="flex items-center gap-1 text-slate-400">
                    <GripVertical className="h-4 w-4" />
                    <div className="flex flex-col">
                      <button
                        type="button"
                        onClick={() => move(i, -1)}
                        aria-label="Move up"
                        disabled={i === 0}
                        className="text-[10px] leading-none text-slate-400 transition hover:text-[#10B981] disabled:opacity-30"
                      >
                        ▲
                      </button>
                      <button
                        type="button"
                        onClick={() => move(i, 1)}
                        aria-label="Move down"
                        disabled={i === options.length - 1}
                        className="text-[10px] leading-none text-slate-400 transition hover:text-[#10B981] disabled:opacity-30"
                      >
                        ▼
                      </button>
                    </div>
                  </div>

                  <div className="space-y-1">
                    <Label className="text-xs">Label *</Label>
                    <Input
                      value={opt.label}
                      onChange={(e) => update(i, { label: e.target.value })}
                      placeholder="Display label"
                    />
                  </div>

                  <div className="space-y-1">
                    <Label className="text-xs">Value *</Label>
                    <Input
                      value={opt.value}
                      onChange={(e) => update(i, { value: e.target.value })}
                      placeholder="Stored value"
                      className="font-mono"
                    />
                  </div>

                  <div className="space-y-1">
                    <Label className="text-xs">Note</Label>
                    <Input
                      value={opt.note ?? ""}
                      onChange={(e) => update(i, { note: e.target.value })}
                      placeholder="Helper text"
                    />
                  </div>

                  <div className="space-y-1">
                    <Label className="text-xs">Badge</Label>
                    <Input
                      value={opt.meta?.badge ?? ""}
                      onChange={(e) => updateMeta(i, { badge: e.target.value })}
                      placeholder="—"
                      className="w-20"
                    />
                  </div>

                  <div className="space-y-1">
                    <Label className="text-xs">Color</Label>
                    <Input
                      value={opt.meta?.color ?? ""}
                      onChange={(e) => updateMeta(i, { color: e.target.value })}
                      placeholder="#…"
                      className="w-24 font-mono"
                    />
                  </div>

                  <div className="flex flex-col items-center justify-between gap-2">
                    <label className="flex items-center gap-1 text-xs text-slate-600">
                      <input
                        type="checkbox"
                        checked={opt.disabled ?? false}
                        onChange={(e) =>
                          update(i, { disabled: e.target.checked })
                        }
                        className="h-3.5 w-3.5 rounded border-slate-300 text-[#10B981] focus:ring-[#10B981]/20"
                      />
                      Off
                    </label>
                    <button
                      type="button"
                      onClick={() => remove(i)}
                      aria-label={`Remove ${opt.label || "option"}`}
                      className="rounded-lg p-1 text-red-500 transition hover:bg-red-50"
                    >
                      <Trash2 className="h-3.5 w-3.5" />
                    </button>
                  </div>
                </div>
              ))}
            </div>
          )}
        </div>

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

        <div className="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 Options"
            )}
          </Button>
        </div>
      </DialogContent>
    </Dialog>
  );
}
