/* eslint-disable react/jsx-props-no-spreading */
/* eslint-disable import/prefer-default-export */
import classNames from 'classnames'
import {
  useState,
  useEffect,
  useLayoutEffect,
  useRef,
  useCallback,
  useId,
  ReactElement,
  MouseEvent,
  CSSProperties,
  FocusEvent,
} from 'react'
import { BiX } from 'react-icons/bi'
import { DropDown } from '../dropdown/DropDown'
import { OptionProps } from './Select'

export interface MultiSelectProps {
  label?: string
  id?: string
  dataTestId?: string
  name?: string
  errorMessage?: string
  leftContent?: ReactElement
  rightContent?: ReactElement
  options: OptionProps[]
  value?: (string | number)[]
  onChange?: (value: (string | number)[]) => void
  placeholder?: string
  isLoading?: boolean
  horizontal?: boolean
  required?: boolean
  className?: string
  renderOption?: (option: OptionProps) => ReactElement
  renderTag?: (option: OptionProps) => ReactElement
  showSearch?: boolean
  searchPlaceholder?: string
}

const baseClass = 'w-full select-bordered border rounded-lg flex items-center gap-2 min-h-9 py-2 px-2'

export const MultiSelect = (props: MultiSelectProps) => {
  const {
    label,
    id,
    name,
    dataTestId,
    errorMessage,
    leftContent,
    rightContent,
    options,
    value = [],
    onChange,
    placeholder = 'Select options',
    isLoading,
    horizontal,
    required,
    className,
    renderOption,
    renderTag,
    showSearch,
    searchPlaceholder,
  } = props

  const [selectedValues, setSelectedValues] = useState<(string | number)[]>(value)
  const [dropdownPosition, setDropdownPosition] = useState<CSSProperties>({})
  const [isDropdownOpen, setIsDropdownOpen] = useState(false)
  const [searchValue, setSearchValue] = useState('')
  const triggerRef = useRef<HTMLDivElement>(null)
  const reactId = useId()
  const safeReactId = reactId.replace(/:/g, '')
  const qaBaseId = id || name || dataTestId || `multi-select-${safeReactId}`

  // Initialize width and position immediately before paint to prevent flash
  // This works for both horizontal and normal layouts
  useLayoutEffect(() => {
    const updatePosition = () => {
      if (triggerRef.current) {
        const triggerRect = triggerRef.current.getBoundingClientRect()
        const viewportWidth = window.innerWidth

        // Set both width and left position to prevent shift
        const { width } = triggerRect
        const { left } = triggerRect
        const maxLeft = viewportWidth - width
        const adjustedLeft = Math.max(0, Math.min(left, maxLeft))

        setDropdownPosition((prev) => ({
          ...prev,
          width: `${width}px`,
          left: `${adjustedLeft}px`,
        }))
      }
    }

    // Initial calculation
    updatePosition()

    // Watch for resize changes (important for horizontal layout responsiveness)
    const resizeObserver = new ResizeObserver(() => {
      updatePosition()
    })

    if (triggerRef.current) {
      resizeObserver.observe(triggerRef.current)
    }

    return () => {
      resizeObserver.disconnect()
    }
  }, [horizontal]) // Recalculate when horizontal prop changes

  useEffect(() => {
    setSelectedValues(value)
  }, [value])

  const calculateDropdownPosition = useCallback(() => {
    if (!triggerRef.current) return

    const triggerRect = triggerRef.current.getBoundingClientRect()
    const viewportHeight = window.innerHeight
    const viewportWidth = window.innerWidth
    const estimatedDropdownHeight = 200 // Approximate height of dropdown
    const spaceBelow = viewportHeight - triggerRect.bottom
    const spaceAbove = triggerRect.top

    // Calculate if we should open upward
    const openUpward = spaceBelow < estimatedDropdownHeight && spaceAbove > spaceBelow

    // Calculate position
    const top = openUpward ? triggerRect.top - estimatedDropdownHeight : triggerRect.bottom
    const { left } = triggerRect
    const { width } = triggerRect

    // Ensure dropdown doesn't go off screen horizontally
    const maxLeft = viewportWidth - width
    const adjustedLeft = Math.max(0, Math.min(left, maxLeft))

    // Update position, preserving existing values if they exist to prevent flash
    setDropdownPosition((prev) => ({
      width: `${width}px`,
      left: `${adjustedLeft}px`,
      top: `${Math.max(0, top)}px`,
    }))
  }, [])

  const handleDropdownOpen = () => {
    // Calculate position synchronously before setting state
    if (triggerRef.current) {
      const triggerRect = triggerRef.current.getBoundingClientRect()
      const viewportHeight = window.innerHeight
      const viewportWidth = window.innerWidth
      const estimatedDropdownHeight = 200
      const spaceBelow = viewportHeight - triggerRect.bottom
      const spaceAbove = triggerRect.top

      const openUpward = spaceBelow < estimatedDropdownHeight && spaceAbove > spaceBelow

      const top = openUpward ? triggerRect.top - estimatedDropdownHeight : triggerRect.bottom
      const { left } = triggerRect
      const { width } = triggerRect

      const maxLeft = viewportWidth - width
      const adjustedLeft = Math.max(0, Math.min(left, maxLeft))

      // Set position synchronously before opening
      setDropdownPosition((prev) => ({
        width: `${width}px`,
        left: `${adjustedLeft}px`,
        top: `${Math.max(0, top)}px`,
      }))
    }
    setIsDropdownOpen(true)
  }

  // Use useLayoutEffect to recalculate position if needed when dropdown opens
  useLayoutEffect(() => {
    if (isDropdownOpen && triggerRef.current) {
      calculateDropdownPosition()
    }
  }, [isDropdownOpen, calculateDropdownPosition])

  const handleDropdownClose = () => {
    // Delay to allow clicks inside dropdown to register
    setTimeout(() => {
      setIsDropdownOpen(false)
    }, 150)
  }

  const handleTriggerClick = () => {
    handleDropdownOpen()
  }

  const handleTriggerFocus = () => {
    handleDropdownOpen()
  }

  const handleTriggerBlur = (e: FocusEvent<HTMLDivElement>) => {
    // Don't close if focus is moving to dropdown content
    const relatedTarget = e.relatedTarget as HTMLElement
    if (relatedTarget?.closest('.dropdown-content')) {
      return
    }
    handleDropdownClose()
  }

  useEffect(() => {
    if (!isDropdownOpen) {
      return undefined
    }

    const handleResize = () => {
      calculateDropdownPosition()
    }

    const handleScroll = () => {
      calculateDropdownPosition()
    }

    window.addEventListener('resize', handleResize)
    window.addEventListener('scroll', handleScroll, true)

    return () => {
      window.removeEventListener('resize', handleResize)
      window.removeEventListener('scroll', handleScroll, true)
    }
  }, [isDropdownOpen, calculateDropdownPosition])

  const handleToggle = (optionValue: string | number) => {
    const newValues = selectedValues.includes(optionValue)
      ? selectedValues.filter((v) => v !== optionValue)
      : [...selectedValues, optionValue]

    setSelectedValues(newValues)
    onChange?.(newValues)
  }

  const handleRemove = (optionValue: string | number, e: MouseEvent) => {
    e.stopPropagation()
    const newValues = selectedValues.filter((v) => v !== optionValue)
    setSelectedValues(newValues)
    onChange?.(newValues)
  }

  const getSelectedOptions = () =>
    selectedValues
      .map((val) => {
        const option = options.find((opt) => opt.value === val)
        return option || null
      })
      .filter(Boolean) as OptionProps[]

  const filteredOptions = (() => {
    if (!showSearch || !searchValue) {
      return options
    }
    const term = searchValue.toLowerCase()
    return options.filter((option) => String(option.label || '').toLowerCase().includes(term))
  })()

  return (
    <div
      className={classNames(
        'w-full gap-2 text-sm',
        horizontal ? 'flex flex-col md:flex-row md:items-center' : 'flex flex-col',
        className,
      )}
    >
      {label && (
        <label
          htmlFor={id}
          className={classNames('label capitalize text-secondary flex justify-start gap-1', horizontal && 'md:w-[30%]')}
        >
          {label}
          {required && <span className="text-red-700">*</span>}
        </label>
      )}

      <div className={classNames(horizontal && 'md:w-[70%]', 'flex flex-col')}>
        <DropDown
          width="w-full"
          onHover={false}
          useFixedPosition
          fixedPositionStyle={dropdownPosition}
          trigger={
            <div
              ref={triggerRef}
              role="button"
              tabIndex={0}
              className={classNames(
                baseClass,
                errorMessage && 'input-error',
                'cursor-pointer flex-wrap',
                selectedValues.length > 0 ? 'py-2' : '',
              )}
              onClick={handleTriggerClick}
              onFocus={handleTriggerFocus}
              onBlur={handleTriggerBlur}
              onKeyDown={(e) => {
                if (e.key === 'Enter' || e.key === ' ') {
                  e.preventDefault()
                  handleTriggerClick()
                }
              }}
            >
              {leftContent && <span className="pl-2 text-placeholderColor">{leftContent}</span>}

              {isLoading && (
                <div className="lds-ring--input px-4">
                  <div />
                  <div />
                  <div />
                  <div />
                </div>
              )}

              <div className="flex flex-wrap gap-1 flex-1 items-center min-h-[1.5rem]">
                {selectedValues.length > 0 ? (
                  <>
                    {getSelectedOptions().map((option) => (
                      <span
                        key={option.value as string}
                        className="inline-flex items-center gap-1 px-2 py-1 bg-blue-100 text-blue-800 rounded-md text-sm font-medium"
                      >
                        {renderTag ? renderTag(option) : option.label}
                        <button
                          type="button"
                          onClick={(e) => handleRemove(option.value, e)}
                          className="hover:bg-blue-200 rounded-full p-0.5 transition-colors ml-1"
                          aria-label={`Remove ${option.label}`}
                        >
                          <BiX size={14} />
                        </button>
                      </span>
                    ))}
                  </>
                ) : (
                  <span className="text-gray-500 text-sm">{placeholder}</span>
                )}
              </div>

              {rightContent && <span className="pr-2">{rightContent}</span>}
            </div>
          }
          content={
            <div className="max-h-60 flex flex-col">
              {showSearch && (
                <div className="p-2 border-b border-gray-200">
                  <input
                    type="text"
                    className="input input-bordered input-sm w-full"
                    placeholder={searchPlaceholder || 'Search'}
                    value={searchValue}
                    onChange={(e) => setSearchValue(e.target.value)}
                  />
                </div>
              )}
              <div className="flex-1 overflow-y-auto">
                {filteredOptions.length > 0 ? (
                  <ul className="menu menu-compact w-full">
                    {filteredOptions.map((option) => {
                      const isSelected = selectedValues.includes(option.value)
                      const optionId = `${qaBaseId}-option-${String(option.value)}`
                      return (
                        <li key={option.value as string}>
                          <label
                            className="cursor-pointer flex items-center gap-2 p-2 hover:bg-gray-100 rounded"
                            htmlFor={optionId}
                          >
                            <input
                              id={optionId}
                              type="checkbox"
                              checked={isSelected}
                              onChange={() => handleToggle(option.value)}
                              className="checkbox checkbox-sm"
                            />
                            {renderOption ? renderOption(option) : <span className="capitalize">{option.label}</span>}
                          </label>
                        </li>
                      )
                    })}
                  </ul>
                ) : (
                  <div className="p-4 text-center text-gray-500 text-sm">No options available</div>
                )}
              </div>
            </div>
          }
        />

        {errorMessage && (
          <label htmlFor={id} className="text-red-500 text-sm">
            {errorMessage}
          </label>
        )}

        {/* Hidden input for form compatibility */}
        <input type="hidden" id={id} name={name} data-testid={dataTestId || `${name}-multi-select-${id}`} value={JSON.stringify(selectedValues)} />
      </div>
    </div>
  )
}
