import {
  memo,
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from 'react'
import { useNavigate, useSearchParams } from 'react-router-dom'
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
import { Link as LinkIcon, Link2Off, Pencil, Search, Trash2 } from 'lucide-react'
import SimpleBar from 'simplebar-react'
import 'simplebar-react/dist/simplebar.min.css'

import type {
  Machine,
  MachineSortDirection,
  MachineSortField,
} from '../../entities/machine/model/types'
import {
  machinesService,
  type MachinesPage as MachinesPageResponse,
  type MachinesQueryParams,
} from '../../entities/machine/api/machinesService'
import {
  formatMachineDateTime,
  getMachineStateClass,
  getMachineStateLabel,
} from '../../entities/machine/lib/machineFormatters'
import type { Employee } from '../../entities/employee/model/types'
import { authService } from '../../features/auth/api/authService'
import { EmployeesPagination } from '../EmployeesPage/components/EmployeesPagination/EmployeesPagination'
import { getPaginationItems } from '../EmployeesPage/lib/getPaginationItems'
import { ConfirmDangerDialog } from '../../widgets/ConfirmDangerDialog/ConfirmDangerDialog'
import {
  TableSkeleton,
  type TableSkeletonColumn,
} from '../../shared/ui/TableSkeleton/TableSkeleton'
import { RevealContent } from '../../shared/ui/RevealContent/RevealContent'
import { EmptyState } from '../../shared/ui/EmptyState/EmptyState'
import { AddMachineModal } from './components/AddMachineModal/AddMachineModal'
import { BeaconBindingModal } from './components/BeaconBindingModal/BeaconBindingModal'

import '../DevicesPage/DevicesPage.scss'
import '../DevicesPage/components/DevicesToolbar/DevicesToolbar.scss'
import './MachinesPage.scss'

type MachinesSortOption = {
  value: string
  label: string
  sortField: MachineSortField
  sortDirection: MachineSortDirection
}

const machinesSortOptions: MachinesSortOption[] = [
  {
    value: 'date-desc',
    label: 'Сначала новые',
    sortField: 'Date',
    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',
  },
]

const machinesTableSkeletonColumns: TableSkeletonColumn[] = [
  { variant: 'stack', lines: 2, width: '80%', headerWidth: '120px' },
  { width: '70%', headerWidth: '140px' },
  { variant: 'pill', headerWidth: '90px' },
  { variant: 'actions', headerWidth: '150px' },
]

type MachinesToolbarProps = {
  search: string
  selectedSort: MachinesSortOption
  showAddButton: boolean
  onSearchChange: (value: string) => void
  onSortChange: (option: MachinesSortOption) => void
  onAddMachine: () => void
}

const MachinesToolbar = memo(function MachinesToolbar({
  search,
  selectedSort,
  showAddButton,
  onSearchChange,
  onSortChange,
  onAddMachine,
}: MachinesToolbarProps) {
  return (
    <div className="devices-toolbar">
      <label className="devices-search">
        <Search size={16} />
        <input
          placeholder="Поиск"
          value={search}
          onChange={(event) => onSearchChange(event.target.value)}
        />
      </label>

      <div className="devices-toolbar__right">
        <DropdownMenu.Root>
          <DropdownMenu.Trigger asChild>
            <button className="devices-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="devices-sort-menu"
              align="end"
              sideOffset={8}
            >
              {machinesSortOptions.map((option) => (
                <DropdownMenu.Item
                  key={option.value}
                  className="devices-sort-menu__item"
                  onSelect={() => onSortChange(option)}
                >
                  <span>{option.label}</span>

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

        {showAddButton && (
          <button className="add-device" type="button" onClick={onAddMachine}>
            Добавить АТТ
          </button>
        )}
      </div>
    </div>
  )
})

type MachinesTableProps = {
  machines: Machine[]
  canManageMachines: boolean
  onOpen: (machineId: number) => void
  onEdit: (machine: Machine) => void
  onDelete: (machine: Machine) => void
  onBindBeacon: (machine: Machine) => void
  onUnbindBeacon: (machine: Machine) => void
}

type MachinesTableRowProps = {
  machine: Machine
  canManageMachines: boolean
  onOpen: (machineId: number) => void
  onEdit: (machine: Machine) => void
  onDelete: (machine: Machine) => void
  onBindBeacon: (machine: Machine) => void
  onUnbindBeacon: (machine: Machine) => void
}

const MachinesTableRow = memo(function MachinesTableRow({
  machine,
  canManageMachines,
  onOpen,
  onEdit,
  onDelete,
  onBindBeacon,
  onUnbindBeacon,
}: MachinesTableRowProps) {
  const machineDetails = [
    machine.type,
    machine.licensePlate || 'Госномер не указан',
  ].filter(Boolean).join(' · ')
  const lastStatusTitle = `Последний статус: ${getMachineStateLabel(
    machine.lastKnownState,
  )}. Время получения: ${formatMachineDateTime(machine.lastDataReceivedAt)}`

  return (
    <tr
      className="devices-table__row machines-table__row"
      onClick={() => onOpen(machine.id)}
    >
      <td>
        <div className="machine-main">
          <span>{machine.name}</span>
          <small>{machineDetails}</small>
        </div>
      </td>

      <td>{machine.department}</td>

      <td>
        <div
          className={`devices-status machines-status ${getMachineStateClass(
            machine.lastKnownState,
          )}`}
          title={lastStatusTitle}
        >
          <span className="devices-dot" />
          {getMachineStateLabel(machine.lastKnownState)}
        </div>
      </td>

      <td>
        {canManageMachines ? (
          <div className="machines-actions">
            <button
              className="machines-action machines-action--edit"
              type="button"
              onClick={(event) => {
                event.preventDefault()
                event.stopPropagation()
                onEdit(machine)
              }}
            >
              <Pencil size={15} />
              Ред.
            </button>

            {machine.beaconId ? (
              <button
                className="machines-action machines-action--beacon machines-action--warning"
                type="button"
                onClick={(event) => {
                  event.preventDefault()
                  event.stopPropagation()
                  onUnbindBeacon(machine)
                }}
              >
                <Link2Off size={15} />
                Отвязать
              </button>
            ) : (
              <button
                className="machines-action machines-action--beacon machines-action--bind"
                type="button"
                onClick={(event) => {
                  event.preventDefault()
                  event.stopPropagation()
                  onBindBeacon(machine)
                }}
              >
                <LinkIcon size={15} />
                Привязать
              </button>
            )}

            <button
              className="machines-action machines-action--delete"
              type="button"
              onClick={(event) => {
                event.preventDefault()
                event.stopPropagation()
                onDelete(machine)
              }}
            >
              <Trash2 size={15} />
            </button>
          </div>
        ) : (
          <span className="machine-muted">Только просмотр</span>
        )}
      </td>
    </tr>
  )
})

const MachinesTable = memo(function MachinesTable({
  machines,
  canManageMachines,
  onOpen,
  onEdit,
  onDelete,
  onBindBeacon,
  onUnbindBeacon,
}: MachinesTableProps) {
  return (
    <table className="devices-table machines-table">
      <thead>
        <tr>
          <th>Данные АТТ</th>
          <th>Подразделение</th>
          <th>Последний статус</th>
          <th>Действия</th>
        </tr>
      </thead>

      <tbody>
        {machines.map((machine) => (
          <MachinesTableRow
            key={machine.id}
            machine={machine}
            canManageMachines={canManageMachines}
            onOpen={onOpen}
            onEdit={onEdit}
            onDelete={onDelete}
            onBindBeacon={onBindBeacon}
            onUnbindBeacon={onUnbindBeacon}
          />
        ))}
      </tbody>
    </table>
  )
})

export function MachinesPage() {
  const navigate = useNavigate()
  const [searchParams, setSearchParams] = useSearchParams()
  const requestIdRef = useRef(0)

  const currentPage = Number(searchParams.get('mPage') ?? 0)
  const machineSearch = searchParams.get('mQuery') ?? ''
  const selectedSort = useMemo(() => {
    return machinesSortOptions.find(
      (option) => option.value === searchParams.get('mSort'),
    ) ?? machinesSortOptions[0]
  }, [searchParams])

  const [debouncedMachineSearch, setDebouncedMachineSearch] = useState('')
  const [machinesResponse, setMachinesResponse] =
    useState<MachinesPageResponse | null>(null)
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState<Error | null>(null)
  const [currentUser, setCurrentUser] = useState<Employee | null>(null)

  const [isAddMachineOpen, setIsAddMachineOpen] = useState(false)
  const [editingMachine, setEditingMachine] = useState<Machine | null>(null)
  const [deletingMachine, setDeletingMachine] = useState<Machine | null>(null)
  const [bindingMachine, setBindingMachine] = useState<Machine | null>(null)
  const [unbindingMachine, setUnbindingMachine] = useState<Machine | null>(null)
  const [isDangerActionLoading, setIsDangerActionLoading] = useState(false)

  const canManageMachines = authService.isAdmin(currentUser)

  useEffect(() => {
    const timeoutId = window.setTimeout(() => {
      setDebouncedMachineSearch(machineSearch.trim())
    }, 350)

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

  useEffect(() => {
    let isMounted = true

    async function syncCurrentUser() {
      const user = await authService.getCurrentUser()

      if (isMounted) {
        setCurrentUser(user)
      }
    }

    syncCurrentUser()

    window.addEventListener('auth:change', syncCurrentUser)
    window.addEventListener('users:change', syncCurrentUser)

    return () => {
      isMounted = false
      window.removeEventListener('auth:change', syncCurrentUser)
      window.removeEventListener('users:change', syncCurrentUser)
    }
  }, [])

  const machinesQueryVariables = useMemo<MachinesQueryParams>(() => ({
    page: currentPage,
    query: debouncedMachineSearch || undefined,
    sortDirection: selectedSort.sortDirection,
    sortField: selectedSort.sortField,
  }), [
    currentPage,
    debouncedMachineSearch,
    selectedSort.sortDirection,
    selectedSort.sortField,
  ])

  const fetchMachines = useCallback(async (
    variables: MachinesQueryParams = machinesQueryVariables,
  ) => {
    const requestId = requestIdRef.current + 1
    requestIdRef.current = requestId

    setLoading(true)
    setError(null)

    try {
      const machinesData = await machinesService.getMachines(variables)

      if (requestIdRef.current === requestId) {
        setMachinesResponse(machinesData)
      }

      return machinesData
    } catch (fetchError) {
      if (requestIdRef.current === requestId) {
        setError(
          fetchError instanceof Error
            ? fetchError
            : new Error('Не удалось загрузить парк АТТ'),
        )
      }

      throw fetchError
    } finally {
      if (requestIdRef.current === requestId) {
        setLoading(false)
      }
    }
  }, [machinesQueryVariables])

  useEffect(() => {
    fetchMachines().catch(() => undefined)
  }, [fetchMachines])

  useEffect(() => {
    function handleDataChange() {
      fetchMachines().catch(() => undefined)
    }

    window.addEventListener('machines:change', handleDataChange)

    return () => {
      window.removeEventListener('machines:change', handleDataChange)
    }
  }, [fetchMachines])

  const machines = machinesResponse?.page ?? []
  const totalPages = machinesResponse?.totalPages ?? 0
  const isInitialLoading = loading && !machinesResponse
  const isUpdating = loading && Boolean(machinesResponse)
  const hasMachines = machines.length > 0
  const hasAppliedFilters = Boolean(debouncedMachineSearch)
  const shouldShowErrorState = Boolean(error) && !hasMachines
  const shouldShowEmptyState = !loading && !error && !hasMachines
  const hasPrevPage = currentPage > 0
  const hasNextPage = totalPages > 0 && currentPage < totalPages - 1
  const paginationItems = useMemo(
    () => getPaginationItems(currentPage, totalPages),
    [currentPage, totalPages],
  )

  const updateSearchParams = useCallback((
    updates: Record<string, string | null>,
  ) => {
    setSearchParams(
      (prev) => {
        const next = new URLSearchParams(prev)

        Object.entries(updates).forEach(([key, value]) => {
          if (value === null || value === '') {
            next.delete(key)
            return
          }

          next.set(key, value)
        })

        return next
      },
      {
        replace: true,
      },
    )
  }, [setSearchParams])

  const setMachinesPage = useCallback((page: number) => {
    updateSearchParams({
      mPage: page > 0 ? String(page) : null,
    })
  }, [updateSearchParams])

  const refetchFirstMachinesPage = useCallback(async () => {
    setMachinesPage(0)

    await fetchMachines({
      ...machinesQueryVariables,
      page: 0,
    })
  }, [fetchMachines, machinesQueryVariables, setMachinesPage])

  const handleSearchChange = useCallback((value: string) => {
    updateSearchParams({
      mQuery: value || null,
      mPage: null,
    })
  }, [updateSearchParams])

  const handleSortChange = useCallback((option: MachinesSortOption) => {
    updateSearchParams({
      mSort: option.value === machinesSortOptions[0].value ? null : option.value,
      mPage: null,
    })
  }, [updateSearchParams])

  const handleNextPage = useCallback(() => {
    if (!hasNextPage || loading) return

    setMachinesPage(currentPage + 1)
  }, [currentPage, hasNextPage, loading, setMachinesPage])

  const handlePrevPage = useCallback(() => {
    if (!hasPrevPage || loading) return

    setMachinesPage(Math.max(0, currentPage - 1))
  }, [currentPage, hasPrevPage, loading, setMachinesPage])

  const handleJumpPrevPage = useCallback(() => {
    if (loading) return

    setMachinesPage(Math.max(0, currentPage - 5))
  }, [currentPage, loading, setMachinesPage])

  const handleJumpNextPage = useCallback(() => {
    if (loading) return

    setMachinesPage(Math.min(totalPages - 1, currentPage + 5))
  }, [currentPage, loading, setMachinesPage, totalPages])

  const handleOpenMachine = useCallback((machineId: number) => {
    navigate(`/machines/${machineId}`)
  }, [navigate])

  const handleConfirmDeleteMachine = useCallback(async () => {
    if (!deletingMachine || !canManageMachines) return

    try {
      setIsDangerActionLoading(true)
      await machinesService.deleteMachine(deletingMachine.id)
      setDeletingMachine(null)
      await refetchFirstMachinesPage()
    } finally {
      setIsDangerActionLoading(false)
    }
  }, [canManageMachines, deletingMachine, refetchFirstMachinesPage])

  const handleConfirmUnbindBeacon = useCallback(async () => {
    if (!unbindingMachine || !canManageMachines) return

    try {
      setIsDangerActionLoading(true)
      await machinesService.unbindBeacon(unbindingMachine.id)
      setUnbindingMachine(null)
      await refetchFirstMachinesPage()
    } finally {
      setIsDangerActionLoading(false)
    }
  }, [canManageMachines, refetchFirstMachinesPage, unbindingMachine])

  return (
    <section className="devices-page machines-page">
      <MachinesToolbar
        search={machineSearch}
        selectedSort={selectedSort}
        showAddButton={canManageMachines}
        onSearchChange={handleSearchChange}
        onSortChange={handleSortChange}
        onAddMachine={() => setIsAddMachineOpen(true)}
      />

      <div className="devices-table-container">
        <div className="employees-table-shadow">
          <SimpleBar
            className={`devices-table-card ${isUpdating ? 'is-updating' : ''}`}
          >
            <RevealContent
              loading={isInitialLoading}
              fallback={
                <TableSkeleton
                  className="devices-table machines-table"
                  columns={machinesTableSkeletonColumns}
                />
              }
            >
              {shouldShowErrorState && (
                <div className="devices-state devices-state--error">
                  Не удалось загрузить парк АТТ
                </div>
              )}

              {shouldShowEmptyState && (
                <EmptyState
                  className="devices-state"
                  variant={hasAppliedFilters ? 'not-found' : 'no-items'}
                  title={
                    hasAppliedFilters
                      ? 'Не найдено результатов'
                      : 'Парк АТТ пока пуст'
                  }
                  description={
                    hasAppliedFilters
                      ? 'По заданным параметрам не найдено ни одной АТТ. Попробуйте изменить поиск.'
                      : 'Добавьте первую АТТ, чтобы она появилась в списке мониторинга.'
                  }
                />
              )}

              {hasMachines && !shouldShowErrorState && (
                <MachinesTable
                  machines={machines}
                  canManageMachines={canManageMachines}
                  onOpen={handleOpenMachine}
                  onEdit={(machine) => setEditingMachine(machine)}
                  onDelete={(machine) => setDeletingMachine(machine)}
                  onBindBeacon={(machine) => setBindingMachine(machine)}
                  onUnbindBeacon={(machine) => setUnbindingMachine(machine)}
                />
              )}
            </RevealContent>
          </SimpleBar>
        </div>

        {totalPages > 1 && (
          <EmployeesPagination
            currentPage={currentPage}
            loading={loading}
            items={paginationItems}
            hasPrevPage={hasPrevPage}
            hasNextPage={hasNextPage}
            onPrev={handlePrevPage}
            onNext={handleNextPage}
            onJumpPrev={handleJumpPrevPage}
            onJumpNext={handleJumpNextPage}
            onPageChange={setMachinesPage}
          />
        )}
      </div>

      <AddMachineModal
        open={isAddMachineOpen}
        mode="create"
        onOpenChange={setIsAddMachineOpen}
        onSuccess={refetchFirstMachinesPage}
      />

      <AddMachineModal
        open={Boolean(editingMachine)}
        mode="edit"
        machine={editingMachine}
        onOpenChange={(open) => {
          if (!open) {
            setEditingMachine(null)
          }
        }}
        onSuccess={refetchFirstMachinesPage}
      />

      <BeaconBindingModal
        open={Boolean(bindingMachine)}
        machine={bindingMachine}
        onOpenChange={(open) => {
          if (!open) {
            setBindingMachine(null)
          }
        }}
        onSuccess={refetchFirstMachinesPage}
      />

      <ConfirmDangerDialog
        open={Boolean(deletingMachine)}
        title="Удалить АТТ?"
        description={
          deletingMachine
            ? `АТТ «${deletingMachine.name}» будет удалена из системы. Если к ней привязан маяк, он станет свободным.`
            : ''
        }
        confirmText="Удалить"
        isLoading={isDangerActionLoading}
        onOpenChange={(open) => {
          if (!open) {
            setDeletingMachine(null)
          }
        }}
        onConfirm={handleConfirmDeleteMachine}
      />

      <ConfirmDangerDialog
        open={Boolean(unbindingMachine)}
        title="Отвязать маяк?"
        description={
          unbindingMachine
            ? `Маяк будет отвязан от АТТ «${unbindingMachine.name}» и станет доступен для привязки.`
            : ''
        }
        confirmText="Отвязать"
        isLoading={isDangerActionLoading}
        onOpenChange={(open) => {
          if (!open) {
            setUnbindingMachine(null)
          }
        }}
        onConfirm={handleConfirmUnbindBeacon}
      />
    </section>
  )
}
