"use client"

import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"

import { cn } from "@/lib/utils"

type RadioGroupOrientation = "vertical" | "horizontal"
type RadioGroupVisualState = "default" | "error" | "disabled"
type RadioFieldControlPosition = "start" | "end"

const radioRoot = cva(
  "group/radio relative inline-grid size-[var(--bh-space-4xl-20)] shrink-0 place-items-center rounded-[var(--bh-radius-full)]"
)

const radioControl = cva([
  "pointer-events-none flex size-[var(--bh-space-3xl-16)] items-center justify-center rounded-[var(--bh-radius-full)]",
  "bg-[var(--bh-bg-default)] shadow-[var(--shadow-radio-control)] transition-[background-color,box-shadow]",
  "[--bh-radio-control-border:var(--bh-border-input)]",
  "group-data-[invalid=true]/radio:[--bh-radio-control-border:var(--bh-border-danger-strong)]",
  "group-hover/radio-field:[--bh-radio-control-border:var(--bh-border-brand-strong)]",
  "peer-checked:bg-[var(--bh-interactive-radiobutton-default)] peer-checked:[--bh-radio-control-border:transparent]",
  "peer-focus-visible:shadow-[var(--shadow-radio-control),var(--shadow-button-focus)]",
  "peer-disabled:bg-[var(--bh-bg-disabled)] peer-disabled:shadow-[inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-border-disabled)]",
])

const radioGroupRoot = cva(
  "m-0 grid min-w-0 border-0 p-0 text-start [min-inline-size:0]",
  {
    variants: {
      state: {
        default: "",
        error: "",
        disabled: "",
      },
    },
    defaultVariants: {
      state: "default",
    },
  }
)

const radioGroupItems = cva("flex min-w-0", {
  variants: {
    orientation: {
      vertical: "flex-col gap-[var(--bh-space-xl-12)]",
      horizontal:
        "flex-row flex-wrap items-start gap-x-[var(--bh-space-3xl-16)] gap-y-[var(--bh-space-xl-12)]",
    },
  },
  defaultVariants: {
    orientation: "vertical",
  },
})

const radioField = cva(
  "group/radio-field inline-flex min-w-0 max-w-full gap-[var(--bh-space-lg-10)] text-start",
  {
    variants: {
      controlPosition: {
        start: "",
        end: "justify-between",
      },
      density: {
        compact: "items-center",
        stacked: "items-start",
      },
    },
    defaultVariants: {
      controlPosition: "start",
      density: "compact",
    },
  }
)

type RadioProps = Omit<React.ComponentProps<"input">, "size" | "type"> &
  VariantProps<typeof radioRoot> & {
    controlClassName?: string
    indicatorClassName?: string
    inputClassName?: string
    invalid?: boolean
  }

const Radio = React.forwardRef<HTMLInputElement, RadioProps>(function Radio({
  className,
  controlClassName,
  indicatorClassName,
  inputClassName,
  invalid = false,
  ...props
}, ref) {
  return (
    <span
      data-invalid={invalid ? "true" : undefined}
      data-slot="radio"
      className={cn(radioRoot({ className }))}
    >
      <input
        data-slot="radio-input"
        ref={ref}
        type="radio"
        className={cn(
          "peer absolute inset-0 z-[var(--bh-z-raised)] m-0 size-full cursor-pointer appearance-none rounded-[var(--bh-radius-full)] opacity-0 outline-none disabled:cursor-not-allowed",
          inputClassName
        )}
        {...props}
      />
      <span
        aria-hidden="true"
        data-slot="radio-control"
        className={cn(radioControl(), controlClassName)}
      >
        <span
          data-slot="radio-indicator"
          className={cn(
            "size-[var(--bh-space-sm-6)] rounded-[var(--bh-radius-full)] bg-[var(--bh-bg-always-white)] opacity-[var(--bh-opacity-0)] transition-opacity peer-checked:opacity-[var(--bh-opacity-100)] peer-disabled:bg-[var(--bh-content-disabled)]",
            indicatorClassName
          )}
        />
      </span>
    </span>
  )
})
Radio.displayName = "Radio"

type RadioGroupContextValue = {
  disabled?: boolean
  invalid?: boolean
  name: string
  onValueChange: (value: string) => void
  required?: boolean
  value?: string
}

const RadioGroupContext = React.createContext<RadioGroupContextValue | null>(
  null
)

type RadioGroupProps = Omit<
  React.ComponentProps<"fieldset">,
  "children" | "defaultValue" | "onChange"
> &
  VariantProps<typeof radioGroupRoot> & {
    children?: React.ReactNode
    defaultValue?: string
    description?: React.ReactNode
    errorMessage?: React.ReactNode
    hasDescription?: boolean
    hasLabel?: boolean
    isOptional?: boolean
    isRequired?: boolean
    itemsClassName?: string
    label?: React.ReactNode
    name?: string
    onValueChange?: (value: string) => void
    optionalText?: React.ReactNode
    orientation?: RadioGroupOrientation
    required?: boolean
    state?: RadioGroupVisualState
    value?: string
  }

const RadioGroup = React.forwardRef<HTMLFieldSetElement, RadioGroupProps>(
  function RadioGroup({
    children,
    className,
    defaultValue,
    description,
    disabled,
    errorMessage,
    hasDescription,
    hasLabel,
    isOptional = false,
    isRequired = false,
    itemsClassName,
    label,
    name,
    onValueChange,
    optionalText,
    orientation = "vertical",
    required,
    state = "default",
    value,
    ...props
  }, ref) {
    const generatedId = React.useId()
    const groupName = name ?? `radio-group-${generatedId.replace(/:/g, "")}`
    const isControlled = value !== undefined
    const [uncontrolledValue, setUncontrolledValue] =
      React.useState(defaultValue)
    const selectedValue = isControlled ? value : uncontrolledValue
    const visualState: RadioGroupVisualState = disabled
      ? "disabled"
      : errorMessage
        ? "error"
        : state
    const invalid = visualState === "error"
    const labelId = `${generatedId}-label`
    const descriptionId = `${generatedId}-description`
    const errorId = `${generatedId}-error`
    const shouldRenderLabel = hasLabel ?? hasRenderableContent(label)
    const shouldRenderDescription =
      hasDescription ?? hasRenderableContent(description)
    const shouldRenderError = hasRenderableContent(errorMessage)
    const describedBy = joinIds(
      shouldRenderDescription ? descriptionId : undefined,
      shouldRenderError ? errorId : undefined
    )
    const context = React.useMemo<RadioGroupContextValue>(
      () => ({
        disabled,
        invalid,
        name: groupName,
        onValueChange: (nextValue) => {
          if (!isControlled) setUncontrolledValue(nextValue)
          onValueChange?.(nextValue)
        },
        required: isRequired || required,
        value: selectedValue,
      }),
      [
        disabled,
        groupName,
        invalid,
        isControlled,
        isRequired,
        onValueChange,
        required,
        selectedValue,
      ]
    )

    return (
      <fieldset
        aria-describedby={describedBy}
        aria-invalid={invalid || undefined}
        data-orientation={orientation}
        data-slot="radio-group"
        data-state={visualState}
        disabled={disabled}
        ref={ref}
        className={cn(radioGroupRoot({ state: visualState }), className)}
        {...props}
      >
        {shouldRenderLabel ? (
          <legend
            data-slot="radio-group-label"
            id={labelId}
            className="m-0 flex max-w-full items-center gap-[var(--bh-space-xs-4)] p-0 text-start"
          >
            <span
              data-slot="radio-group-label-text"
              dir="auto"
              className="min-w-0 text-[length:var(--bh-text-body-sm-medium-font-size)] font-[var(--bh-text-body-sm-medium-font-weight)] leading-[var(--bh-text-body-sm-medium-line-height)] tracking-[var(--bh-text-body-sm-medium-letter-spacing)] text-[var(--bh-content-default)]"
            >
              {label}
            </span>
            {isRequired || required ? (
              <span
                aria-hidden="true"
                data-slot="radio-group-required"
                className="shrink-0 text-[length:var(--bh-text-body-xs-regular-font-size)] font-[var(--bh-text-body-xs-regular-font-weight)] leading-[var(--bh-text-body-xs-regular-line-height)] tracking-[var(--bh-text-body-xs-regular-letter-spacing)] text-[var(--bh-content-danger-default)]"
              >
                *
              </span>
            ) : null}
            {isOptional && hasRenderableContent(optionalText) ? (
              <span
                data-slot="radio-group-optional"
                dir="auto"
                className="min-w-0 text-[length:var(--bh-text-body-xs-regular-font-size)] font-[var(--bh-text-body-xs-regular-font-weight)] leading-[var(--bh-text-body-xs-regular-line-height)] tracking-[var(--bh-text-body-xs-regular-letter-spacing)] text-[var(--bh-content-subtle)]"
              >
                {optionalText}
              </span>
            ) : null}
          </legend>
        ) : null}

        {shouldRenderDescription ? (
          <p
            data-slot="radio-group-description"
            dir="auto"
            id={descriptionId}
            className={cn(
              shouldRenderLabel && "mt-[var(--bh-space-xxs-2)]",
              "m-0 max-w-[var(--bh-radio-group-message-max-width)] text-[length:var(--bh-text-body-xs-regular-font-size)] font-[var(--bh-text-body-xs-regular-font-weight)] leading-[var(--bh-text-body-xs-regular-line-height)] tracking-[var(--bh-text-body-xs-regular-letter-spacing)] text-[var(--bh-content-subtle)]"
            )}
          >
            {description}
          </p>
        ) : null}

        <RadioGroupContext.Provider value={context}>
          <div
            data-orientation={orientation}
            data-slot="radio-group-items"
            className={cn(
              (shouldRenderLabel || shouldRenderDescription) &&
                "mt-[var(--bh-space-md-8)]",
              radioGroupItems({ orientation }),
              itemsClassName
            )}
          >
            {children}
          </div>
        </RadioGroupContext.Provider>

        {shouldRenderError ? (
          <p
            data-slot="radio-group-error"
            dir="auto"
            id={errorId}
            className="m-0 mt-[var(--bh-space-md-8)] max-w-[var(--bh-radio-group-message-max-width)] text-[length:var(--bh-text-body-xs-regular-font-size)] font-[var(--bh-text-body-xs-regular-font-weight)] leading-[var(--bh-text-body-xs-regular-line-height)] tracking-[var(--bh-text-body-xs-regular-letter-spacing)] text-[var(--bh-content-danger-default)]"
          >
            {errorMessage}
          </p>
        ) : null}
      </fieldset>
    )
  }
)
RadioGroup.displayName = "RadioGroup"

type RadioFieldInputProps = Omit<
  RadioProps,
  | "checked"
  | "className"
  | "controlClassName"
  | "defaultChecked"
  | "disabled"
  | "indicatorClassName"
  | "inputClassName"
  | "invalid"
  | "name"
  | "onChange"
  | "required"
  | "type"
  | "value"
>

type RadioFieldProps = Omit<
  React.ComponentProps<"label">,
  "children" | "onChange"
> &
  VariantProps<typeof radioField> & {
    checked?: boolean
    controlPosition?: RadioFieldControlPosition
    defaultChecked?: boolean
    description?: React.ReactNode
    disabled?: boolean
    inputClassName?: string
    inputProps?: RadioFieldInputProps
    invalid?: boolean
    label: React.ReactNode
    name?: string
    onCheckedChange?: (checked: boolean) => void
    radioClassName?: string
    required?: boolean
    value?: string
  }

const RadioField = React.forwardRef<HTMLLabelElement, RadioFieldProps>(
  function RadioField(props, ref) {
    return <RadioFieldBase ref={ref} {...props} />
  }
)
RadioField.displayName = "RadioField"

type RadioGroupItemProps = Omit<
  React.ComponentProps<"label">,
  "children" | "onChange"
> &
  VariantProps<typeof radioField> & {
    controlPosition?: RadioFieldControlPosition
    description?: React.ReactNode
    disabled?: boolean
    inputClassName?: string
    inputProps?: RadioFieldInputProps
    label: React.ReactNode
    onCheckedChange?: (checked: boolean) => void
    radioClassName?: string
    value: string
  }

const RadioGroupItem = React.forwardRef<HTMLLabelElement, RadioGroupItemProps>(
  function RadioGroupItem({
    disabled,
    inputProps,
    onCheckedChange,
    value,
    ...props
  }, ref) {
    const group = React.useContext(RadioGroupContext)
    const isChecked = group?.value === value
    const isDisabled = disabled || group?.disabled

    return (
      <RadioFieldBase
        ref={ref}
        checked={group ? isChecked : undefined}
        disabled={isDisabled}
        inputProps={inputProps}
        invalid={group?.invalid}
        name={group?.name}
        onCheckedChange={(checked) => {
          if (checked) group?.onValueChange(value)
          onCheckedChange?.(checked)
        }}
        required={group?.required}
        value={value}
        {...props}
      />
    )
  }
)
RadioGroupItem.displayName = "RadioGroupItem"

type RadioFieldBaseProps = RadioFieldProps

const RadioFieldBase = React.forwardRef<HTMLLabelElement, RadioFieldBaseProps>(
  function RadioFieldBase({
    checked,
    className,
    controlPosition = "start",
    defaultChecked,
    description,
    disabled,
    inputClassName,
    inputProps,
    invalid = false,
    label,
    name,
    onCheckedChange,
    radioClassName,
    required,
    value,
    ...props
  }, ref) {
    const generatedId = React.useId()
    const descriptionId = `${generatedId}-description`
    const hasDescription = hasRenderableContent(description)
    const isDisabled = disabled
    const density = hasDescription ? "stacked" : "compact"
    const ariaDescribedBy = joinIds(
      inputProps?.["aria-describedby"],
      hasDescription ? descriptionId : undefined
    )

    const control = (
      <span
        data-slot="radio-field-control"
        className={cn(
          "inline-flex shrink-0",
          hasDescription && "pt-[var(--bh-space-xxs-2)]"
        )}
      >
        <Radio
          {...inputProps}
          aria-describedby={ariaDescribedBy}
          aria-invalid={invalid || undefined}
          checked={checked}
          className={radioClassName}
          defaultChecked={defaultChecked}
          disabled={isDisabled}
          inputClassName={inputClassName}
          invalid={invalid}
          name={name}
          onChange={(event) => {
            if (event.currentTarget.checked) onCheckedChange?.(true)
          }}
          required={required}
          value={value}
        />
      </span>
    )
    const content = (
      <span
        data-slot="radio-field-content"
        className="flex min-w-0 flex-1 flex-col items-start gap-[var(--bh-space-xxs-2)]"
      >
        <span
          data-slot="radio-field-label"
          dir="auto"
          className={cn(
            "min-w-0 break-words text-[length:var(--bh-text-body-md-regular-font-size)] leading-[var(--bh-text-body-md-regular-line-height)] tracking-[var(--bh-text-body-md-regular-letter-spacing)] text-[var(--bh-content-default)] group-data-[disabled=true]/radio-field:text-[var(--bh-content-disabled)]",
            hasDescription
              ? "font-[var(--bh-text-body-md-medium-font-weight)]"
              : "font-[var(--bh-text-body-md-regular-font-weight)]"
          )}
        >
          {label}
        </span>
        {hasDescription ? (
          <span
            data-slot="radio-field-description"
            dir="auto"
            id={descriptionId}
            className="min-w-0 break-words text-[length:var(--bh-text-body-sm-regular-font-size)] font-[var(--bh-text-body-sm-regular-font-weight)] leading-[var(--bh-text-body-sm-regular-line-height)] tracking-[var(--bh-text-body-sm-regular-letter-spacing)] text-[var(--bh-content-subtle)] group-data-[disabled=true]/radio-field:text-[var(--bh-content-disabled)]"
          >
            {description}
          </span>
        ) : null}
      </span>
    )

    return (
      <label
        data-checked={checked ? "true" : "false"}
        data-disabled={isDisabled ? "true" : undefined}
        data-invalid={invalid ? "true" : undefined}
        data-slot="radio-field"
        ref={ref}
        className={cn(
          radioField({ controlPosition, density }),
          isDisabled ? "cursor-not-allowed" : "cursor-pointer",
          className
        )}
        {...props}
      >
        {controlPosition === "start" ? (
          <>
            {control}
            {content}
          </>
        ) : (
          <>
            {content}
            {control}
          </>
        )}
      </label>
    )
  }
)
RadioFieldBase.displayName = "RadioFieldBase"

function hasRenderableContent(content: React.ReactNode) {
  return (
    content !== undefined &&
    content !== null &&
    content !== false &&
    content !== ""
  )
}

function joinIds(...ids: Array<string | undefined | false>) {
  const value = ids.filter(Boolean).join(" ")
  return value || undefined
}

export {
  Radio,
  RadioField,
  RadioGroup,
  RadioGroupItem,
  radioControl,
  radioField,
  radioGroupItems,
  radioGroupRoot,
  radioRoot,
}
export type {
  RadioFieldControlPosition,
  RadioFieldProps,
  RadioGroupItemProps,
  RadioGroupOrientation,
  RadioGroupProps,
  RadioGroupVisualState,
  RadioProps,
}
