import React, { useCallback, useMemo, useState } from 'react'

import { iAMServiceListAPIKeys } from 'apiClient/services/core-server'
import type { V1APIKey } from 'apiClient/services/core-server/interface'
import DebounceSelect from 'components/DebounceSelect/DebounceSelect'

type APIKeyOption = {
  label: React.ReactNode
  value: string
}

type APIKeySearchInputValue = string | string[]

export interface APIKeySearchInputProps {
  value?: APIKeySearchInputValue
  onChange?: (value?: APIKeySearchInputValue) => void
  disabled?: boolean
  placeholder?: string
  className?: string
  style?: React.CSSProperties
  allowClear?: boolean
  mode?: 'multiple'
}

const DEFAULT_PLACEHOLDER = 'Search by API key name'
const DEFAULT_PAGE_SIZE = 20

function formatAPIKeyLabel(apiKey: V1APIKey) {
  const name = apiKey.name

  if (!name) {
    return ''
  }

  return apiKey.owner ? `${name} (${apiKey.owner})` : name
}

const APIKeySearchInput: React.FC<APIKeySearchInputProps> = ({
  value,
  onChange,
  disabled,
  placeholder = DEFAULT_PLACEHOLDER,
  className,
  style,
  allowClear = true,
  mode
}) => {
  const [innerValue, setInnerValue] = useState<APIKeySearchInputValue>()
  const [selectedOptions, setSelectedOptions] = useState<APIKeyOption[]>([])
  const isMultiple = mode === 'multiple'

  const currentValue = value ?? innerValue

  const fetchOptions = useCallback(async (search: string) => {
    const { data } = await iAMServiceListAPIKeys({
      nameLike: search,
      page: 1,
      perPage: DEFAULT_PAGE_SIZE
    })

    return (data.apiKeys || [])
      .filter((item) => item.name)
      .map((item) => {
        const name = item.name!

        return {
          label: formatAPIKeyLabel(item),
          value: name
        }
      })
  }, [])

  const selectValue = useMemo(() => {
    if (
      !currentValue ||
      (Array.isArray(currentValue) && currentValue.length === 0)
    ) {
      return undefined
    }

    const currentValues = Array.isArray(currentValue)
      ? currentValue
      : [currentValue]

    const mappedOptions = currentValues.map((item) => {
      return (
        selectedOptions.find(
          (selectedOption) => selectedOption.value === item
        ) || {
          value: item,
          label: item
        }
      )
    })

    return isMultiple ? mappedOptions : mappedOptions[0]
  }, [currentValue, isMultiple, selectedOptions])

  const handleChange = (option?: APIKeyOption | APIKeyOption[]) => {
    const nextOptions = Array.isArray(option) ? option : option ? [option] : []
    const nextSelectedOptions = nextOptions.map((selectedOption) => ({
      value: selectedOption.value.toString(),
      label: selectedOption.label
    }))
    const nextValue = isMultiple
      ? nextSelectedOptions.map((selectedOption) => selectedOption.value)
      : nextSelectedOptions[0]?.value

    if (value === undefined) {
      setInnerValue(nextValue)
    }

    setSelectedOptions(nextSelectedOptions)
    onChange?.(nextValue)
  }

  return (
    <DebounceSelect<APIKeyOption>
      value={selectValue}
      mode={mode}
      disabled={disabled}
      placeholder={placeholder}
      className={className}
      style={style}
      allowClear={allowClear}
      showSearch
      fetchOptions={fetchOptions}
      onChange={handleChange}
    />
  )
}

export default APIKeySearchInput
export { APIKeySearchInput }
