'use client'

import type { ComponentProps, ElementType } from 'react'

import {
  ButtonOrLink,
  type ButtonOrLinkRenderState,
  type InternalButtonOrLinkProps,
  type OmitInternalButtonOrLinkProps,
} from './ButtonOrLink.tsx'
import type { RenderFn } from './Render.tsx'

type OtherElementRenderState = {
  category: 'other'
  role: ButtonOrLinkRenderState['role']
  disabled: false
  type?: undefined
  checked?: undefined
  href?: undefined
  target?: undefined
  Element: ButtonOrLinkRenderState['Element']
  elementProps: ButtonOrLinkRenderState['elementProps']
}

type MaybeButtonOrLinkRenderState = ButtonOrLinkRenderState | OtherElementRenderState

export declare namespace MaybeButtonOrLinkProps {
  export type DisableableProps = InternalButtonOrLinkProps.DisableableProps
  export type ButtonProps = OmitInternalButtonOrLinkProps<InternalButtonOrLinkProps.ButtonProps> & {
    onClick: NonNullable<InternalButtonOrLinkProps.ButtonProps['onClick']>
    as?: undefined
    render?: RenderFn<MaybeButtonOrLinkRenderState> | undefined
  }
  export type ToggleButtonProps =
    OmitInternalButtonOrLinkProps<InternalButtonOrLinkProps.ToggleButtonProps> & {
      onClick: NonNullable<InternalButtonOrLinkProps.ToggleButtonProps['onClick']>
      as?: undefined
      render?: RenderFn<MaybeButtonOrLinkRenderState> | undefined
    }
  export type LinkProps = OmitInternalButtonOrLinkProps<InternalButtonOrLinkProps.LinkProps> & {
    as?: undefined
    render?: RenderFn<MaybeButtonOrLinkRenderState> | undefined
  }
  export type OtherElementProps =
    OmitInternalButtonOrLinkProps<InternalButtonOrLinkProps.CommonProps> &
      ComponentProps<'span'> & {
        onClick?: InternalButtonOrLinkProps.ButtonProps['onClick'] // Not technically correct, but prevents the event from being `any` when `as` is not specified
        as?: ElementType | undefined
        render?: RenderFn<MaybeButtonOrLinkRenderState> | undefined
        disabled?: undefined
        type?: undefined
        checked?: undefined
        href?: undefined
        target?: undefined
      }
}

export type MaybeButtonOrLinkProps =
  | MaybeButtonOrLinkProps.ButtonProps
  | MaybeButtonOrLinkProps.ToggleButtonProps
  | MaybeButtonOrLinkProps.LinkProps
  | MaybeButtonOrLinkProps.OtherElementProps

type InternalMaybeButtonOrLinkProps = MaybeButtonOrLinkProps &
  InternalButtonOrLinkProps.DisableableProps &
  Omit<InternalButtonOrLinkProps.CommonProps, 'render'>

/**
 * Renders a `ButtonOrLink` if one of `href` or `onClick` is passed along with no `as`. Otherwise,
 * renders the element type passed in `as` (defaulting to `span`).
 */
export function MaybeButtonOrLink(props: InternalMaybeButtonOrLinkProps) {
  if ((props.href !== undefined || props.onClick !== undefined) && props.as === undefined) {
    return <ButtonOrLink {...(props as InternalButtonOrLinkProps)} />
  } else {
    const {
      as: Element = 'span',
      render,
      disabled: _disabled,
      inline: _inline,
      linkComponent: _linkComponent,
      type: _type,
      checked: _checked,
      href: _href,
      target: _target,
      ...otherElementProps
    } = props

    const renderState: OtherElementRenderState = {
      category: 'other',
      role: props.role ?? 'none',
      disabled: false,
      // The types lie to ensure there's a type error if consumers forget to spread `elementProps` on `Element`
      Element: Element as OtherElementRenderState['Element'],
      elementProps: {} as OtherElementRenderState['elementProps'],
    }

    return render ? render(otherElementProps, renderState) : <Element {...otherElementProps} />
  }
}
