'use client';

import { useState } from 'react';
import { useMutation } from '@tanstack/react-query';
import { Plus, Trash2 } from 'lucide-react';
import { toast } from 'sonner';

import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Switch } from '@/components/ui/switch';
import { Textarea } from '@/components/ui/textarea';
import {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectLabel,
  SelectTrigger,
  SelectValue,
} from '@/components/ui/select';
import { Sheet, SheetContent, SheetFooter, SheetHeader, SheetTitle } from '@/components/ui/sheet';
import { Badge } from '@/components/ui/badge';
import { apiFetch } from '@/lib/api/client';
import { toastError } from '@/lib/api/toast-error';
import type { FormField } from '@/lib/validators/form-templates';
import {
  bindableFieldsByEntity,
  getBindableField,
  type BindableType,
} from '@/lib/templates/bindable-fields';

const BIND_TO_NONE = '__none__';

interface FormTemplate {
  id: string;
  name: string;
  description: string | null;
  fields: FormField[];
  isActive: boolean;
}

interface Props {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  template: FormTemplate | null;
  onSaved: () => void;
}

const DEFAULT_FIELD: FormField = {
  key: '',
  label: '',
  type: 'text',
  required: false,
};

const FIELD_TYPES: Array<{ value: FormField['type']; label: string }> = [
  { value: 'text', label: 'Text' },
  { value: 'textarea', label: 'Long text' },
  { value: 'email', label: 'Email' },
  { value: 'phone', label: 'Phone' },
  { value: 'number', label: 'Number' },
  { value: 'select', label: 'Select' },
  { value: 'checkbox', label: 'Checkbox' },
];

export function FormTemplateForm(props: Props) {
  // Key-based remount seeds state on each open + template change.
  return (
    <FormTemplateFormBody
      key={props.open ? `open:${props.template?.id ?? 'new'}` : 'closed'}
      {...props}
    />
  );
}

function FormTemplateFormBody({ open, onOpenChange, template, onSaved }: Props) {
  const [name, setName] = useState(template?.name ?? '');
  const [description, setDescription] = useState(template?.description ?? '');
  const [isActive, setIsActive] = useState(template?.isActive ?? true);
  const [fields, setFields] = useState<FormField[]>(
    template && template.fields.length > 0 ? template.fields : [{ ...DEFAULT_FIELD }],
  );

  const saveMutation = useMutation({
    mutationFn: () => {
      const payload = {
        name,
        description: description || undefined,
        fields,
        isActive,
      };
      if (template) {
        return apiFetch(`/api/v1/admin/form-templates/${template.id}`, {
          method: 'PATCH',
          body: payload,
        });
      }
      return apiFetch('/api/v1/admin/form-templates', {
        method: 'POST',
        body: payload,
      });
    },
    onSuccess: () => {
      toast.success(template ? 'Template saved' : 'Template created');
      onSaved();
      onOpenChange(false);
    },
    onError: (err) => toastError(err),
  });

  function updateField(idx: number, patch: Partial<FormField>) {
    setFields((prev) => prev.map((f, i) => (i === idx ? { ...f, ...patch } : f)));
  }

  function changeBinding(idx: number, raw: string) {
    if (raw === BIND_TO_NONE) {
      // Clear the binding but leave the rest of the field untouched -
      // admins may want to keep a custom field that no longer autofills.
      setFields((prev) =>
        prev.map((f, i) => {
          if (i !== idx) return f;
          const { bindTo, ...rest } = f;
          void bindTo;
          return rest;
        }),
      );
      return;
    }
    const meta = getBindableField(raw);
    if (!meta) return;
    // Adopt the binding + auto-derive input type and label when the admin
    // hasn't typed one yet (saves repetitive data entry on the common
    // "bind to client email" flow). Pre-existing labels are preserved so
    // admins can override the friendly name per template.
    setFields((prev) =>
      prev.map((f, i) =>
        i === idx
          ? {
              ...f,
              bindTo: raw,
              type: coerceFieldType(meta.inputType, f.type),
              label: f.label.trim() ? f.label : meta.label,
              key: f.key.trim() ? f.key : meta.path.split('.').pop()!,
            }
          : f,
      ),
    );
  }

  function addField() {
    setFields((prev) => [...prev, { ...DEFAULT_FIELD }]);
  }

  function removeField(idx: number) {
    setFields((prev) => (prev.length === 1 ? prev : prev.filter((_, i) => i !== idx)));
  }

  return (
    <Sheet open={open} onOpenChange={onOpenChange}>
      <SheetContent className="overflow-y-auto sm:max-w-2xl lg:max-w-4xl">
        <SheetHeader>
          <SheetTitle>{template ? 'Edit form template' : 'New form template'}</SheetTitle>
        </SheetHeader>
        <div className="space-y-4 py-4">
          <div className="space-y-1">
            <Label>Name</Label>
            <Input value={name} onChange={(e) => setName(e.target.value)} />
          </div>
          <div className="space-y-1">
            <Label>Description</Label>
            <Textarea
              rows={2}
              value={description}
              onChange={(e) => setDescription(e.target.value)}
            />
          </div>
          <div className="flex items-center gap-2">
            <Switch checked={isActive} onCheckedChange={setIsActive} />
            <Label>Active</Label>
          </div>

          <div className="border-t pt-3 space-y-3">
            <div className="flex items-center justify-between">
              <Label className="text-sm font-medium">Fields</Label>
              <Button variant="outline" size="sm" onClick={addField}>
                <Plus className="h-3.5 w-3.5 mr-1" aria-hidden />
                Add field
              </Button>
            </div>
            {fields.map((f, i) => (
              <div key={i} className="rounded-md border p-3 space-y-2">
                <div className="flex items-center justify-between">
                  <span className="text-xs text-muted-foreground">Field {i + 1}</span>
                  {fields.length > 1 && (
                    <Button
                      variant="ghost"
                      size="icon"
                      className="text-destructive h-7 w-7"
                      onClick={() => removeField(i)}
                    >
                      <Trash2 className="h-3.5 w-3.5" aria-hidden />
                    </Button>
                  )}
                </div>

                <div className="space-y-1">
                  <Label className="text-xs">Bind to (autofill + write-back)</Label>
                  <Select
                    value={f.bindTo ?? BIND_TO_NONE}
                    onValueChange={(v) => changeBinding(i, v)}
                  >
                    <SelectTrigger>
                      <SelectValue placeholder="No binding (free-form)" />
                    </SelectTrigger>
                    <SelectContent>
                      <SelectItem value={BIND_TO_NONE}>
                        No binding - store in submission only
                      </SelectItem>
                      {bindableFieldsByEntity().map((group) => (
                        <SelectGroup key={group.entity}>
                          <SelectLabel>{group.label}</SelectLabel>
                          {group.fields.map((bf) => (
                            <SelectItem key={bf.path} value={bf.path}>
                              {bf.label}
                            </SelectItem>
                          ))}
                        </SelectGroup>
                      ))}
                    </SelectContent>
                  </Select>
                  {f.bindTo ? (
                    <Badge variant="secondary" className="text-xs font-normal">
                      Autofills from + writes back to {getBindableField(f.bindTo)?.label} ·{' '}
                      {f.bindTo}
                    </Badge>
                  ) : null}
                </div>

                <div className="grid grid-cols-2 gap-2">
                  <div className="space-y-1">
                    <Label className="text-xs">Key (no spaces)</Label>
                    <Input
                      value={f.key}
                      onChange={(e) => updateField(i, { key: e.target.value })}
                      placeholder="email"
                    />
                  </div>
                  <div className="space-y-1">
                    <Label className="text-xs">Label</Label>
                    <Input
                      value={f.label}
                      onChange={(e) => updateField(i, { label: e.target.value })}
                      placeholder="Email address"
                    />
                  </div>
                  <div className="space-y-1">
                    <Label className="text-xs">Type</Label>
                    <Select
                      value={f.type}
                      onValueChange={(v) => updateField(i, { type: v as FormField['type'] })}
                    >
                      <SelectTrigger>
                        <SelectValue />
                      </SelectTrigger>
                      <SelectContent>
                        {FIELD_TYPES.map((ft) => (
                          <SelectItem key={ft.value} value={ft.value}>
                            {ft.label}
                          </SelectItem>
                        ))}
                      </SelectContent>
                    </Select>
                  </div>
                  <div className="space-y-1 flex items-end gap-2 pb-1">
                    <Switch
                      checked={!!f.required}
                      onCheckedChange={(v) => updateField(i, { required: v })}
                    />
                    <Label className="text-xs">Required</Label>
                  </div>
                </div>
                {f.type === 'select' && (
                  <div className="space-y-1">
                    <Label className="text-xs">Options (comma-separated)</Label>
                    <Input
                      value={(f.options ?? []).join(', ')}
                      onChange={(e) =>
                        updateField(i, {
                          options: e.target.value
                            .split(',')
                            .map((s) => s.trim())
                            .filter(Boolean),
                        })
                      }
                    />
                  </div>
                )}
              </div>
            ))}
          </div>
        </div>
        <SheetFooter>
          <Button variant="ghost" onClick={() => onOpenChange(false)}>
            Cancel
          </Button>
          <Button
            onClick={() => saveMutation.mutate()}
            disabled={
              saveMutation.isPending ||
              !name.trim() ||
              fields.some((f) => !f.key.trim() || !f.label.trim())
            }
          >
            {saveMutation.isPending ? 'Saving…' : 'Save template'}
          </Button>
        </SheetFooter>
      </SheetContent>
    </Sheet>
  );
}

/**
 * Map a bindable column's natural input type onto the form-field types we
 * actually render. When binding to a `number` column we still let the
 * admin keep `select` if they'd already chosen it (e.g. they want to
 * constrain to specific values) - same for `textarea`.
 */
function coerceFieldType(
  bindableType: BindableType,
  currentType: FormField['type'],
): FormField['type'] {
  if (currentType === 'select' || currentType === 'checkbox') return currentType;
  if (currentType === 'textarea' && bindableType === 'text') return 'textarea';
  return bindableType;
}
