import type { ReactNode, ComponentPropsWithoutRef, ElementType } from 'react'
import {
  createElement,
  createContext,
  forwardRef,
  useContext,
  useId,
} from 'react'
import { ExclamationCircleIcon } from '@heroicons/react/24/outline'
import { omit } from 'ramda'
import { Loader } from './loader'
import clsx from 'clsx'

interface ControlContextProps {
  id?: string
  className?: string
  [key: string]: unknown
}

const ControlContext = createContext<ControlContextProps>({})

interface ControlProviderProps extends ControlContextProps {
  children: ReactNode
}

function ControlProvider({ children, ...props }: ControlProviderProps) {
  return (
    <ControlContext.Provider value={props}>{children}</ControlContext.Provider>
  )
}

function useControl<T extends ControlContextProps>(props?: T) {
  const field = useContext(ControlContext)
  return { ...field, ...props } as T & ControlContextProps
}

interface ControlProps extends ComponentPropsWithoutRef<'div'> {
  as?: ElementType
  children?: ReactNode
}

export const Control = forwardRef<HTMLElement, ControlProps>(function Control(
  { as = 'div', children, ...props },
  ref,
) {
  const id = useId()
  const props_ = omit(['id'], props)

  return (
    <ControlProvider id={props?.id || id} {...props_}>
      {createElement(as, { ref, ...props }, children)}
    </ControlProvider>
  )
})

interface ButtonProps extends ComponentPropsWithoutRef<'button'> {
  as?: ElementType
  isLoading?: boolean
  children?: ReactNode
}

export function Button({
  children,
  as = 'button',
  className,
  isLoading = false,
  ...props
}: ButtonProps) {
  if (isLoading) {
    props['disabled'] = isLoading
  }

  return createElement(
    as,
    {
      className: clsx(
        'font-medium text-green-primary rounded-lg bg-green-secondary hover:bg-green-support',
        'h-12 px-4 relative',
        'disabled:bg-green-secondary/50 disabled:pointer-events-none',
        'transition',
        className,
      ),
      ...props,
    } as React.HTMLAttributes<HTMLElement>,
    <>
      {isLoading && (
        <div className="absolute inset-0 flex items-center justify-center bg-green-secondary rounded-xl">
          <Loader className="w-5 text-white" />
        </div>
      )}
      {children}
    </>,
  )
}

interface LabelProps extends ComponentPropsWithoutRef<'label'> {
  children?: ReactNode
}

export function Label({ children, className, ...props }: LabelProps) {
  const { id, htmlFor, className: _, ...field } = useControl(props)

  return (
    <label
      className={clsx(
        "black cursor-pointer aria-required:after:content-['*'] aria-required:after:ml-0.5 aria-required:after:text-red-500",
        className,
      )}
      htmlFor={htmlFor ?? id}
      {...field}
    >
      {children}
    </label>
  )
}

interface InputProps extends Omit<ComponentPropsWithoutRef<'input'>, 'size'> {
  as?: ElementType
  size?: 'base'
  children?: ReactNode
}
export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
  { as = 'input', size = 'base', className, children, ...props },
  ref,
) {
  const { className: _, ...field } = useControl(props)
  const sizes = { base: 'h-12' }

  return createElement(
    as,
    {
      className: clsx(
        'bg-white outline-none px-4 rounded-lg transition',
        'border border-green-light dark:border-gray-700 dark:bg-gray-800',
        'focus:ring-1 focus:border-green-secondary focus:ring-green-secondary focus:placeholder:text-transparent',
        'dark:focus:border-green-secondary',
        'aria-[invalid=true]:border-red-400 aria-[invalid=true]:ring-red-400',
        'dark:aria-[invalid=true]:border-red-500 dark:aria-[invalid=true]:ring-red-500',
        'disabled:text-gray-400 disabled:border-gray-300 disabled:bg-gray-200',
        'dark:disabled:text-gray-500 dark:disabled:bg-gray-700',
        sizes?.[size],
        className,
      ),
      ref,
      ...field,
    } as React.InputHTMLAttributes<HTMLInputElement>,
    children,
  )
})

interface CheckboxProps extends ComponentPropsWithoutRef<'input'> {
  className?: string
}

export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
  function Checkbox({ className, ...props }, ref) {
    const { className: _, ...field } = useControl(props)

    return (
      <input
        type="checkbox"
        className={clsx(
          'text-green-secondary border border-gray-300',
          'focus:ring-2 focus:border-green-secondary focus:ring-green-secondary focus:ring-offset-0  focus:ring-opacity-30',
          'dark:border-gray-700 dark:bg-gray-800 focus:dark:border-security dark:checked:bg-green-secondary dark:checked:border-security dark:disabled:bg-gray-700',
          'disabled:bg-gray-200 outline-none rounded transition',
          className,
        )}
        ref={ref}
        {...field}
      />
    )
  },
)

interface ErrorProps {
  children?: ReactNode
}

export function Error({ children }: ErrorProps) {
  return (
    <div className="text-sm text-red-500 flex items-start gap-0.5">
      <ExclamationCircleIcon className="w-4 mt-[1.5px] flex-shrink-0" />
      <div>{children}</div>
    </div>
  )
}
