import {
    memo,
    type ChangeEvent,
    useCallback,
    useEffect,
    useMemo,
    useRef,
    useState,
} from 'react'
import { useQuery } from '@apollo/client/react'
import { ChevronDown, Search } from 'lucide-react'
import { AnimatePresence, m } from 'framer-motion'

import { GET_ORGANISATIONS_QUERY } from '../../../../entities/employee/api/employee.graphql'
import type {
    GetOrganisationsData,
    GetOrganisationsVariables,
} from '../../../../entities/employee/model/types'

import './OrganisationSelect.scss'
import { popoverVariants, uiFadeTransition } from '../../../../shared/lib/motion'

type OrganisationSelectProps = {
    value: string
    onChange: (value: string) => void
    enabled: boolean
    disabled?: boolean
    selectedLabel?: string | null
}

export const OrganisationSelect = memo(function OrganisationSelect({
    value,
    onChange,
    enabled,
    disabled = false,
    selectedLabel = null,
}: OrganisationSelectProps) {
    const rootRef = useRef<HTMLDivElement | null>(null)

    const [isOpen, setIsOpen] = useState(false)
    const [search, setSearch] = useState('')
    const [debouncedSearch, setDebouncedSearch] = useState('')

    const [selectedName, setSelectedName] = useState<string | null>(selectedLabel)

    useEffect(() => {
        if (!value) {
            setSelectedName(null)
            return
        }

        if (selectedLabel) {
            setSelectedName(selectedLabel)
        }
    }, [selectedLabel, value])

    useEffect(() => {
        const timeoutId = window.setTimeout(() => {
            setDebouncedSearch(search.trim())
        }, 300)

        return () => {
            window.clearTimeout(timeoutId)
        }
    }, [search])

    useEffect(() => {
        if (!enabled) {
            setIsOpen(false)
            setSearch('')
        }
    }, [enabled])

    useEffect(() => {
        if (!isOpen) return

        function handleDocumentMouseDown(event: MouseEvent) {
            if (!rootRef.current?.contains(event.target as Node)) {
                setIsOpen(false)
            }
        }

        document.addEventListener('mousedown', handleDocumentMouseDown)

        return () => {
            document.removeEventListener('mousedown', handleDocumentMouseDown)
        }
    }, [isOpen])

    const queryVariables = useMemo<GetOrganisationsVariables>(() => ({
        page: 0,
        query: debouncedSearch || undefined,
        sortDirection: 'ASC',
        sortField: 'Name',
    }), [debouncedSearch])

    const {
        data,
        previousData,
        loading,
        error,
    } = useQuery<GetOrganisationsData, GetOrganisationsVariables>(
        GET_ORGANISATIONS_QUERY,
        {
            variables: queryVariables,
            skip: !enabled || !isOpen,
            fetchPolicy: 'network-only',
            notifyOnNetworkStatusChange: true,
        },
    )

    const organisationsResponse =
        data?.getOrganisations ?? previousData?.getOrganisations

    const organisations = useMemo(
        () => organisationsResponse?.page ?? [],
        [organisationsResponse?.page],
    )
    const totalElements = organisationsResponse?.totalElements ?? 0

    const selectedOrganisation = useMemo(
        () => organisations.find(
            (organisation) => String(organisation.id) === value,
        ),
        [organisations, value],
    )

    const buttonText =
        selectedOrganisation?.name ||
        selectedName ||
        selectedLabel ||
        'Выберите организацию'

    const handleToggleOpen = useCallback(() => {
        setIsOpen((prev) => !prev)
    }, [])

    const handleSearchChange = useCallback((
        event: ChangeEvent<HTMLInputElement>,
    ) => {
        setSearch(event.target.value)
    }, [])

    const handleSelect = useCallback((organisation: { id: number; name: string }) => {
        onChange(String(organisation.id))
        setSelectedName(organisation.name)
        setIsOpen(false)
        setSearch('')
    }, [onChange])

    return (
        <div
            className={`organisation-select ${isOpen ? 'is-open' : ''}`}
            ref={rootRef}
        >
            <button
                className="organisation-select__trigger"
                type="button"
                disabled={disabled}
                onClick={handleToggleOpen}
            >
                <span className={value ? '' : 'is-placeholder'}>{buttonText}</span>
                <ChevronDown size={18} />
            </button>

            <AnimatePresence initial={false}>
                {isOpen && (
                <m.div
                    className="organisation-select__dropdown"
                    variants={popoverVariants}
                    initial="initial"
                    animate="animate"
                    exit="exit"
                    transition={uiFadeTransition}
                >
                    <label className="organisation-select__search">
                        <Search size={16} />

                        <input
                            value={search}
                            onChange={handleSearchChange}
                            placeholder="Поиск организации"
                            autoFocus
                        />
                    </label>

                    <div className="organisation-select__list">
                        {loading && organisations.length === 0 && (
                            <div className="organisation-select__state">
                                Загрузка организаций...
                            </div>
                        )}

                        {error && (
                            <div className="organisation-select__state is-error">
                                Не удалось загрузить организации
                            </div>
                        )}

                        {!loading && !error && organisations.length === 0 && (
                            <div className="organisation-select__state">
                                Организации не найдены
                            </div>
                        )}

                        {organisations.map((organisation) => {
                            const organisationId = String(organisation.id)
                            const isSelected = organisationId === value

                            return (
                                <button
                                    className={`organisation-select__option ${isSelected ? 'is-selected' : ''
                                        }`}
                                    type="button"
                                    key={organisation.id}
                                    onClick={() => handleSelect(organisation)}
                                >
                                    <span>{organisation.name}</span>
                                    <small>ID: {organisation.id}</small>
                                </button>
                            )
                        })}
                    </div>

                    {totalElements > organisations.length && (
                        <div className="organisation-select__hint">
                            Показаны первые {organisations.length} из {totalElements}.
                            Используйте поиск, чтобы найти нужную организацию.
                        </div>
                    )}
                </m.div>
                )}
            </AnimatePresence>
        </div>
    )
})
