import {
  memo,
  type ChangeEvent,
  type KeyboardEvent,
  type MouseEvent,
  type PointerEvent,
  useCallback,
  useEffect,
  useMemo,
  useState,
} from 'react'
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
import * as Popover from '@radix-ui/react-popover'
import { Building2, ChevronDown, Search, ShieldCheck, X } from 'lucide-react'
import { useQuery } from '@apollo/client/react'

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

import './EmployeesToolbar.scss'

export type EmployeesSortOption = {
  value: string
  label: string
  sortField: 'ID' | 'Name' | 'Date'
  sortDirection: 'ASC' | 'DESC'
}

export const usersSortOptions: EmployeesSortOption[] = [
  {
    value: 'date-desc',
    label: 'Сначала новые',
    sortField: 'Date',
    sortDirection: 'DESC',
  },
  {
    value: 'id-asc',
    label: 'ID по возрастанию',
    sortField: 'ID',
    sortDirection: 'ASC',
  },
  {
    value: 'id-desc',
    label: 'ID по убыванию',
    sortField: 'ID',
    sortDirection: 'DESC',
  },
  {
    value: 'name-asc',
    label: 'ФИО А–Я',
    sortField: 'Name',
    sortDirection: 'ASC',
  },
  {
    value: 'name-desc',
    label: 'ФИО Я–А',
    sortField: 'Name',
    sortDirection: 'DESC',
  },
  {
    value: 'date-asc',
    label: 'Сначала старые',
    sortField: 'Date',
    sortDirection: 'ASC',
  },
]

export const organisationsSortOptions: EmployeesSortOption[] = [
  {
    value: 'date-desc',
    label: 'Сначала новые',
    sortField: 'Date',
    sortDirection: 'DESC',
  },
  {
    value: 'id-asc',
    label: 'ID по возрастанию',
    sortField: 'ID',
    sortDirection: 'ASC',
  },
  {
    value: 'id-desc',
    label: 'ID по убыванию',
    sortField: 'ID',
    sortDirection: 'DESC',
  },
  {
    value: 'name-asc',
    label: 'Название А–Я',
    sortField: 'Name',
    sortDirection: 'ASC',
  },
  {
    value: 'name-desc',
    label: 'Название Я–А',
    sortField: 'Name',
    sortDirection: 'DESC',
  },
  {
    value: 'date-asc',
    label: 'Сначала старые',
    sortField: 'Date',
    sortDirection: 'ASC',
  },
]

export type EmployeesRoleFilterOption = {
  value: 'Admin' | 'User'
  label: string
}

const roleFilterOptions: EmployeesRoleFilterOption[] = [
  {
    value: 'Admin',
    label: 'Администратор',
  },
  {
    value: 'User',
    label: 'Пользователь',
  },
]

type EmployeesToolbarProps = {
  addButtonText: string
  onAdd: () => void

  searchValue?: string
  onSearchChange?: (value: string) => void

  sortOptions: EmployeesSortOption[]
  selectedSort: EmployeesSortOption
  onSortChange: (option: EmployeesSortOption) => void

  showSort?: boolean

  roleFilter?: EmployeeRole | null
  onRoleFilterChange?: (role: EmployeeRole | null) => void

  organisationFilterId?: string | null
  onOrganisationFilterChange?: (
    organisation: { id: string; name: string } | null,
  ) => void

  showUserFilters?: boolean
}

export const EmployeesToolbar = memo(function EmployeesToolbar({
  addButtonText,
  onAdd,
  searchValue = '',
  onSearchChange,
  sortOptions,
  selectedSort,
  onSortChange,
  showSort = true,
  roleFilter = null,
  onRoleFilterChange,
  organisationFilterId = null,
  onOrganisationFilterChange,
  showUserFilters = false,
}: EmployeesToolbarProps) {
  const [isOrganisationOpen, setIsOrganisationOpen] = useState(false)
  const [organisationSearch, setOrganisationSearch] = useState('')
  const [debouncedOrganisationSearch, setDebouncedOrganisationSearch] =
    useState('')

  useEffect(() => {
    const timeoutId = window.setTimeout(() => {
      setDebouncedOrganisationSearch(organisationSearch.trim())
    }, 300)

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

  const selectedRoleOption = useMemo(
    () =>
      roleFilterOptions.find((option) => option.value === roleFilter) ?? null,
    [roleFilter],
  )

  const selectedOrganisationVariables = useMemo<GetOrganisationVariables>(
    () => ({
      id: organisationFilterId ?? '',
    }),
    [organisationFilterId],
  )

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

  const {
    data: selectedOrganisationData,
  } = useQuery<GetOrganisationData, GetOrganisationVariables>(
    GET_ORGANISATION_QUERY,
    {
      variables: selectedOrganisationVariables,
      skip: !showUserFilters || !organisationFilterId,
      fetchPolicy: 'cache-first',
    },
  )

  const {
    data: organisationsData,
    previousData: previousOrganisationsData,
    loading: organisationsLoading,
    error: organisationsError,
  } = useQuery<GetOrganisationsData, GetOrganisationsVariables>(
    GET_ORGANISATIONS_QUERY,
    {
      variables: organisationsVariables,
      skip: !showUserFilters || !isOrganisationOpen,
      fetchPolicy: 'network-only',
      notifyOnNetworkStatusChange: true,
    },
  )

  const organisationsResponse =
    organisationsData?.getOrganisations ??
    previousOrganisationsData?.getOrganisations

  const organisations = useMemo(
    () => organisationsResponse?.page ?? [],
    [organisationsResponse?.page],
  )
  const selectedOrganisationName =
    selectedOrganisationData?.getOrganisation?.name ??
    (organisationFilterId ? `Организация #${organisationFilterId}` : null)

  const organisationOptions = useMemo(() => {
    const map = new Map<string, { id: string; name: string }>()

    if (organisationFilterId && selectedOrganisationName) {
      map.set(organisationFilterId, {
        id: organisationFilterId,
        name: selectedOrganisationName,
      })
    }

    organisations.forEach((organisation) => {
      map.set(String(organisation.id), {
        id: String(organisation.id),
        name: organisation.name,
      })
    })

    return Array.from(map.values())
  }, [organisationFilterId, selectedOrganisationName, organisations])

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

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

  const handleResetClick = useCallback((
    event: MouseEvent<HTMLSpanElement>,
  ) => {
    event.preventDefault()
    event.stopPropagation()
  }, [])

  const handleRoleResetPointerDown = useCallback((
    event: PointerEvent<HTMLSpanElement>,
  ) => {
    event.preventDefault()
    event.stopPropagation()
    onRoleFilterChange?.(null)
  }, [onRoleFilterChange])

  const handleOrganisationResetPointerDown = useCallback((
    event: PointerEvent<HTMLSpanElement>,
  ) => {
    event.preventDefault()
    event.stopPropagation()
    onOrganisationFilterChange?.(null)
  }, [onOrganisationFilterChange])

  const handleRoleResetKeyDown = useCallback((
    event: KeyboardEvent<HTMLSpanElement>,
  ) => {
    if (event.key !== 'Enter' && event.key !== ' ') return

    event.preventDefault()
    event.stopPropagation()
    onRoleFilterChange?.(null)
  }, [onRoleFilterChange])

  const handleOrganisationResetKeyDown = useCallback((
    event: KeyboardEvent<HTMLSpanElement>,
  ) => {
    if (event.key !== 'Enter' && event.key !== ' ') return

    event.preventDefault()
    event.stopPropagation()
    onOrganisationFilterChange?.(null)
  }, [onOrganisationFilterChange])

  const handleOrganisationSelect = useCallback((
    organisation: { id: string; name: string },
  ) => {
    onOrganisationFilterChange?.(organisation)
    setIsOrganisationOpen(false)
    setOrganisationSearch('')
    setDebouncedOrganisationSearch('')
  }, [onOrganisationFilterChange])

  return (
    <div className="devices-toolbar">
      <label className="devices-search">
        <Search size={16} />

        <input
          type="text"
          placeholder="Поиск"
          value={searchValue}
          onChange={handleSearchChange}
        />
      </label>

      <div className="devices-toolbar__right">
        {showUserFilters && (
          <>
            <DropdownMenu.Root>
              <DropdownMenu.Trigger asChild>
                <button
                  className={`employees-filter-button ${
                    selectedRoleOption ? 'is-active' : ''
                  }`}
                  type="button"
                >
                  <ShieldCheck size={17} />
                  <span>{selectedRoleOption?.label ?? 'Роль'}</span>

                  {selectedRoleOption ? (
                    <span
                      className="employees-filter-button__reset"
                      role="button"
                      tabIndex={0}
                      onPointerDown={handleRoleResetPointerDown}
                      onClick={handleResetClick}
                      onKeyDown={handleRoleResetKeyDown}
                      aria-label="Сбросить фильтр роли"
                    >
                      <X size={14} />
                    </span>
                  ) : (
                    <ChevronDown size={16} />
                  )}
                </button>
              </DropdownMenu.Trigger>

              <DropdownMenu.Portal>
                <DropdownMenu.Content
                  className="employees-sort-menu"
                  align="end"
                  sideOffset={8}
                >
                  {roleFilterOptions.map((option) => (
                    <DropdownMenu.Item
                      key={option.value}
                      className="employees-sort-menu__item"
                      onSelect={() => onRoleFilterChange?.(option.value)}
                    >
                      <span>{option.label}</span>

                      {roleFilter === option.value && (
                        <span className="employees-sort-menu__check" />
                      )}
                    </DropdownMenu.Item>
                  ))}
                </DropdownMenu.Content>
              </DropdownMenu.Portal>
            </DropdownMenu.Root>

            <Popover.Root
              open={isOrganisationOpen}
              onOpenChange={setIsOrganisationOpen}
            >
              <Popover.Trigger asChild>
                <button
                  className={`employees-filter-button employees-filter-button--organisation ${
                    organisationFilterId ? 'is-active' : ''
                  }`}
                  type="button"
                >
                  <Building2 size={17} />
                  <span>{selectedOrganisationName ?? 'Организация'}</span>

                  {organisationFilterId ? (
                    <span
                      className="employees-filter-button__reset"
                      role="button"
                      tabIndex={0}
                      onPointerDown={handleOrganisationResetPointerDown}
                      onClick={handleResetClick}
                      onKeyDown={handleOrganisationResetKeyDown}
                      aria-label="Сбросить фильтр организации"
                    >
                      <X size={14} />
                    </span>
                  ) : (
                    <ChevronDown size={16} />
                  )}
                </button>
              </Popover.Trigger>

              <Popover.Portal>
                <Popover.Content
                  className="employees-organisation-filter"
                  align="end"
                  sideOffset={8}
                  collisionPadding={12}
                >
                  <label className="employees-organisation-filter__search">
                    <Search size={16} />

                    <input
                      value={organisationSearch}
                      onChange={handleOrganisationSearchChange}
                      placeholder="Название организации"
                      autoFocus
                    />
                  </label>

                  <div className="employees-organisation-filter__list">
                    {organisationsLoading && organisationOptions.length === 0 && (
                      <div className="employees-organisation-filter__state">
                        Загрузка организаций...
                      </div>
                    )}

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

                    {!organisationsLoading &&
                      !organisationsError &&
                      organisationOptions.length === 0 && (
                        <div className="employees-organisation-filter__state">
                          Организации не найдены
                        </div>
                      )}

                    {organisationOptions.map((organisation) => {
                      const isSelected =
                        organisation.id === organisationFilterId

                      return (
                        <button
                          className={`employees-organisation-filter__option ${
                            isSelected ? 'is-selected' : ''
                          }`}
                          type="button"
                          key={organisation.id}
                          onClick={() => handleOrganisationSelect(organisation)}
                        >
                          <span>{organisation.name}</span>
                          <small>ID: {organisation.id}</small>
                        </button>
                      )
                    })}
                  </div>
                </Popover.Content>
              </Popover.Portal>
            </Popover.Root>
          </>
        )}

        {showSort && (
          <DropdownMenu.Root>
            <DropdownMenu.Trigger asChild>
              <button className="employees-sort" type="button">
                {selectedSort.label}

                <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
                  <path fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5" d="M11 10h7m-7 4h5m-5 4h3M11 6h10M7 18.813C6.607 19.255 5.56 21 5 21m-2-2.187C3.393 19.255 4.44 21 5 21m0 0v-6M3 5.188C3.393 4.745 4.44 3 5 3m2 2.188C6.607 4.745 5.56 3 5 3m0 0v6" />
                </svg>
              </button>
            </DropdownMenu.Trigger>

            <DropdownMenu.Portal>
              <DropdownMenu.Content
                className="employees-sort-menu"
                align="end"
                sideOffset={8}
              >
                {sortOptions.map((option) => (
                  <DropdownMenu.Item
                    key={option.value}
                    className="employees-sort-menu__item"
                    onSelect={() => onSortChange?.(option)}
                  >
                    <span>{option.label}</span>

                    {selectedSort.value === option.value && (
                      <span className="employees-sort-menu__check" />
                    )}
                  </DropdownMenu.Item>
                ))}
              </DropdownMenu.Content>
            </DropdownMenu.Portal>
          </DropdownMenu.Root>
        )}
        <button
          className="add-device add-employees"
          type="button"
          onClick={onAdd}
        >
          {addButtonText === 'Добавить организацию' ? (
            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
              <defs>
                <mask id="SVGRejWFe9v">
                  <g fill="none" stroke="#fff" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2">
                    <path strokeDasharray="64" d="M9 7h11c0.55 0 1 0.45 1 1v11c0 0.55 -0.45 1 -1 1h-16c-0.55 0 -1 -0.45 -1 -1v-11c0 -0.55 0.45 -1 1 -1Z">
                      <animate fill="freeze" attributeName="stroke-dashoffset" dur="0.6s" values="64;0" />
                    </path>
                    <path strokeDasharray="16" strokeDashoffset="16" d="M9 7v-3c0 -0.55 0.45 -1 1 -1h4c0.55 0 1 0.45 1 1v3">
                      <animate fill="freeze" attributeName="stroke-dashoffset" begin="0.6s" dur="0.3s" to="0" />
                    </path>
                  </g>
                  <path d="M19 13c3.31 0 6 2.69 6 6c0 3.31 -2.69 6 -6 6c-3.31 0 -6 -2.69 -6 -6c0 -3.31 2.69 -6 6 -6Z" opacity="0">
                    <set fill="freeze" attributeName="opacity" begin="1s" to="1" />
                  </path>
                </mask>
              </defs>
              <path fill="currentColor" d="M0 0h24v24H0z" mask="url(#SVGRejWFe9v)" />
              <g fill="none" stroke="currentColor" strokeDasharray="8" strokeDashoffset="8" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2">
                <path d="M16 19h6">
                  <animate fill="freeze" attributeName="stroke-dashoffset" begin="1s" dur="0.2s" to="0" />
                </path>
                <path d="M19 16v6">
                  <animate fill="freeze" attributeName="stroke-dashoffset" begin="1.2s" dur="0.2s" to="0" />
                </path>
              </g>
            </svg>
          ) : (
            <svg
              width="16"
              height="16"
              viewBox="0 0 16 16"
              fill="none"
              xmlns="http://www.w3.org/2000/svg"
            >
              <path
                d="M4.00004 4.66683C4.00004 3.78277 4.35123 2.93493 4.97635 2.30981C5.60147 1.68469 6.44932 1.3335 7.33337 1.3335C8.21743 1.3335 9.06527 1.68469 9.6904 2.30981C10.3155 2.93493 10.6667 3.78277 10.6667 4.66683C10.6667 5.55088 10.3155 6.39873 9.6904 7.02385C9.06527 7.64897 8.21743 8.00016 7.33337 8.00016C6.44932 8.00016 5.60147 7.64897 4.97635 7.02385C4.35123 6.39873 4.00004 5.55088 4.00004 4.66683ZM3.21471 9.7815C4.28337 9.1295 5.73671 8.66683 7.33337 8.66683C7.6316 8.66683 7.92404 8.68238 8.21071 8.7135C8.32522 8.72578 8.4346 8.76752 8.52818 8.83465C8.62177 8.90178 8.69636 8.99201 8.7447 9.09654C8.79304 9.20108 8.81348 9.31635 8.80401 9.43114C8.79455 9.54592 8.75551 9.65629 8.69071 9.7515C8.23917 10.4143 7.99844 11.1981 8.00004 12.0002C8.00004 12.6135 8.13804 13.1935 8.38337 13.7115C8.43118 13.8124 8.45286 13.9237 8.44645 14.0352C8.44004 14.1467 8.40572 14.2547 8.34666 14.3495C8.2876 14.4443 8.20568 14.5227 8.10843 14.5775C8.01117 14.6324 7.90169 14.662 7.79004 14.6635L7.33337 14.6668C5.84737 14.6668 4.44337 14.5735 3.39137 14.2948C2.86804 14.1562 2.37537 13.9575 2.00204 13.6575C1.60671 13.3402 1.33337 12.8968 1.33337 12.3335C1.33337 11.8088 1.57204 11.3182 1.89604 10.9075C2.22537 10.4908 2.68071 10.1075 3.21471 9.78083V9.7815ZM12 9.3335C12.1769 9.3335 12.3464 9.40373 12.4714 9.52876C12.5965 9.65378 12.6667 9.82335 12.6667 10.0002V11.3335H14C14.1769 11.3335 14.3464 11.4037 14.4714 11.5288C14.5965 11.6538 14.6667 11.8234 14.6667 12.0002C14.6667 12.177 14.5965 12.3465 14.4714 12.4716C14.3464 12.5966 14.1769 12.6668 14 12.6668H12.6667V14.0002C12.6667 14.177 12.5965 14.3465 12.4714 14.4716C12.3464 14.5966 12.1769 14.6668 12 14.6668C11.8232 14.6668 11.6537 14.5966 11.5286 14.4716C11.4036 14.3465 11.3334 14.177 11.3334 14.0002V12.6668H10C9.82323 12.6668 9.65366 12.5966 9.52864 12.4716C9.40361 12.3465 9.33337 12.177 9.33337 12.0002C9.33337 11.8234 9.40361 11.6538 9.52864 11.5288C9.65366 11.4037 9.82323 11.3335 10 11.3335H11.3334V10.0002C11.3334 9.82335 11.4036 9.65378 11.5286 9.52876C11.6537 9.40373 11.8232 9.3335 12 9.3335Z"
                fill="white"
              />
            </svg>
          )}

          {addButtonText}
        </button>
      </div>
    </div>
  )
})
