import {
    memo,
    type ChangeEvent,
    useCallback,
    useEffect,
    useMemo,
    useState,
} from 'react'
import * as Popover from '@radix-ui/react-popover'
import { useQuery } from '@apollo/client/react'
import {
    ChevronDown,
    Search,
    X,
} from 'lucide-react'

import { GET_ORGANISATIONS_QUERY } from '../../../../entities/employee/api/employee.graphql'

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

import './DevicesOrganisationFilter.scss'

type DevicesOrganisationFilterProps = {
    selectedOrganisationIds: string[]
    onChange: (ids: string[]) => void
}

type OrganisationFilterItem = {
    id: string
    name: string
}

const SORT_DIRECTION = 'ASC' as const
const SORT_FIELD = 'Name' as const

export const DevicesOrganisationFilter = memo(function DevicesOrganisationFilter({
    selectedOrganisationIds = [],
    onChange,
}: DevicesOrganisationFilterProps) {
    const [open, setOpen] = useState(false)

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

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

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

    const {
        data,
        previousData,
        loading,
        error,
    } = useQuery<
        GetOrganisationsData,
        GetOrganisationsVariables
    >(GET_ORGANISATIONS_QUERY, {
        variables: {
            page: 0,
            query: debouncedSearch || undefined,
            sortDirection: SORT_DIRECTION,
            sortField: SORT_FIELD,
        },
        skip: !open && selectedOrganisationIds.length === 0,
        fetchPolicy: 'cache-and-network',
        nextFetchPolicy: 'cache-first',
        notifyOnNetworkStatusChange: true,
    })

    const response =
        data?.getOrganisations ??
        previousData?.getOrganisations

    const organisations = useMemo(
        () => response?.page ?? [],
        [response?.page],
    )

    const selectedOrganisationIdSet = useMemo(
        () => new Set(selectedOrganisationIds),
        [selectedOrganisationIds],
    )

    const [
        selectedOrganisations,
        setSelectedOrganisations,
    ] = useState<
        OrganisationFilterItem[]
    >([])

    useEffect(() => {
        if (organisations.length === 0) return

        setSelectedOrganisations((prev) => {
            const map = new Map(
                prev.map((item) => [item.id, item]),
            )

            organisations.forEach((organisation) => {
                const id = String(organisation.id)

                if (
                    selectedOrganisationIdSet.has(id)
                ) {
                    map.set(id, {
                        id,
                        name: organisation.name,
                    })
                }
            })

            return Array.from(map.values()).filter(
                (item) =>
                    selectedOrganisationIdSet.has(item.id),
            )
        })
    }, [
        organisations,
        selectedOrganisationIdSet,
    ])

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

    const handleSelect = useCallback((
        organisation: OrganisationFilterItem,
    ) => {
        const alreadySelected =
            selectedOrganisationIdSet.has(organisation.id)

        if (alreadySelected) {
            onChange(
                selectedOrganisationIds.filter(
                    (item) =>
                        item !== organisation.id,
                ),
            )

            return
        }

        setSelectedOrganisations((prev) => {
            const exists = prev.some(
                (item) =>
                    item.id === organisation.id,
            )

            if (exists) {
                return prev
            }

            return [...prev, organisation]
        })

        onChange([
            ...selectedOrganisationIds,
            organisation.id,
        ])

        setSearch('')
        setDebouncedSearch('')
    }, [
        onChange,
        selectedOrganisationIds,
        selectedOrganisationIdSet,
    ])

    const handleRemove = useCallback((id: string) => {
        onChange(
            selectedOrganisationIds.filter(
                (item) => item !== id,
            ),
        )
    }, [onChange, selectedOrganisationIds])

    const mergedOrganisations = useMemo(
        () => [
            ...selectedOrganisations,

            ...organisations
                .filter(
                    (organisation) =>
                        !selectedOrganisationIdSet.has(
                            String(organisation.id),
                        ),
                )
                .map((organisation) => ({
                    id: String(organisation.id),
                    name: organisation.name,
                })),
        ],
        [
            organisations,
            selectedOrganisationIdSet,
            selectedOrganisations,
        ],
    )

    return (
        <div className="organisation-filter">
            {selectedOrganisations.length > 0 && (
                <div className="organisation-filter__tags">
                    {selectedOrganisations.map(
                        (organisation) => (
                            <div
                                className="organisation-filter__tag"
                                key={organisation.id}
                            >
                                <span>{organisation.name}</span>

                                <button
                                    type="button"
                                    onClick={() =>
                                        handleRemove(
                                            String(organisation.id),
                                        )
                                    }
                                >
                                    <X size={14} />
                                </button>
                            </div>
                        ),
                    )}
                </div>
            )}

            <Popover.Root
                open={open}
                onOpenChange={setOpen}
            >
                <Popover.Trigger asChild>
                    <button
                        className="organisation-select__trigger"
                        type="button"
                    >
                        <span className="is-placeholder">
                            Выберите организацию
                        </span>

                        <ChevronDown size={18} />
                    </button>
                </Popover.Trigger>

                <Popover.Portal>
                    <Popover.Content
                        className="organisation-select__dropdown"
                        sideOffset={8}
                        align="start"
                        collisionPadding={12}
                    >
                        <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>
                                )}

                               {mergedOrganisations.map((organisation) => {
                                const isSelected =
                                    selectedOrganisationIds.includes(
                                        organisation.id,
                                    )

                                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>
                    </Popover.Content>
                </Popover.Portal>
            </Popover.Root>
        </div>
    )
})
