import type { PartialElement } from '@furystack/shades'
import { Shade, createComponent } from '@furystack/shades'
import { cssVariableTheme } from '../../services/css-variable-theme.js'
import type { Palette } from '../../services/theme-provider-service.js'
import { ThemeProviderService } from '../../services/theme-provider-service.js'
import type { ComponentSize } from '../component-size.js'
import { FormContextToken } from '../form.js'

export type CheckboxProps = {
  /**
   * Whether the checkbox is checked
   */
  checked?: boolean
  /**
   * Whether the checkbox is in an indeterminate state
   */
  indeterminate?: boolean
  /**
   * Whether the checkbox is disabled
   */
  disabled?: boolean
  /**
   * The palette color for the checkbox
   */
  color?: keyof Palette
  /**
   * Callback when the checked state changes
   */
  onchange?: (event: Event) => void
  /**
   * Label text or element displayed next to the checkbox
   */
  labelTitle?: JSX.Element | string
  /**
   * The name attribute for the underlying input element
   */
  name?: string
  /**
   * The value attribute for the underlying input element
   */
  value?: string
  /**
   * Whether the checkbox is required
   */
  required?: boolean
  /**
   * The size of the checkbox.
   * @default 'medium'
   */
  size?: ComponentSize
  /**
   * Optional props for the label element
   */
  labelProps?: PartialElement<HTMLLabelElement>
}

export const Checkbox = Shade<CheckboxProps>({
  customElementName: 'shade-checkbox',
  css: {
    display: 'inline-flex',
    fontFamily: cssVariableTheme.typography.fontFamily,
    alignItems: 'center',

    '& label': {
      display: 'inline-flex',
      alignItems: 'center',
      gap: cssVariableTheme.spacing.sm,
      cursor: 'pointer',
      fontSize: cssVariableTheme.typography.fontSize.sm,
      color: cssVariableTheme.text.primary,
      userSelect: 'none',
      webkitUserSelect: 'none',
    },

    '& .checkbox-control': {
      position: 'relative',
      display: 'inline-flex',
      alignItems: 'center',
      justifyContent: 'center',
      width: '20px',
      height: '20px',
      flexShrink: '0',
    },

    '& input[type="checkbox"]': {
      appearance: 'none',
      webkitAppearance: 'none',
      width: '20px',
      height: '20px',
      margin: '0',
      border: `2px solid ${cssVariableTheme.text.secondary}`,
      borderRadius: cssVariableTheme.shape.borderRadius.xs,
      backgroundColor: 'transparent',
      cursor: 'pointer',
      transition: `all ${cssVariableTheme.transitions.duration.fast} ${cssVariableTheme.transitions.easing.default}`,
      outline: 'none',
    },

    '& input[type="checkbox"]:hover:not(:disabled)': {
      borderColor: 'var(--checkbox-color)',
    },

    '& input[type="checkbox"]:focus-visible': {
      outline: 'none',
      boxShadow: cssVariableTheme.action.focusRing,
    },

    '& input[type="checkbox"]:checked': {
      backgroundColor: 'var(--checkbox-color)',
      borderColor: 'var(--checkbox-color)',
    },

    '& input[type="checkbox"]:checked::after': {
      content: '""',
      position: 'absolute',
      left: '6px',
      top: '2px',
      width: '5px',
      height: '10px',
      border: `solid ${cssVariableTheme.background.paper}`,
      borderWidth: '0 2px 2px 0',
      transform: 'rotate(45deg)',
      pointerEvents: 'none',
    },

    '&[data-indeterminate] input[type="checkbox"]': {
      backgroundColor: 'var(--checkbox-color)',
      borderColor: 'var(--checkbox-color)',
    },

    '&[data-indeterminate] input[type="checkbox"]::after': {
      content: '""',
      position: 'absolute',
      left: '4px',
      top: '8px',
      width: '10px',
      height: '2px',
      background: cssVariableTheme.background.paper,
      border: 'none',
      transform: 'none',
      pointerEvents: 'none',
    },

    '&[data-disabled] label': {
      color: cssVariableTheme.text.disabled,
      cursor: 'not-allowed',
    },

    '&[data-disabled] input[type="checkbox"]': {
      opacity: cssVariableTheme.action.disabledOpacity,
      cursor: 'not-allowed',
    },

    // Size: small
    '&[data-size="small"] label': {
      fontSize: cssVariableTheme.typography.fontSize.xs,
    },
    '&[data-size="small"] .checkbox-control': {
      width: '16px',
      height: '16px',
    },
    '&[data-size="small"] input[type="checkbox"]': {
      width: '16px',
      height: '16px',
    },
    '&[data-size="small"] input[type="checkbox"]:checked::after': {
      left: '4px',
      top: '1px',
      width: '4px',
      height: '8px',
    },
    '&[data-size="small"][data-indeterminate] input[type="checkbox"]::after': {
      left: '3px',
      top: '6px',
      width: '8px',
    },

    // Size: large
    '&[data-size="large"] label': {
      fontSize: cssVariableTheme.typography.fontSize.md,
    },
    '&[data-size="large"] .checkbox-control': {
      width: '24px',
      height: '24px',
    },
    '&[data-size="large"] input[type="checkbox"]': {
      width: '24px',
      height: '24px',
    },
    '&[data-size="large"] input[type="checkbox"]:checked::after': {
      left: '8px',
      top: '3px',
      width: '6px',
      height: '12px',
    },
    '&[data-size="large"][data-indeterminate] input[type="checkbox"]::after': {
      left: '5px',
      top: '10px',
      width: '12px',
    },
  },
  render: ({ props, injector, useDisposable, useHostProps, useRef }) => {
    const inputRef = useRef<HTMLInputElement>('formInput')

    useDisposable('form-registration', () => {
      const formService = injector.get(FormContextToken)
      if (formService) {
        queueMicrotask(() => {
          if (inputRef.current) formService.inputs.add(inputRef.current)
        })
      }
      return {
        [Symbol.dispose]: () => {
          if (inputRef.current && formService) formService.inputs.delete(inputRef.current)
        },
      }
    })

    const themeProvider = injector.get(ThemeProviderService)

    const color = themeProvider.theme.palette[props.color || 'primary'].main
    useHostProps({
      'data-size': props.size && props.size !== 'medium' ? props.size : undefined,
      'data-disabled': props.disabled ? '' : undefined,
      'data-indeterminate': props.indeterminate ? '' : undefined,
      style: { '--checkbox-color': color },
    })

    // The host element already receives `props.onchange` via Shade's attachProps;
    // a bubbled change from the inner input triggers it once. This handler exists
    // only to keep the input in its `indeterminate` visual state across toggles.
    const handleChange = (): void => {
      if (props.indeterminate && inputRef.current) {
        inputRef.current.indeterminate = true
      }
    }

    return (
      <label {...props.labelProps}>
        <span className="checkbox-control">
          <input
            ref={inputRef}
            type="checkbox"
            checked={props.checked}
            disabled={props.disabled}
            name={props.name}
            required={props.required}
            onchange={handleChange}
            {...(props.value !== undefined ? { value: props.value } : {})}
          />
        </span>
        {props.labelTitle ? <span className="checkbox-label">{props.labelTitle}</span> : null}
      </label>
    )
  },
})
