import { FC, memo, useCallback, useEffect, useMemo } from 'react'
import Select, { ActionMeta, CSSObjectWithLabel } from 'react-select'
import { isNonNullable } from '../../../src/utils'
import styles from './FactorySelect.module.css'
import { EntitySpan } from '../EntitySpan/EntitySpan'
import { useFactories } from '../../contexts/FactoryProvider'

interface Props {
  id: string
  factories: string[]
  onSetFactories: (factories: string[]) => void
  fixInputs?: boolean
}

interface Option {
  label: string
  value: string
  isFixed: boolean
}

const selectStyles: Record<
  'multiValue' | 'multiValueLabel' | 'multiValueRemove',
  (base: CSSObjectWithLabel, state: { data: Option }) => CSSObjectWithLabel
> = {
  multiValue: (base, state) => {
    return state.data.isFixed
      ? { ...base, backgroundColor: 'var(--fixed-item-bg) !important' }
      : base
  },
  multiValueLabel: (base, state) => {
    return state.data.isFixed ? { ...base, fontWeight: 'bold', paddingRight: '6px' } : base
  },
  multiValueRemove: (base, state) => {
    return state.data.isFixed ? { ...base, display: 'none' } : base
  }
}

const orderOptions = (values: Option[]) => {
  return values.filter(v => v.isFixed).concat(values.filter(v => !v.isFixed))
}

const FactorySelectBase: FC<Props> = ({ id, factories, onSetFactories, fixInputs }) => {
  const { factories: details } = useFactories()
  const options = useMemo<Option[]>(
    () =>
      details.map(detail => ({
        label: detail.name,
        value: detail.href,
        isFixed: !!fixInputs && !detail.recipe
      })),
    [details, fixInputs]
  )

  const state = useMemo<typeof options>(() => {
    return factories
      .map(factory => options.find(option => option.value === factory))
      .filter(isNonNullable)
  }, [factories, options])

  useEffect(() => {
    if (!fixInputs) return
    const set = new Set(factories)
    const addOptions = options
      .filter(option => option.isFixed && !set.has(option.value))
      .map(option => option.value)
    if (addOptions.length) {
      onSetFactories([...factories, ...addOptions])
    }
  }, [factories, fixInputs, onSetFactories, options])

  const onChange = useCallback(
    (values: readonly Option[], { action, removedValue }: ActionMeta<Option>) => {
      if ((action === 'remove-value' || action === 'pop-value') && removedValue.isFixed) {
        return
      } else if (action === 'clear') {
        const clearedValues = orderOptions(options.filter(v => v.isFixed))
        onSetFactories(clearedValues.map(s => s?.value))
      } else {
        onSetFactories(values.map(s => s?.value))
      }
    },
    [onSetFactories, options]
  )

  return (
    <Select
      id={id}
      instanceId={id}
      value={orderOptions(state)}
      isClearable={state.some(option => !option.isFixed)}
      components={{
        MultiValueLabel: ({ data, innerProps }) => (
          <EntitySpan {...innerProps} value={data.value} simpleStyle={true} />
        ),
        Option: ({ innerProps, data }) => (
          <div {...innerProps} className={styles.option}>
            <EntitySpan value={data.value} simpleStyle={true} />
          </div>
        )
      }}
      isMulti
      options={orderOptions(options)}
      onChange={onChange}
      className={styles.select}
      classNamePrefix={'factory-select'}
      styles={selectStyles}
    />
  )
}

export const FactorySelect = memo(FactorySelectBase)
