'use client'

import * as React from 'react'
import { SvgVisible, SvgInvisible1 } from '@chainlink/blocks-icons'
import { cn } from '../../utils/cn'
import { FieldError } from '../Field'
import { type FieldSize, useField } from '../Field/Field'
import { ClearIcon, InvalidIcon, LoadingSpinner, ValidIcon } from './icons'
import { getInputWidhtStyles, Input, type InputProps } from './Input'
import { useInputClear } from './useInputClear'

// --- Context ---
interface FieldInputContextValue {
  inputRef: React.RefObject<HTMLInputElement | null>
  size: FieldSize
  fieldItemId?: string
  fieldDescriptionId?: string
  handleChange: (e: React.ChangeEvent<HTMLInputElement>) => void
  handleClear: () => void
  handleTogglePassword?: () => void
  hasValue: boolean
  /** When false, the hover clear control is not shown (default true). */
  clearable: boolean
  canShowPasswordToggle: boolean
  showPassword: boolean
  type?: React.HTMLInputTypeAttribute
  error?: boolean
  disabled?: boolean
  value?: string | number | readonly string[]
}

const FieldInputContext = React.createContext<
  FieldInputContextValue | undefined
>(undefined)

export const useFieldInputContext = () => {
  return React.useContext(FieldInputContext)
}

// --- Left Icon Component ---
interface FieldInputIconProps {
  children: React.ReactNode
  className?: string
  size?: FieldSize
}

/**
 * Left-side icon slot inside FieldInputRoot. Positions the icon absolutely
 * and adjusts padding accordingly. Must be used inside FieldInputRoot.
 */
export const FieldInputIcon = ({ children, size }: FieldInputIconProps) => {
  const context = useFieldInputContext()

  if (!context) {
    throw new Error('FieldInputIcon must be used within FieldInputRoot.')
  }

  const finalSize = size ?? context.size

  if (!children) return null

  return (
    <div
      data-slot="field-input-left-icon"
      className={cn(
        'absolute top-4 flex items-center pl-4',
        'group-disabled:opacity-50',
        finalSize === 'sm' && 'top-3',
      )}
    >
      {children}
    </div>
  )
}
FieldInputIcon.displayName = 'FieldInputIcon'

interface FieldInputActionProps {
  children?: React.ReactNode
  className?: string
  /**
   * Called when the clear button is pressed. Receives the internal `clearInput`
   * function so you can run side effects before or after clearing.
   */
  onClear?: (clearInput: () => void) => void
  /** Shows a loading spinner. Takes priority over valid/invalid states. */
  isLoading?: boolean
  /** Shows a success icon. Shown when isLoading is false. */
  isValid?: boolean
  /** Shows an error icon with a clear action. Shown when isLoading and isValid are false. */
  isInvalid?: boolean
}

/**
 * Right-side action slot inside FieldInputRoot. Renders state indicators with
 * this priority: password toggle → loading → valid → invalid → custom children → clear-on-hover.
 * Hidden when the input is disabled.
 */
export const FieldInputAction = ({
  children,
  className,
  onClear: onClearProp,
  isLoading,
  isValid,
  isInvalid,
}: FieldInputActionProps) => {
  const context = useFieldInputContext()

  // Early return if no context and no custom children
  if (!context && !children) {
    return null
  }

  const disabled = context?.disabled
  const onTogglePassword = context?.handleTogglePassword
  const canShowPasswordToggle = context?.canShowPasswordToggle ?? false
  const showPassword = context?.showPassword ?? false
  const hasValue = context?.hasValue ?? false

  // Don't render if disabled
  if (disabled) {
    return null
  }

  // Handler that supports composition pattern
  const handleClear = () => {
    if (onClearProp && context?.handleClear) {
      onClearProp(context.handleClear)
    } else {
      context?.handleClear()
    }
  }

  let content: React.ReactNode = null

  // Priority order (highest to lowest):
  // 1. Password toggle (always shows for password inputs)
  // 2. Loading state
  // 3. Valid state
  // 4. Invalid state
  // 5. Custom children (if provided)
  // 6. Clear button on hover (if has value)

  if (canShowPasswordToggle && onTogglePassword) {
    // Password toggle always takes precedence
    content = (
      <button
        type="button"
        onClick={onTogglePassword}
        aria-label={showPassword ? 'Hide password' : 'Show password'}
      >
        {showPassword ? (
          <SvgInvisible1 color="muted-more" />
        ) : (
          <SvgVisible color="muted-more" />
        )}
      </button>
    )
  } else if (isLoading) {
    content = <LoadingSpinner />
  } else if (isValid) {
    content = <ValidIcon />
  } else if (isInvalid) {
    content = (
      <button
        type="button"
        onMouseDown={(e) => {
          e.preventDefault()
          handleClear()
        }}
        onClick={(e) => {
          e.preventDefault()
        }}
        aria-label="Clear invalid input"
      >
        <InvalidIcon />
      </button>
    )
  } else if (children) {
    // Custom children only show if no state indicators
    content = children
  } else if (hasValue && (context?.clearable ?? true)) {
    content = (
      <button
        className="hidden group-hover:block"
        type="button"
        onMouseDown={(e) => {
          e.preventDefault()
          handleClear()
        }}
        onClick={(e) => {
          e.preventDefault()
        }}
        aria-label="Clear input"
      >
        <ClearIcon />
      </button>
    )
  }

  if (!content) {
    return null
  }

  return (
    <div
      className={cn(
        'absolute bottom-0 right-0 top-0 flex items-center pr-4',
        className,
      )}
    >
      {content}
    </div>
  )
}
FieldInputAction.displayName = 'FieldInputAction'

// --- Input Control Component (Context-Aware Input) ---
interface FieldInputControlProps
  extends Omit<
    InputProps,
    'error' | 'type' | 'size' | 'width' | 'onChange' | 'value'
  > {
  error?: string | boolean
}

/**
 * Context-aware Input that reads size, id, aria props, and value from
 * FieldInputRoot. Use this inside FieldInputRoot instead of a bare Input.
 * Throws if used outside FieldInputRoot.
 */
export const FieldInputControl = ({
  className,
  ...inputProps
}: FieldInputControlProps) => {
  const context = useFieldInputContext()

  if (!context) {
    throw new Error(
      'FieldInputControl must be used within FieldInputRoot. For standalone inputs, use the Input component directly.',
    )
  }

  return (
    <Input
      {...inputProps}
      id={context.fieldItemId}
      aria-describedby={context.fieldDescriptionId}
      aria-invalid={context.error}
      size={context.size}
      width="full"
      ref={context.inputRef}
      onChange={context.handleChange}
      type={context.type}
      error={context.error}
      value={context.value}
      className={className}
    />
  )
}
FieldInputControl.displayName = 'FieldInputControl'

// --- Main FieldInput Component ---
export interface FieldInputProps extends Omit<InputProps, 'error'> {
  children?: React.ReactNode
  /** Shows a loading spinner in the action slot. */
  isLoading?: boolean
  /** Shows a success icon in the action slot. */
  isValid?: boolean
  /** Shows an error icon with a clear action in the action slot. */
  isInvalid?: boolean
  /**
   * Error state for the input.
   * - `boolean`: applies error styles only.
   * - `string`: applies error styles and renders an inline FieldError message below the input.
   */
  error?: string | boolean
  /** When false, the hover clear control is not shown (default true). */
  clearable?: boolean
}

export interface FieldInputRootProps extends FieldInputProps {
  ref?: React.Ref<HTMLInputElement>
}

/**
 * Low-level composition root for FieldInput. Sets up the context that wires
 * together FieldInputControl, FieldInputIcon, and FieldInputAction. Use this
 * when you need to compose a custom input layout. For standard usage, use the
 * FieldInput convenience component instead.
 */
export const FieldInputRoot = ({
  children,
  className,
  type,
  size,
  width,
  error,
  disabled,
  value,
  defaultValue,
  onChange,
  clearable = true,
  ref,
}: FieldInputRootProps) => {
  const {
    hasValue,
    canShowPasswordToggle,
    showPassword,
    handleChange,
    handleClear,
    handleTogglePassword,
    inputRef,
  } = useInputClear({
    value,
    defaultValue,
    onChange,
    type,
  })

  const fieldContext = useField()
  const fieldSize = size ?? fieldContext?.size ?? 'default'
  const fieldItemId = fieldContext?.fieldItemId
  const fieldDescriptionId = fieldContext?.fieldDescriptionId

  // Merge refs
  React.useImperativeHandle(ref, () => inputRef.current!, [inputRef])

  const contextValue: FieldInputContextValue = React.useMemo(
    () => ({
      inputRef,
      size: fieldSize,
      fieldItemId,
      fieldDescriptionId,
      handleChange,
      handleClear,
      handleTogglePassword,
      hasValue,
      clearable,
      canShowPasswordToggle,
      showPassword,
      type: type === 'password' && showPassword ? 'text' : type,
      error: !!error,
      disabled,
      value,
    }),
    [
      inputRef,
      fieldSize,
      fieldItemId,
      fieldDescriptionId,
      handleChange,
      handleClear,
      handleTogglePassword,
      hasValue,
      clearable,
      canShowPasswordToggle,
      showPassword,
      type,
      error,
      disabled,
      value,
    ],
  )

  return (
    <FieldInputContext.Provider value={contextValue}>
      <div
        className={cn(
          'group relative inline-block gap-2 [&_input]:pr-11',
          '[&:has([data-slot="field-input-left-icon"])_input]:pl-11',
          // Hide number input spinners when type is number
          type === 'number' &&
            '[&_input::-webkit-inner-spin-button]:m-0 [&_input::-webkit-inner-spin-button]:appearance-none [&_input::-webkit-outer-spin-button]:m-0 [&_input::-webkit-outer-spin-button]:appearance-none [&_input]:[-moz-appearance:textfield]',
          // Hide browser's native clear button
          '[&_input::-webkit-search-cancel-button]:hidden [&_input::-webkit-search-cancel-button]:appearance-none',
          getInputWidhtStyles(width),
          className,
        )}
      >
        {children}
      </div>
      {error && typeof error === 'string' && (
        <FieldError className="mt-2">{error}</FieldError>
      )}
    </FieldInputContext.Provider>
  )
}
FieldInputRoot.displayName = 'FieldInputRoot'

export interface FieldInputComponentProps extends FieldInputProps {
  ref?: React.Ref<HTMLInputElement>
}

/**
 * Opinionated input with built-in icon slot, action slot (loading, valid,
 * invalid, clear), password toggle, and inline error message. Reads `size`
 * and ARIA attributes from an enclosing Field when available. For custom
 * layouts, use FieldInputRoot, FieldInputIcon, FieldInputControl, and
 * FieldInputAction directly.
 */
export const FieldInput = ({
  isLoading,
  isValid,
  isInvalid,
  type,
  size,
  width,
  className,
  children,
  error,
  clearable,
  ref,
  ...inputProps
}: FieldInputComponentProps) => {
  return (
    <FieldInputRoot
      type={type}
      size={size}
      width={width}
      error={error}
      clearable={clearable}
      ref={ref}
      {...inputProps}
    >
      <FieldInputIcon>{children}</FieldInputIcon>
      <FieldInputControl {...inputProps} className={className} />
      <FieldInputAction
        isLoading={isLoading}
        isValid={isValid}
        isInvalid={isInvalid}
      />
    </FieldInputRoot>
  )
}
FieldInput.displayName = 'FieldInput'
