import { composeEventHandlers } from "@radix-ui/primitive"
import { Primitive } from "@radix-ui/react-primitive"
import { useControllableState } from "@radix-ui/react-use-controllable-state"
import type { ComponentProps, ReactNode } from "react"
import { createContext, useContext, useMemo } from "react"

export type PasswordInputContextProps = Required<
  Pick<PasswordInputProps, "visible" | "onVisibleChange">
>

const PasswordInputContext = createContext<PasswordInputContextProps>({
  visible: false,
  onVisibleChange: () => {},
})

export const usePasswordInputContext = () => useContext(PasswordInputContext)

export interface PasswordInputProps {
  visible?: boolean
  defaultVisible?: boolean
  onVisibleChange?: (visible: boolean) => void
  children?: ReactNode
}

const PasswordInput = ({
  visible: visibleProp,
  defaultVisible,
  onVisibleChange,
  children,
}: PasswordInputProps) => {
  const [visible = false, setVisible] = useControllableState({
    prop: visibleProp,
    defaultProp: defaultVisible ?? false,
    onChange: onVisibleChange,
  })

  const contextValue = useMemo(
    () => ({
      visible,
      onVisibleChange: setVisible,
    }),
    [visible, setVisible],
  )

  return (
    <PasswordInputContext.Provider value={contextValue}>{children}</PasswordInputContext.Provider>
  )
}
PasswordInput.displayName = "PasswordInput"

const PasswordInputInput = ({ ref, ...props }: ComponentProps<typeof Primitive.input>) => {
  const { visible } = usePasswordInputContext()

  return <Primitive.input ref={ref} type={visible ? "text" : "password"} {...props} />
}
PasswordInputInput.displayName = "PasswordInputInput"

const PasswordInputToggle = ({ onClick, ...props }: ComponentProps<typeof Primitive.button>) => {
  const { visible, onVisibleChange } = usePasswordInputContext()

  return (
    <Primitive.button
      type="button"
      data-state={visible ? "visible" : "hidden"}
      onClick={composeEventHandlers(onClick, () => onVisibleChange(!visible))}
      {...props}
    />
  )
}
PasswordInputToggle.displayName = "PasswordInputToggle"

const PasswordInputIndicator = ({ ...props }: ComponentProps<typeof Primitive.span>) => {
  const { visible } = usePasswordInputContext()

  return (
    <Primitive.span aria-hidden="true" data-state={visible ? "visible" : "hidden"} {...props} />
  )
}
PasswordInputIndicator.displayName = "PasswordInputIndicator"

const Root = PasswordInput
const Input = PasswordInputInput
const Toggle = PasswordInputToggle
const Indicator = PasswordInputIndicator

export { Root, Input, Toggle, Indicator }
