import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { HelperText, Label } from 'flowbite-react'
import { toast } from 'react-toastify'

import formTheme from '@/lib/themes/form'
import { getMerchant, getMerchants } from '@/lib/services/merchant-services'
import { Merchant } from '@/app/(dashboard)/merchants/types'
import { paginationDefaultData } from '@/lib/data/pagination'
import * as PaginationConstant from '@/lib/constants/pagination'
import { cn } from '@/lib/utils/tw-merge'
import InputWithClearButton from './InputWithClearButton'
import Combobox from '../Combobox'

interface MerchantSelectorProps {
  value?: number | string
  label?: string
  required?: boolean
  id?: string
  errorMessage?: string
  fullWidth?: boolean
  onChange?: (merchant: Merchant | null) => void
}

export default function MerchantSelector({
  value,
  label = 'Merchant',
  required = false,
  id = 'merchant_id',
  errorMessage,
  fullWidth = false,
  onChange,
}: MerchantSelectorProps) {
  const [merchants, setMerchants] = useState<Pagination<Merchant>>(
    paginationDefaultData,
  )
  const [selectedMerchant, setSelectedMerchant] = useState<Merchant | null>(
    null,
  )
  const [isLoadingInitial, setIsLoadingInitial] = useState(true)
  const [isLoadingMore, setIsLoadingMore] = useState(false)
  const [showDropdown, setShowDropdown] = useState(false)
  const [searchValue, setSearchValue] = useState('')
  const [debouncedSearch, setDebouncedSearch] = useState('')

  const searchInputRef = useRef<HTMLInputElement>(null)
  const containerRef = useRef<HTMLDivElement>(null)

  const hasMore = useMemo(
    () =>
      !!merchants.next_page_url && merchants.current_page < merchants.last_page,
    [merchants],
  )

  // Debounce search input for API calls
  useEffect(() => {
    const timer = setTimeout(() => {
      setDebouncedSearch(searchValue)
    }, 300)

    return () => clearTimeout(timer)
  }, [searchValue])

  // Fetch merchants (initial + on search change)
  useEffect(() => {
    async function fetchMerchants() {
      setIsLoadingInitial(true)

      try {
        const data = (await getMerchants({
          page: 1,
          limit: PaginationConstant.MIN_DATA_PER_PAGE,
          search: debouncedSearch,
        })) as Pagination<Merchant>
        setMerchants(data)
      } catch {
        toast.error('Error fetching merchants. Please try again.')
      } finally {
        setIsLoadingInitial(false)
      }
    }

    fetchMerchants()
  }, [debouncedSearch])

  // Load more pages for infinite scroll
  const loadMore = useCallback(async () => {
    if (isLoadingMore || !hasMore) return

    setIsLoadingMore(true)

    try {
      const updatedMerchants = (await getMerchants({
        page: merchants.current_page + 1,
        limit: PaginationConstant.MIN_DATA_PER_PAGE,
        search: debouncedSearch,
      })) as Pagination<Merchant>

      setMerchants((prev) => ({
        ...updatedMerchants,
        data: [...prev.data, ...updatedMerchants.data],
      }))
    } finally {
      setIsLoadingMore(false)
    }
  }, [merchants, hasMore, isLoadingMore, debouncedSearch])

  // Auto-select merchant when value is provided
  useEffect(() => {
    if (!value) {
      setSelectedMerchant(null)
      return
    }

    // Check if already selected
    if (selectedMerchant && String(selectedMerchant.id) === String(value)) {
      return
    }

    // Check if merchant exists in loaded list
    const matched = merchants.data.find(
      (merchant) => String(merchant.id) === String(value),
    )
    if (matched) {
      setSelectedMerchant(matched)
      return
    }

    // Fetch the specific merchant by ID if not in loaded list
    if (!isLoadingInitial) {
      getMerchant(Number(value)).then(setSelectedMerchant)
    }
  }, [value, merchants.data, isLoadingInitial, selectedMerchant])

  // Close dropdown on outside click
  useEffect(() => {
    function handleClickOutside(event: MouseEvent) {
      if (
        containerRef.current &&
        !containerRef.current.contains(event.target as Node)
      ) {
        setShowDropdown(false)
        setSearchValue('')
      }
    }

    document.addEventListener('mousedown', handleClickOutside)
    return () => document.removeEventListener('mousedown', handleClickOutside)
  }, [])

  function handleSelect(merchant: Merchant) {
    setSelectedMerchant(merchant)
    onChange?.(merchant)
    setShowDropdown(false)
    setSearchValue('')
  }

  function handleSearch(e: React.ChangeEvent<HTMLInputElement>) {
    setSearchValue(e.target.value)
  }

  function handleInputClick() {
    setShowDropdown(true)

    setTimeout(() => {
      searchInputRef.current?.focus()
    }, 100)
  }

  function handleClear() {
    setSelectedMerchant(null)
    onChange?.(null)
    setSearchValue('')
  }

  return (
    <div className="w-full">
      <div className="mb-2 block">
        <Label theme={formTheme.label} htmlFor={id}>
          {label} {required && <span className="text-red-500">*</span>}
        </Label>
      </div>

      <div className="relative" ref={containerRef}>
        <InputWithClearButton
          id={id}
          className={`w-full ${fullWidth ? '' : 'max-w-96'} cursor-pointer`}
          placeholder="Select a merchant..."
          value={selectedMerchant?.name || ''}
          onClick={handleInputClick}
          onClear={handleClear}
          readOnly
          showClearButton={!!selectedMerchant}
        />

        <Combobox
          showDropdown={showDropdown}
          searchValue={searchValue}
          placeholderText="Search merchants..."
          inputRef={searchInputRef as React.RefObject<HTMLInputElement>}
          isLoading={isLoadingInitial}
          filteredOptions={merchants.data}
          selectedOption={selectedMerchant as Merchant}
          options={merchants.data}
          handleSelectOption={handleSelect}
          handleSearch={handleSearch}
          hasMore={hasMore}
          isLoadingMore={isLoadingMore}
          onLoadMore={loadMore}
          renderLoadingMore={() => <span>Loading more merchants...</span>}
          renderOption={(merchant) => (
            <div
              key={merchant.id}
              className={cn(
                'p-3 hover:bg-gray-50 cursor-pointer border-b border-gray-100 last:border-b-0 flex items-center gap-3',
                {
                  'bg-gray-50': merchant.id === selectedMerchant?.id,
                },
              )}
              onClick={() => handleSelect(merchant)}
            >
              {merchant.id === selectedMerchant?.id ? (
                <div className="h-2 w-2 rounded-full bg-blue-500"></div>
              ) : null}
              <div>
                <div className="font-medium text-gray-900">{merchant.name}</div>
                {merchant.code && (
                  <div className="text-xs text-gray-400">{merchant.code}</div>
                )}
              </div>
            </div>
          )}
          renderNoResultsFound={() => (
            <div className="p-3 text-center text-gray-500">
              No merchants found
            </div>
          )}
          renderSkeletonComponent={() =>
            Array.from({ length: 5 }).map((_, index) => (
              <div
                key={index}
                className="p-3 border-b border-gray-100 last:border-b-0 animate-pulse"
              >
                <div className="h-4 bg-gray-200 rounded w-3/4 mb-2"></div>
                <div className="h-3 bg-gray-200 rounded w-1/2"></div>
              </div>
            ))
          }
        />
      </div>

      {errorMessage && <HelperText color="failure">{errorMessage}</HelperText>}
    </div>
  )
}
