import React from 'react'

import Button from '../Button/Button'
import { hasValue } from '../../services/HelperServiceTyped'
import Tooltip from '../Tooltip/Tooltip'
import { type TooltipProps } from '../Tooltip/Tooltip'
import FormLabel from '../FormLabel/FormLabel'
import styles from './_picker.module.scss'
import { type ButtonProps } from '../Button/Button.models'

export default function Picker<
  OptionGeneric extends RequiredOptionKeys<OptionGeneric['value']>,
  StateGeneric,
>({
  options,
  state,
  callout,
  stateName,
  customClass,
  valuesAreBooleans,
  disabled,
  exposedFilterVariant,
}: StateAndStateNameProps<OptionGeneric, StateGeneric>): React.JSX.Element

export default function Picker<
  OptionGeneric extends RequiredOptionKeys<OptionGeneric['value']>,
  StateGeneric,
>({
  options,
  selected,
  callout,
  customClass,
  valuesAreBooleans,
  disabled,
  exposedFilterVariant,
}: SelectedProps<OptionGeneric, StateGeneric>): React.JSX.Element

export default function Picker<
  OptionGeneric extends RequiredOptionKeys<OptionGeneric['value']>,
  StateGeneric,
>({
  options,
  selected,
  state,
  callout,
  stateName,
  customClass,
  valuesAreBooleans = false,
  disabled = false,
  error = false,
  required,
  labelText,
  rightLabel,
  labelTooltip,
  tooltip,
  qaTestId = 'picker',
  exposedFilterVariant = false,
}: PickerProps<OptionGeneric, StateGeneric>): React.JSX.Element {
  const inlineStyles = {
    '--options-length': options.length,
  } as React.CSSProperties

  const renderPicker = () => {
    const isActive = (optionValue: OptionGeneric['value']) => {
      if (valuesAreBooleans) {
        return optionValue === selected
      }
      return (
        optionValue ===
        (selected ||
          (state as Record<string | number, unknown>)?.[stateName ?? ''])
      )
    }

    return (
      <div
        data-id='picker-container'
        className={`${styles.container} ${customClass || ''} ${
          disabled ? styles.disabled : ''
        } ${error ? styles.errorBorder : ''} ${
          exposedFilterVariant ? styles.exposedFilterVariant : ''
        }`}
        style={inlineStyles}
      >
        {options.map((e, i) => {
          const active = isActive(e.value)
          const buttonElement = (
            <Button
              as='unstyled'
              data-id='picker-button'
              className={`${styles.button} ${active ? styles.active : ''} ${
                error ? styles.errorBorder : ''
              } ${exposedFilterVariant ? styles.exposedFilterButton : ''}`}
              disabled={disabled}
              onClick={() => {
                if (!disabled) {
                  if (hasValue(selected)) {
                    const actualCallout = callout as SelectedPropsCallout<
                      OptionGeneric['value']
                    >
                    actualCallout(e.value)
                  } else {
                    const actualCallout = callout as StateAndStateNameCallout<
                      OptionGeneric['value']
                    >
                    actualCallout(stateName ?? '', e.value)
                  }
                }
              }}
              data-text={e.text}
            >
              <span className={styles.picker} data-text={e.text}>
                {e.text}
              </span>
            </Button>
          )

          // For exposedFilterVariant, wrap individual buttons with tooltips if provided
          if (
            exposedFilterVariant &&
            'tooltip' in e &&
            e.tooltip &&
            e.tooltip.tooltipContent
          ) {
            return (
              <Tooltip
                key={e.text ?? e.id ?? i}
                position={e.tooltip.position ?? 'top'}
                tooltipContent={e.tooltip.tooltipContent}
                {...e.tooltip}
              >
                {buttonElement}
              </Tooltip>
            )
          }

          return (
            <React.Fragment key={e.text ?? e.id ?? i}>
              {buttonElement}
            </React.Fragment>
          )
        })}
      </div>
    )
  }
  return (
    <div qa-test-id={qaTestId}>
      {labelText && (
        <FormLabel
          label={labelText}
          tooltip={labelTooltip}
          required={required}
          rightLabel={rightLabel}
          disabled={disabled}
          error={error}
        />
      )}
      {/*
        Note: When exposedFilterVariant is true and individual option tooltips are provided,
        the picker-level tooltip prop should not be used to avoid nested/conflicting tooltips.
        Individual option tooltips take precedence in this case.
      */}
      {tooltip &&
      !(
        exposedFilterVariant &&
        options.some((opt) => 'tooltip' in opt && opt.tooltip?.tooltipContent)
      ) ? (
        <Tooltip position='bottom' {...tooltip}>
          {renderPicker()}
        </Tooltip>
      ) : (
        renderPicker()
      )}
    </div>
  )
}

/**
 *  pass in an array of options to select that will be mapped out and styled for you
 *  - then pass in what value is currently selected in state or pass in a state object
 *  w/ fieldName and the component will show that option as selected - state needs to
 *  be managed outside of this component and in the file you are consuming it in.
 **/
interface RequiredOptionKeys<ValueTypeGeneric> {
  id?: number
  text?: string
  value: ValueTypeGeneric
  /** Tooltip to display on hover for each option button (only available when exposedFilterVariant is true) */
  tooltip?: Omit<TooltipProps, 'children'>
}

type Selected = string | number | boolean

// Helper type to conditionally exclude tooltip when exposedFilterVariant is not true
type OptionsWithoutTooltip<T> = T extends { tooltip?: unknown }
  ? Omit<T, 'tooltip'>
  : T

type PickerBaseProps<
  OptionGeneric extends RequiredOptionKeys<OptionGeneric['value']>,
> = {
  customClass?: string
  valuesAreBooleans?: boolean
  disabled?: boolean
  labelText?: string
  rightLabel?: React.ReactNode
  labelTooltip?: Omit<TooltipProps, 'children'>
  required?: boolean
  error?: boolean
  /** Optional prop to add a test id to the Picker for QA testing */
  qaTestId?: string
} & (
  | {
      /** Use the exposed filter variant styling - when true, options can include tooltip */
      exposedFilterVariant: true
      options: OptionGeneric[]
    }
  | {
      /** Use the exposed filter variant styling - when false/undefined, options cannot have tooltip */
      exposedFilterVariant?: false
      options: Array<OptionsWithoutTooltip<OptionGeneric>>
    }
)

type StateAndStateNameCallout<Param2Generic> = (
  param1: string,
  param2: Param2Generic,
) => void

// if you use state, then it and stateName are both required.
type StateAndStateNameProps<
  OptionGeneric extends RequiredOptionKeys<OptionGeneric['value']>,
  StateGeneric,
> = PickerBaseProps<OptionGeneric> & {
  state: StateGeneric
  stateName: string
  selected?: Selected
  callout: StateAndStateNameCallout<OptionGeneric['value']>
  tooltip?: ButtonProps['tooltip']
}

type SelectedPropsCallout<Param1Generic> = (param1: Param1Generic) => void
// if you use selected, then it's required... unless there isn't anything selected yet. Then it's optional
type SelectedProps<
  OptionGeneric extends RequiredOptionKeys<OptionGeneric['value']>,
  StateGeneric,
> = PickerBaseProps<OptionGeneric> & {
  state?: StateGeneric
  stateName?: string
  selected?: Selected
  callout: SelectedPropsCallout<OptionGeneric['value']>
  tooltip?: ButtonProps['tooltip']
}

export type PickerProps<
  OptionGeneric extends RequiredOptionKeys<OptionGeneric['value']>,
  StateGeneric,
> =
  | SelectedProps<OptionGeneric, StateGeneric>
  | StateAndStateNameProps<OptionGeneric, StateGeneric>

// here's an example of passed in options
// [
//   { id: 0, text: 'SNOWBOARDING', value: 'SNOWBOARDING' },
//   { id: 1, text: 'SKIING', value: 'SKIING' },
// ]

// selected value could be - 'SNOWBOARDING'
// or if state obj and stateName i.e. key value pair - {preferred_method: 'SNOWBOARDING'} and would // statifsy component
