'use client'

import { useState } from 'react'
import { Plus, Trash2 } from 'lucide-react'
import { Button } from '../ui/button'
import { Input } from '../ui/input'
import { Label } from '../ui/label'
import { Checkbox } from '../ui/checkbox'
import { Card, CardContent, CardHeader, CardTitle } from '../ui/card'
import {
  Select,
  SelectTrigger,
  SelectValue,
  SelectContent,
  SelectItem,
} from '../ui/select'
import { notify as defaultNotify } from '../toast'
import { cn } from '../../lib/utils'
import {
  type DataType,
  type EditableDraft,
  type ExistingAttribute,
  type AttributePayload,
  type SchemaId,
  DATA_TYPES,
  blankAttribute,
  toKey,
  draftToAttributePayloads,
  attributeToDraft,
  diffAttributes,
} from './schema-builder-core'

/** The type the backend echoes back after creation (only `id` is required). */
export interface CreatedType {
  id: SchemaId
  key: string
  label: string
}

/** A persisted type loaded into the builder for editing. */
export interface ExistingType {
  id: SchemaId
  key: string
  label: string
  attributes: ExistingAttribute[]
}

/** A minimal notifier surface; defaults to @startsimpli/ui's toast notify. */
export interface SchemaTypeBuilderNotifier {
  success?: (message: string) => void
  error?: (message: string) => void
}

/** Theme slots so a consuming app can restyle the builder without forking it. */
export interface SchemaTypeBuilderClassNames {
  root?: string
  card?: string
  fieldRow?: string
  submit?: string
}

export interface SchemaTypeBuilderProps {
  /** Persist the entity type. Must resolve to at least its server-assigned id. */
  createType: (input: { key: string; label: string }) => Promise<{ id: SchemaId }>
  /** Persist one attribute against a type. */
  createAttribute: (input: AttributePayload) => Promise<unknown>
  /** Fired after the type + its attributes are persisted (create OR save). Navigate / refetch here. */
  onCreated?: (created: CreatedType) => void

  // ---- edit mode (optional): pass an existing type to load + edit it ----
  /** When present, the builder opens in edit mode pre-filled from this type. */
  existingType?: ExistingType
  /** Rename a type's label (key is immutable). Required for label edits. */
  updateType?: (id: SchemaId, input: { label: string }) => Promise<unknown>
  /** Patch an existing attribute. Required for field edits in edit mode. */
  updateAttribute?: (
    id: SchemaId,
    input: Omit<AttributePayload, 'entityType'>,
  ) => Promise<unknown>
  /** Delete an existing attribute. Required to remove fields in edit mode. */
  deleteAttribute?: (id: SchemaId) => Promise<unknown>
  /** Delete the whole type. When provided in edit mode, a delete control is shown. */
  deleteType?: (id: SchemaId) => Promise<unknown>
  /** Fired after the type is deleted (navigate away / refetch here). */
  onDeleted?: (type: ExistingType) => void

  /** Inject a notifier; defaults to the shared @startsimpli/ui notify. */
  notify?: SchemaTypeBuilderNotifier
  /** Override the create-mode submit label (default "Create type"). */
  submitLabel?: string
  classNames?: SchemaTypeBuilderClassNames
}

function initialDrafts(existingType?: ExistingType): EditableDraft[] {
  if (existingType && existingType.attributes.length) {
    return existingType.attributes.map(attributeToDraft)
  }
  return [blankAttribute()]
}

/**
 * No-code entity-type builder, promoted from frontend-starter (rule #9). The
 * persistence layer is injected via callback props so the component carries no
 * API client, router, or query-cache coupling. Pass `existingType` (+ the
 * update/delete callbacks) to open it in edit mode; otherwise it creates.
 */
export function SchemaTypeBuilder({
  createType,
  createAttribute,
  onCreated,
  existingType,
  updateType,
  updateAttribute,
  deleteAttribute,
  deleteType,
  onDeleted,
  notify = defaultNotify,
  submitLabel = 'Create type',
  classNames,
}: SchemaTypeBuilderProps) {
  const editing = !!existingType
  const [label, setLabel] = useState(existingType?.label ?? '')
  const [keyOverride, setKeyOverride] = useState('')
  const [attributes, setAttributes] = useState<EditableDraft[]>(() => initialDrafts(existingType))
  const [saving, setSaving] = useState(false)
  const [confirmingDelete, setConfirmingDelete] = useState(false)

  const effectiveKey = editing ? existingType!.key : keyOverride.trim() || toKey(label)

  function updateAttr(index: number, patch: Partial<EditableDraft>) {
    setAttributes((prev) => prev.map((a, i) => (i === index ? { ...a, ...patch } : a)))
  }

  function addAttr() {
    setAttributes((prev) => [...prev, blankAttribute()])
  }

  function removeAttr(index: number) {
    setAttributes((prev) => prev.filter((_, i) => i !== index))
  }

  async function persistEdit() {
    const type = existingType!
    if (updateType && label.trim() !== type.label) {
      await updateType(type.id, { label: label.trim() })
    }
    const { creates, updates, deletes } = diffAttributes(type.id, type.attributes, attributes)
    for (const payload of creates) await createAttribute(payload)
    for (const u of updates) {
      const { id, ...rest } = u
      if (updateAttribute) await updateAttribute(id, rest)
    }
    for (const id of deletes) {
      if (deleteAttribute) await deleteAttribute(id)
    }
  }

  async function persistCreate() {
    const type = await createType({ key: effectiveKey, label: label.trim() })
    // Create each declared attribute, in order; blank rows + config are handled
    // by the pure helper. (New attributes carry no id in edit mode either.)
    for (const payload of draftToAttributePayloads(type.id, attributes)) {
      await createAttribute(payload)
    }
    return type.id
  }

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault()
    if (!label.trim() || !effectiveKey) {
      notify.error?.('Give your type a name.')
      return
    }
    setSaving(true)
    try {
      if (editing) {
        await persistEdit()
        notify.success?.(`Saved "${label.trim()}".`)
        onCreated?.({ id: existingType!.id, key: effectiveKey, label: label.trim() })
      } else {
        const id = await persistCreate()
        notify.success?.(`Created "${label.trim()}".`)
        onCreated?.({ id, key: effectiveKey, label: label.trim() })
      }
    } catch (err) {
      notify.error?.(err instanceof Error ? err.message : 'Could not save the type.')
    } finally {
      setSaving(false)
    }
  }

  async function handleDelete() {
    if (!existingType || !deleteType) return
    setSaving(true)
    try {
      await deleteType(existingType.id)
      notify.success?.(`Deleted "${existingType.label}".`)
      onDeleted?.(existingType)
    } catch (err) {
      notify.error?.(err instanceof Error ? err.message : 'Could not delete the type.')
    } finally {
      setSaving(false)
      setConfirmingDelete(false)
    }
  }

  return (
    <form onSubmit={handleSubmit} className={cn('space-y-6', classNames?.root)}>
      <Card className={classNames?.card}>
        <CardHeader>
          <CardTitle>Type details</CardTitle>
        </CardHeader>
        <CardContent className="space-y-4">
          <div className="space-y-1.5">
            <Label htmlFor="type-label">Name</Label>
            <Input
              id="type-label"
              value={label}
              onChange={(e) => setLabel(e.target.value)}
              placeholder="e.g. Customer, Invoice, Property"
            />
          </div>
          <div className="space-y-1.5">
            <Label htmlFor="type-key">Key{editing ? '' : ' (optional)'}</Label>
            <Input
              id="type-key"
              value={editing ? existingType!.key : keyOverride}
              onChange={(e) => setKeyOverride(e.target.value)}
              disabled={editing}
              placeholder={toKey(label) || 'auto-generated from the name'}
            />
            <p className="text-xs text-gray-500">
              {editing ? (
                <>The key is fixed once a type exists.</>
              ) : (
                <>
                  Stable identifier used in URLs and the API. Defaults to{' '}
                  <span className="font-mono">{effectiveKey || '—'}</span>.
                </>
              )}
            </p>
          </div>
        </CardContent>
      </Card>

      <Card className={classNames?.card}>
        <CardHeader>
          <CardTitle>Fields</CardTitle>
        </CardHeader>
        <CardContent className="space-y-4">
          {attributes.map((attr, i) => (
            <div
              key={attr.id ?? `new-${i}`}
              className={cn(
                'grid items-end gap-3 sm:grid-cols-[1fr_180px_auto_auto]',
                classNames?.fieldRow,
              )}
            >
              <div className="space-y-1.5">
                <Label htmlFor={`attr-name-${i}`}>Field name</Label>
                <Input
                  id={`attr-name-${i}`}
                  value={attr.name}
                  onChange={(e) => updateAttr(i, { name: e.target.value })}
                  placeholder="e.g. email, due_date, amount"
                />
              </div>

              <div className="space-y-1.5">
                <Label>Type</Label>
                <Select
                  value={attr.dataType}
                  onValueChange={(v) => updateAttr(i, { dataType: v as DataType })}
                >
                  <SelectTrigger>
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent>
                    {DATA_TYPES.map((dt) => (
                      <SelectItem key={dt.value} value={dt.value}>
                        {dt.label}
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
              </div>

              <div className="flex items-center gap-2 pb-2.5">
                <Checkbox
                  id={`attr-required-${i}`}
                  checked={attr.required}
                  onCheckedChange={(c) => updateAttr(i, { required: c === true })}
                />
                <Label htmlFor={`attr-required-${i}`}>Required</Label>
              </div>

              <Button
                type="button"
                variant="ghost"
                size="icon"
                onClick={() => removeAttr(i)}
                aria-label="Remove field"
                disabled={attributes.length === 1 && !editing}
              >
                <Trash2 className="h-4 w-4" />
              </Button>

              {attr.dataType === 'enum' && (
                <div className="space-y-1.5 sm:col-span-4">
                  <Label htmlFor={`attr-choices-${i}`}>Choices (comma-separated)</Label>
                  <Input
                    id={`attr-choices-${i}`}
                    value={attr.choices}
                    onChange={(e) => updateAttr(i, { choices: e.target.value })}
                    placeholder="e.g. Open, In progress, Done"
                  />
                </div>
              )}

              {(attr.dataType === 'number' || attr.dataType === 'integer') && (
                <div className="grid gap-3 sm:col-span-4 sm:grid-cols-2">
                  <div className="space-y-1.5">
                    <Label htmlFor={`attr-min-${i}`}>Minimum (optional)</Label>
                    <Input
                      id={`attr-min-${i}`}
                      type="number"
                      value={attr.min}
                      onChange={(e) => updateAttr(i, { min: e.target.value })}
                      placeholder="e.g. 0"
                    />
                  </div>
                  <div className="space-y-1.5">
                    <Label htmlFor={`attr-max-${i}`}>Maximum (optional)</Label>
                    <Input
                      id={`attr-max-${i}`}
                      type="number"
                      value={attr.max}
                      onChange={(e) => updateAttr(i, { max: e.target.value })}
                      placeholder="e.g. 100"
                    />
                  </div>
                </div>
              )}

              {(attr.dataType === 'text' || attr.dataType === 'longtext') && (
                <div className="space-y-1.5 sm:col-span-4">
                  <Label htmlFor={`attr-regex-${i}`}>Pattern (regex, optional)</Label>
                  <Input
                    id={`attr-regex-${i}`}
                    value={attr.regex}
                    onChange={(e) => updateAttr(i, { regex: e.target.value })}
                    placeholder="e.g. [^@]+@[^@]+\.[^@]+"
                  />
                  <p className="text-xs text-gray-500">
                    Values must fully match this pattern.
                  </p>
                </div>
              )}
            </div>
          ))}

          <Button type="button" variant="outline" onClick={addAttr}>
            <Plus className="mr-2 h-4 w-4" />
            Add field
          </Button>
        </CardContent>
      </Card>

      <div className="flex items-center justify-between gap-3">
        <div>
          {editing && deleteType && (
            confirmingDelete ? (
              <div className="flex items-center gap-2">
                <span className="text-sm text-gray-600">Delete this type and all its fields?</span>
                <Button
                  type="button"
                  variant="destructive"
                  size="sm"
                  onClick={handleDelete}
                  disabled={saving}
                >
                  Delete
                </Button>
                <Button
                  type="button"
                  variant="ghost"
                  size="sm"
                  onClick={() => setConfirmingDelete(false)}
                  disabled={saving}
                >
                  Cancel
                </Button>
              </div>
            ) : (
              <Button
                type="button"
                variant="outline"
                onClick={() => setConfirmingDelete(true)}
                disabled={saving}
              >
                <Trash2 className="mr-2 h-4 w-4" />
                Delete type
              </Button>
            )
          )}
        </div>

        <Button type="submit" disabled={saving} className={classNames?.submit}>
          {editing
            ? saving
              ? 'Saving…'
              : 'Save changes'
            : saving
              ? 'Creating…'
              : submitLabel}
        </Button>
      </div>
    </form>
  )
}
