'use client'

import { Slot } from '@radix-ui/react-slot'
import React from 'react'
import { cn } from '../../utils/cn'
import { ErrorMessage } from '../ErrorMessage/ErrorMessage'
import { Helper } from '../Helper/Helper'
import { Label } from '../Label/Label'

export const FIELD_SIZE_OPTIONS = ['default', 'sm', 'xs'] as const
export type FieldSize = (typeof FIELD_SIZE_OPTIONS)[number]

type FieldContextValue = {
  id: string
  size: FieldSize
  fieldItemId: string
  fieldDescriptionId: string
  fieldMessageId: string
}

const FieldContext = React.createContext<FieldContextValue | undefined>(
  undefined,
)

const useField = () => {
  const context = React.useContext(FieldContext)

  return context
}

export type FieldProps = React.HTMLAttributes<HTMLDivElement> & {
  /**
   * Controls the density of all child field elements (label, helper, error, and control).
   * Propagated automatically via context — no need to set it on each sub-component.
   */
  size?: FieldSize
  children: React.ReactNode
  ref?: React.Ref<HTMLDivElement>
}

/**
 * Accessibility wrapper for a single form field. Generates a shared ID via
 * `React.useId()` and wires it to the label (`htmlFor`), the control (`id`,
 * `aria-describedby`), and the error message (`aria-invalid`). Compose with
 * FieldLabel, FieldHelper, FieldControl, and FieldError.
 */
const Field = ({
  children,
  className,
  size = 'default',
  ref,
  ...props
}: FieldProps) => {
  const id = React.useId()

  const value = {
    id,
    size,
    fieldItemId: `${id}-field-item`,
    fieldDescriptionId: `${id}-field-item-description`,
    fieldMessageId: `${id}-field-item-message`,
  }

  return (
    <FieldContext.Provider value={value}>
      <div
        ref={ref}
        data-slot="field"
        className={cn('flex flex-col gap-1', className)}
        {...props}
      >
        {children}
      </div>
    </FieldContext.Provider>
  )
}
Field.displayName = 'Field'

type FieldErrorProps = React.ComponentProps<typeof ErrorMessage> & {
  ref?: React.Ref<React.ElementRef<typeof ErrorMessage>>
}

/**
 * Error message for a field. Automatically linked to the control via
 * `aria-describedby`. Only renders visible text when children are provided.
 */
const FieldError = ({ ref, ...props }: FieldErrorProps) => {
  const fieldContext = useField()
  const fieldMessageId = fieldContext?.fieldMessageId

  return (
    <ErrorMessage
      ref={ref}
      data-slot="field-error"
      id={fieldMessageId}
      {...props}
    />
  )
}
FieldError.displayName = 'FieldError'

type FieldLabelProps = React.ComponentProps<typeof Label> & {
  ref?: React.Ref<React.ElementRef<typeof Label>>
}

/**
 * Label for a field. Automatically linked to the control via `htmlFor`.
 * Use `asChild` when the label wraps a non-input element (e.g. a Checkbox)
 * to suppress the `htmlFor` link and avoid double-activation.
 */
const FieldLabel = ({
  asChild,
  size: propSize,
  ref,
  ...props
}: FieldLabelProps) => {
  const fieldContext = useField()
  const size = propSize || fieldContext?.size || 'default'
  const fieldItemId = fieldContext?.fieldItemId

  // If asChild is true, we assume it's being used as a non-semantic title (e.g., for a Checkbox).
  // We render the Label with `asChild` but do NOT pass the `htmlFor` prop.
  if (asChild) {
    return (
      <Label ref={ref} size={size} data-slot="field-label" asChild {...props} />
    )
  }

  // Otherwise, we render a standard label linked to the input.
  return (
    <Label
      ref={ref}
      size={size}
      data-slot="field-label"
      htmlFor={fieldItemId}
      {...props}
    />
  )
}
FieldLabel.displayName = 'FieldLabel'

type FieldHelperProps = React.ComponentProps<typeof Helper> & {
  ref?: React.Ref<React.ElementRef<typeof Helper>>
}

/** Helper text below the control. Automatically linked to the control via `aria-describedby`. */
const FieldHelper = ({ size: propSize, ref, ...props }: FieldHelperProps) => {
  const fieldContext = useField()
  const size = propSize || fieldContext?.size || 'default'
  const fieldDescriptionId = fieldContext?.fieldDescriptionId

  return (
    <Helper
      ref={ref}
      data-slot="field-helper"
      id={fieldDescriptionId}
      size={size}
      {...props}
    />
  )
}
FieldHelper.displayName = 'FieldHelper'

export type FieldControlProps = {
  /** When true, sets `aria-invalid` on the control and includes the error message in `aria-describedby`. */
  error?: boolean
  size?: FieldSize
  ref?: React.Ref<HTMLElement>
}

/**
 * Slot wrapper that injects the field's `id`, `aria-describedby`, and
 * `aria-invalid` into the child control. Wrap any input, select, or textarea
 * with this to wire it to the surrounding Field.
 */
const FieldControl = ({
  error,
  ref,
  ...props
}: React.ComponentProps<typeof Slot> & FieldControlProps) => {
  const fieldContext = useField()
  const fieldItemId = fieldContext?.fieldItemId
  const fieldDescriptionId = fieldContext?.fieldDescriptionId
  const fieldMessageId = fieldContext?.fieldMessageId
  const size = fieldContext?.size || 'default'

  return (
    <Slot
      ref={ref}
      data-slot="field-control"
      id={fieldItemId}
      aria-describedby={
        fieldDescriptionId +
        (error && fieldMessageId ? ` ${fieldMessageId}` : '')
      }
      aria-invalid={!!error}
      size={size}
      {...props}
    />
  )
}
FieldControl.displayName = 'FieldControl'

export { useField, Field, FieldError, FieldLabel, FieldHelper, FieldControl }
