'use client'

import { createContext, useContext, type ComponentProps, type ReactNode } from 'react'
import { Checkbox } from '@base-ui/react/checkbox'
import { CheckboxGroup } from '@base-ui/react/checkbox-group'
import { Radio } from '@base-ui/react/radio'
import { RadioGroup } from '@base-ui/react/radio-group'

import type { GDSComponentProps } from '@graphprotocol/gds-css'

import { useAutoValue, useCSSPropsPolyfill, useCSSState, useGDS } from '../hooks/index.ts'
import { cn, getCSSPropsAttributes, splitProps } from '../utils/index.ts'
import { renderAddon, type AddonValue } from './base/Addon.tsx'
import { ButtonOrLink, type ButtonOrLinkProps } from './base/ButtonOrLink.tsx'
import { ChipGroupMeta, ChipMeta } from './Chip.meta.ts'

type ExtractFn<T> = T extends (...args: infer A) => infer R ? (...args: A) => R : never

export declare namespace ChipProps {
  interface OwnProps {
    /** Name used to submit the chip's value when inside a `<Chip.Group type="checkbox">` in a form. */
    name?: string | undefined
    /** Value used to identify this chip when inside a `<Chip.Group>`. */
    value?: string | undefined
    addonBefore?: AddonValue | undefined
    addonAfter?: AddonValue | undefined
    count?: ReactNode | undefined
  }
  interface CommonProps extends OwnProps, GDSComponentProps<typeof ChipMeta> {}
  interface ButtonProps
    extends
      CommonProps,
      Omit<ButtonOrLinkProps.ButtonProps, 'value'>,
      ButtonOrLinkProps.DisableableProps {}
  interface LinkProps
    extends CommonProps, ButtonOrLinkProps.LinkProps, ButtonOrLinkProps.DisableableProps {}
}

export type ChipProps = ChipProps.ButtonProps | ChipProps.LinkProps

interface ChipGroupContextValue {
  type: 'checkbox' | 'radio'
}

const ChipGroupContext = createContext<ChipGroupContextValue | null>(null)

export function ChipRoot({
  size,
  name,
  value: passedValue,
  addonBefore,
  addonAfter,
  count,
  disabled = false,
  className,
  style,
  children,
  ...props
}: ChipProps) {
  useGDS()

  const { rootProps, nestedProps } = splitProps(props)

  const autoValue = useAutoValue(children)
  const value = passedValue !== undefined ? passedValue : autoValue

  const groupContext = useContext(ChipGroupContext)

  const [stateRef, state] = useCSSState({
    pointer: undefined,
    focus: undefined,
    checked: undefined,
    disabled: Boolean(disabled),
  })
  const [cssPropsPolyfillStateRef, cssPropsPolyfillAttributes] = useCSSPropsPolyfill(
    ChipMeta,
    { size },
    { ref: stateRef },
  )

  return (
    <div
      ref={cssPropsPolyfillStateRef}
      data-type={groupContext?.type ?? 'plain'}
      className={cn(
        `gds-chip root-flex flex-col u:size-max u:max-w-full
        u:has-nested-checked/chip-ref:expose-checked
        u:has-nested-hover/chip-ref:expose-hover
        u:has-nested-focus-visible/chip-ref:expose-focus
        u:has-nested-disabled/chip-ref:expose-disabled
        u:i:has-nested-checked/chip-ref:data-[type=radio]:state-idle`,
        className,
      )}
      {...state.exposedAttributes}
      {...state.polyfillAttributes}
      {...getCSSPropsAttributes(ChipMeta, { size }, style)}
      {...cssPropsPolyfillAttributes}
      {...rootProps}
    >
      {(() => {
        if (!groupContext || props.href !== undefined) {
          return renderButtonOrLink({})
        }

        if (groupContext.type === 'checkbox') {
          return (
            <Checkbox.Root
              name={name}
              value={typeof value === 'string' ? value : undefined}
              disabled={Boolean(disabled)}
              nativeButton
              render={renderButtonOrLink}
            />
          )
        } else {
          return (
            <Radio.Root
              value={value}
              disabled={Boolean(disabled)}
              nativeButton
              render={renderButtonOrLink}
            />
          )
        }

        function renderButtonOrLink({
          tabIndex: _tabIndex,
          defaultChecked: _defaultChecked,
          ...renderProps
        }: Parameters<
          ExtractFn<ComponentProps<typeof Checkbox.Root | typeof Radio.Root>['render']>
        >[0]) {
          return (
            <ButtonOrLink
              {...renderProps}
              disabled={disabled}
              className={cn(
                renderProps.className,
                `nested/chip-ref flex grow items-center justify-center
                rounded-full border border-default text-muted transition
                @state-checked/chip:border-brand-muted
                @state-checked/chip:bg-brand-subtlest
                @state-checked/chip:text-default
                @state-hover/chip:border-strong
                @state-hover/chip:text-default
                @state-hover/chip:@state-checked/chip:border-brand-default
                @state-hover/chip:@state-checked/chip:bg-brand-subtler
                @prop-size-small/chip:h-6
                @prop-size-small/chip:px-1
                @prop-size-small/chip:text-12
                @prop-size-medium/chip:h-8
                @prop-size-medium/chip:px-1.5
                @prop-size-medium/chip:text-14
                @prop-size-large/chip:h-10
                @prop-size-large/chip:px-2
                @prop-size-large/chip:text-16
                u:*:transition-opacity
                u:*:@state-disabled/chip:opacity-disabled
                u:*:@state-disabled/chip:grayscale
                u:@prop-size-small/chip:addon-small
                u:@prop-size-medium/chip:addon-medium
                u:@prop-size-large/chip:addon-large
                i:@state-disabled/chip:border-muted
                i:@state-disabled/chip:bg-transparent
                i:@state-disabled/chip:text-muted
                i:@state-disabled/chip:@state-checked/chip:border-transparent
                i:@state-disabled/chip:@state-checked/chip:bg-brand-subtlest`,
              )}
              {...nestedProps}
            >
              {/* Ensure rounded corners are clickable */}
              <span className="absolute inset-0 not-in-clickable:hidden" />
              {addonBefore ? <ChipAddon side="before">{renderAddon(addonBefore)}</ChipAddon> : null}
              <span className="flex items-baseline gap-1 px-1">
                <span className="truncate">{children}</span>
                {count ? (
                  <span
                    className={`
                      max-w-full shrink-0 truncate text-subtle transition
                      @state-enabled/chip:@state-checked/chip:text-current
                      @state-enabled/chip:@state-hover/chip:text-current
                      @prop-size-small/chip:text-10
                      @prop-size-medium/chip:text-12
                      @prop-size-large/chip:text-14
                    `}
                  >
                    {count}
                  </span>
                ) : null}
              </span>
              {addonAfter ? <ChipAddon side="after">{renderAddon(addonAfter)}</ChipAddon> : null}
            </ButtonOrLink>
          )
        }
      })()}
    </div>
  )
}
ChipRoot.displayName = 'Chip'

function ChipAddon({
  side,
  className,
  children,
  ...props
}: ComponentProps<'span'> & { side: 'before' | 'after' }) {
  return (
    <span
      data-side={side}
      className={cn(
        `gds-addon root-flex items-center justify-center u:h-(--gds-addon-size) u:min-w-(--gds-addon-size) u:shrink-0
        u:has-avatar-group:data-[side=after]:ps-0.5
        u:has-tag:data-[side=after]:ps-0.5
        u:has-avatar-group:data-[side=before]:pe-0.5
        u:has-tag:data-[side=before]:pe-0.5`,
        className,
      )}
      {...props}
    >
      {children}
    </span>
  )
}

export declare namespace ChipGroupProps {
  interface HTMLProps extends Omit<ComponentProps<'div'>, 'onChange'> {}
  interface CommonProps extends HTMLProps, GDSComponentProps<typeof ChipGroupMeta> {}
  interface CheckboxProps extends CommonProps {
    type: 'checkbox'
    name?: undefined
    /** Array of checked/selected chip values. */
    value?: string[] | undefined
    defaultValue?: string[] | undefined
    onChange?: ((value: string[]) => void) | undefined
  }
  interface RadioProps extends CommonProps {
    type: 'radio'
    /** Name used to submit the checked/selected chip value when inside a form. */
    name?: string | undefined
    /** Checked/selected chip value. */
    value?: string | undefined
    defaultValue?: string | undefined
    onChange?: ((value: string) => void) | undefined
  }
}

export type ChipGroupProps = ChipGroupProps.CheckboxProps | ChipGroupProps.RadioProps

export function ChipGroup({
  ref: passedRef,
  type,
  name,
  value,
  defaultValue,
  onChange,
  size,
  className,
  style,
  children,
  ...props
}: ChipGroupProps) {
  useGDS()

  if (props['aria-label'] === undefined && props['aria-labelledby'] === undefined) {
    // oxlint-disable-next-line no-console
    console.warn(
      '[Chip.Group] One of `aria-label` or `aria-labelledby` should be set for accessibility',
    )
  }

  const [cssPropsPolyfillPassedRef, cssPropsPolyfillAttributes, cssProps] = useCSSPropsPolyfill(
    ChipGroupMeta,
    { size },
    { ref: passedRef, returnPropValues: { size } },
  )

  const sharedProps = {
    ref: cssPropsPolyfillPassedRef,
    'data-size': cssProps.size,
    className: cn(
      `gds-chip-group root-flex u:flex-wrap
      u:data-[size=large]:gap-2
      u:data-[size=medium]:gap-1.5
      u:data-[size=small]:gap-1
      u:**:chip:@prop-size-small/chip-group:prop-size-small
      u:**:chip:@prop-size-medium/chip-group:prop-size-medium
      u:**:chip:@prop-size-large/chip-group:prop-size-large`,
      className,
    ),
    ...getCSSPropsAttributes(ChipGroupMeta, { size }, style),
    ...cssPropsPolyfillAttributes,
    ...props,
  }

  return (
    <ChipGroupContext.Provider value={{ type }}>
      {type === 'checkbox' ? (
        <CheckboxGroup
          {...sharedProps}
          {...(value !== undefined && { value })}
          {...(defaultValue !== undefined && { defaultValue })}
          {...(onChange !== undefined && { onValueChange: onChange })}
        >
          {children}
        </CheckboxGroup>
      ) : (
        <RadioGroup
          name={name}
          {...sharedProps}
          {...(value !== undefined && { value })}
          {...(defaultValue !== undefined && { defaultValue })}
          {...(onChange !== undefined && { onValueChange: onChange as (value: unknown) => void })}
        >
          {children}
        </RadioGroup>
      )}
    </ChipGroupContext.Provider>
  )
}
ChipGroup.displayName = 'Chip.Group'
