'use client'

import * as React from 'react'
import {
  Accordion,
  AccordionContent,
  AccordionItem,
  AccordionTrigger
} from '@admin/components/ui/accordion'
import { Button } from '@admin/components/ui/button'
import { Card, CardContent, CardHeader } from '@admin/components/ui/card'
import { FormDescription, FormField, FormItem, FormLabel } from '@admin/components/ui/form'
import { cloneDefaultValue } from '@admin/utils/form/clone-default-value'
import { createListItemKey } from '@admin/utils/form/create-list-item-key'
import { cn } from '@admin/utils/shared/cn'
import { Plus, X } from 'lucide-react'
import { useFormContext, useWatch } from 'react-hook-form'

interface NestedObjectListFieldProps {
  name: string
  label: string
  singularLabel: string
  defaultValue: Record<string, unknown>
  children: (index: number) => React.ReactNode
  className?: string
  description?: string
  disabled?: boolean
  hideLabel?: boolean
  maxItems?: number
  renderTitle?: (index: number) => React.ReactNode
  validatePaths?: string[]
}

export function NestedObjectListField({
  name,
  label,
  singularLabel,
  defaultValue,
  children,
  className,
  description,
  disabled,
  hideLabel,
  maxItems,
  renderTitle,
  validatePaths
}: NestedObjectListFieldProps) {
  const { control, setValue, trigger } = useFormContext()
  const watchedItems = useWatch({
    control,
    name
  })
  const items = Array.isArray(watchedItems) ? watchedItems : []
  const [expanded, setExpanded] = React.useState('')
  const [itemKeys, setItemKeys] = React.useState<string[]>([])
  const canAddMore = !maxItems || items.length < maxItems

  React.useEffect(() => {
    setItemKeys((currentKeys) => {
      if (currentKeys.length === items.length) return currentKeys
      if (currentKeys.length > items.length) {
        return currentKeys.slice(0, items.length)
      }

      const missingKeyCount = items.length - currentKeys.length
      const nextKeys = Array.from({ length: missingKeyCount }, createListItemKey)
      return [...currentKeys, ...nextKeys]
    })
  }, [items.length])

  async function handleAdd() {
    if (!canAddMore) return
    if (expanded) {
      const isExpandedItemValid = await validateExpandedItem()
      if (!isExpandedItemValid) return
    }

    const nextValue = cloneDefaultValue(defaultValue) as Record<string, unknown>
    const nextItems = [...items, nextValue]
    setValue(name, nextItems, {
      shouldDirty: true,
      shouldValidate: false
    })
    setItemKeys((currentKeys) => [...currentKeys, createListItemKey()])
    setExpanded(`item-${nextItems.length}`)
  }

  const handleRemove = (index: number) => {
    if (disabled) return

    const nextItems = items.filter((_, itemIndex) => itemIndex !== index)
    setValue(name, nextItems, {
      shouldDirty: true,
      shouldValidate: false
    })
    const keepOtherKeys = (_: string, itemIndex: number) => itemIndex !== index
    setItemKeys((currentKeys) => currentKeys.filter(keepOtherKeys))
    setExpanded('')
  }

  async function validateExpandedItem() {
    if (!expanded) return true

    const expandedIndex = Number(expanded.replace('item-', '')) - 1
    if (expandedIndex < 0) return true

    const itemPath = `${name}.${expandedIndex}`
    const paths =
      validatePaths && validatePaths.length > 0
        ? validatePaths.map((path) => `${itemPath}.${path}`)
        : [itemPath]

    return trigger(paths)
  }

  async function handleExpandedChange(nextExpanded: string) {
    if (expanded && nextExpanded && nextExpanded !== expanded) {
      const isExpandedItemValid = await validateExpandedItem()
      if (!isExpandedItemValid) return
    }

    setExpanded(nextExpanded)
  }

  const getTitle = (index: number) => {
    if (renderTitle) return renderTitle(index)
    return `${singularLabel} ${index + 1}`
  }

  return (
    <FormItem className={cn('space-y-4', className)}>
      {items.length === 0 ? (
        <Card>
          <CardContent className="flex flex-col items-center justify-center">
            <p className="text-muted-foreground">No {label} added yet.</p>
            <Button
              type="button"
              variant="outline"
              onClick={handleAdd}
              disabled={disabled || !canAddMore}
            >
              <Plus className="size-3" />
              Add {singularLabel}
            </Button>
          </CardContent>
        </Card>
      ) : (
        <Card className="space-y-4 border-t border-border pt-4">
          <CardHeader className="flex items-center justify-between pl-5">
            <FormField
              control={control}
              name={name}
              render={() => (
                <FormItem className="flex flex-col gap-1">
                  <FormLabel className={cn({ 'sr-only': hideLabel })}>{label}</FormLabel>
                  {description ? <FormDescription>{description}</FormDescription> : null}
                </FormItem>
              )}
            />
            <Button
              type="button"
              variant="outline"
              onClick={handleAdd}
              disabled={disabled || !canAddMore}
            >
              <Plus className="size-3" />
              Add {singularLabel}
            </Button>
          </CardHeader>

          <CardContent className="p-0!">
            <Accordion
              value={expanded ? [expanded] : []}
              onValueChange={(nextValue) => handleExpandedChange(nextValue[0] ?? '')}
              className="border-none"
            >
              {items.map((_, index) => (
                <AccordionItem
                  key={itemKeys[index] ?? `${name}-${index}`}
                  value={`item-${index + 1}`}
                  className="bg-transparent!"
                >
                  <div className="relative py-1">
                    <AccordionTrigger className="flex min-h-12 w-full items-center gap-2 py-2">
                      <span className="w-full pr-16">{getTitle(index)}</span>
                    </AccordionTrigger>
                    <Button
                      type="button"
                      variant="ghost"
                      size="icon-sm"
                      className="absolute top-1/2 right-10 shrink-0 -translate-y-1/2"
                      aria-label={`Remove ${singularLabel}`}
                      onClick={() => handleRemove(index)}
                      disabled={disabled}
                    >
                      <X />
                    </Button>
                  </div>
                  <AccordionContent className="mb-4 space-y-5 rounded-sm border border-border bg-background p-5">
                    {children(index)}
                  </AccordionContent>
                </AccordionItem>
              ))}
            </Accordion>
          </CardContent>
        </Card>
      )}
    </FormItem>
  )
}
