import {
  lazy,
  Suspense,
  useCallback,
  useEffect,
  useMemo,
  useState,
} from 'react'
import {
  useNavigate,
  useOutletContext,
  useParams,
  useSearchParams,
} from 'react-router-dom'
import { AnimatePresence, m } from 'framer-motion'
import { useQuery } from '@apollo/client/react'

import type { Device as PageDevice } from './types'

import { GET_DEVICE_PAGE_QUERY } from '../../entities/device/api/device.graphql'
import type {
  Device as ApiDevice,
  DeviceNetworkStatus,
  GetPhoneGpsTrackData,
  GetTelemetryData,
  GetDevicePageData,
  GetDevicePageVariables,
} from '../../entities/device/model/types'

import { DeviceMainCard } from './components/DeviceMainCard/DeviceMainCard'
import { DeviceMapCard } from './components/DeviceMapCard/DeviceMapCard'
import { DeviceActionsCard } from './components/DeviceActionsCard/DeviceActionsCard'
import { DevicePermissionsCard } from './components/DevicePermissionsCard/DevicePermissionsCard'
import { DeviceStatsCards } from './components/DeviceStatsCards/DeviceStatsCards'
import { DeviceAppsView } from './components/DeviceAppsView/DeviceAppsView'
import { pageSwapVariants, uiFadeTransition } from '../../shared/lib/motion'
import { isPhonePolicyOptionEnabled } from '../../entities/device/lib/phonePolicy'
import { EmptyState } from '../../shared/ui/EmptyState/EmptyState'

import './DevicePage.scss'
import { DevicePageSkeleton } from './components/DevicePageSkeleton/DevicePageSkeleton'
import type { AppLayoutOutletContext } from '../../app/layouts/AppLayout'
import {
  preloadOnIdle,
  useLazyMount,
} from '../../shared/lib/lazyMount'

const loadDeviceHistoryModal = () =>
  import('./components/DeviceHistoryModal/DeviceHistoryModal').then(
    (module) => ({
      default: module.DeviceHistoryModal,
    }),
  )

const DeviceHistoryModal = lazy(loadDeviceHistoryModal)

function formatLocationDate(timestamp: number) {
  if (!timestamp) return 'Нет данных'

  return new Intl.DateTimeFormat('ru-RU', {
    day: '2-digit',
    month: '2-digit',
    year: 'numeric',
    hour: '2-digit',
    minute: '2-digit',
  }).format(new Date(timestamp))
}

function formatTechValue(value?: number | null) {
  return typeof value === 'number' ? String(value) : undefined
}

function getLatestTelemetryItem(telemetry: GetTelemetryData['getTelemetry']) {
  if (!telemetry.length) return null

  return [...telemetry].sort((a, b) => b.date - a.date)[0]
}

function getSortedGpsTrack(track: GetPhoneGpsTrackData['getPhoneGpsTrack']) {
  return [...track].sort((a, b) => a.date - b.date)
}

function getDeviceNetworkConnection(status?: DeviceNetworkStatus) {
  if (status === 'Online') return 'online'
  if (status === 'Lost') return 'offlineDanger'

  return 'offline'
}

function getDeviceNetworkLabel(status?: DeviceNetworkStatus) {
  if (status === 'Online') return 'В сети'
  if (status === 'Lost') return 'Потерян'

  return 'Не в сети'
}

function mapApiDeviceToPageDevice(
  device: ApiDevice,
  batteryLevel?: number,
  gpsTrack: GetPhoneGpsTrackData['getPhoneGpsTrack'] = [],
  telemetry: GetTelemetryData['getTelemetry'] = [],
): PageDevice {
  const sortedGpsTrack = getSortedGpsTrack(gpsTrack)
  const policy = device.policy
  const techState = device.techState
  const needMaintenance = techState?.needMaintenance ?? false
  const bluetoothEnabled = isPhonePolicyOptionEnabled(
    policy?.bluetooth ?? policy?.canUseBluetooth,
  )
  const gpsEnabled = isPhonePolicyOptionEnabled(policy?.GPS ?? policy?.canUseGPS)
  const cameraEnabled = isPhonePolicyOptionEnabled(
    policy?.camera ?? policy?.canUseCamera,
  )
  const simEnabled = isPhonePolicyOptionEnabled(policy?.sim ?? policy?.canUseSim)
  const isLocked = policy?.locked ?? false

  const currentGpsPoint =
    sortedGpsTrack.length > 0
      ? sortedGpsTrack[sortedGpsTrack.length - 1]
      : null

  const routeGpsPoints =
    sortedGpsTrack.length > 1
      ? sortedGpsTrack.slice(0, -1)
      : []

  const location = currentGpsPoint
    ? {
        lat: currentGpsPoint.lat,
        lng: currentGpsPoint.lng,
      }
    : device.lastLocation
      ? {
          lat: device.lastLocation.lat,
          lng: device.lastLocation.lng,
        }
      : undefined

  const lastLocationAt = currentGpsPoint
    ? formatLocationDate(currentGpsPoint.date)
    : device.lastLocation
      ? formatLocationDate(device.lastLocation.date)
      : 'Нет данных'

  return {
    id: device.id,

    model: 'АРМАФОН S3.3+',
    factoryNumber: device.serial || 'Заводской номер не указан',
    imei: device.imei || 'IMEI не указан',
    imei2: device.imei2 || 'IMEI 2 не указан',
    serialNumber: device.serial || undefined,

    employee: device.org?.name ?? null,
    organisation: device.org?.name,
    organisationId: device.org?.id ?? device.orgId,
    policy,
    organisationPolicy: device.org?.policy ?? null,

    condition: needMaintenance ? 'inspection' : 'ok',
    connection: getDeviceNetworkConnection(device.networkStatus),
    connectionText: getDeviceNetworkLabel(device.networkStatus),

    workTime: null,
    registeredAt: device.registerDate
      ? formatLocationDate(device.registerDate)
      : undefined,

    image: undefined,

    location,
    lastLocationAt,
    lastLocationDate: device.lastLocation?.date,

    route: routeGpsPoints.map((point) => ({
      lat: point.lat,
      lng: point.lng,
      time: formatLocationDate(point.date),
    })),

    permissions: {
      wifi: false,
      bluetooth: bluetoothEnabled,
      gps: gpsEnabled,
      camera: cameraEnabled,
      sim: simEnabled,
      speaker: false,
      locked: isLocked,
    },

    statusIcons: {
      gps: gpsEnabled,
      wifi: false,
      bluetooth: bluetoothEnabled,
      lock: isLocked,
      camera: cameraEnabled,
      sim: simEnabled,
      sound: false,
      kiosk: false,
    },

    battery: techState?.batteryLevel ?? batteryLevel,
    batteryMaxCapacity: techState?.batteryRemainingCapacity ?? undefined,
    telemetry,
    chargeCycles: formatTechValue(techState?.batteryCycles),
    totalWorkTime: techState?.worktime ?? null,
    mediumImpacts: formatTechValue(techState?.hits),
    overheats: formatTechValue(techState?.overheats),
  }
}

export function DevicePage() {
  const { deviceId } = useParams()
  const navigate = useNavigate()
  const { setNavbarBreadcrumbs } =
    useOutletContext<AppLayoutOutletContext>()
  const [searchParams, setSearchParams] = useSearchParams()
  const [isHistoryOpen, setIsHistoryOpen] = useState(false)
  const shouldMountHistoryModal = useLazyMount(isHistoryOpen)

  const numericDeviceId = Number(deviceId)
  const isAppsView = searchParams.get('view') === 'apps'

  useEffect(() => {
    return preloadOnIdle([loadDeviceHistoryModal])
  }, [])

  const {
    data,
    loading,
    error,
    refetch,
    startPolling,
    stopPolling,
  } = useQuery<
    GetDevicePageData,
    GetDevicePageVariables
  >(GET_DEVICE_PAGE_QUERY, {
    variables: {
      id: numericDeviceId,
      phoneId: String(numericDeviceId),
      telemetryStartDate: 0,
      gpsStartDate: 0,
    },
    skip: !numericDeviceId,
    fetchPolicy: 'network-only',
    pollInterval: 15000,
  })

  const handlePermissionsEditingChange = useCallback(
    (isEditing: boolean) => {
      if (isEditing) {
        stopPolling()
        return
      }

      if (numericDeviceId) {
        startPolling(15000)
      }
    },
    [numericDeviceId, startPolling, stopPolling],
  )

  const latestTelemetryItem = useMemo(() => {
    return getLatestTelemetryItem(data?.getTelemetry ?? [])
  }, [data])

  const device = useMemo(() => {
    if (!data?.getPhone) return null

    return mapApiDeviceToPageDevice(
      data.getPhone,
      latestTelemetryItem?.batteryLevel ?? undefined,
      data.getPhoneGpsTrack ?? [],
      data.getTelemetry ?? [],
    )
  }, [data, latestTelemetryItem])

  useEffect(() => {
    if (!device) {
      setNavbarBreadcrumbs([])
      return
    }

    setNavbarBreadcrumbs([
      {
        label: device.factoryNumber,
        to: isAppsView ? `/devices/${device.id}` : undefined,
      },
      ...(isAppsView ? [{ label: 'Приложения' }] : []),
    ])

    return () => {
      setNavbarBreadcrumbs([])
    }
  }, [device, isAppsView, setNavbarBreadcrumbs])

  const handleOpenApps = useCallback(() => {
    const nextParams = new URLSearchParams(searchParams)
    nextParams.set('view', 'apps')

    setSearchParams(nextParams)
  }, [searchParams, setSearchParams])

  const handleCloseApps = useCallback(() => {
    const nextParams = new URLSearchParams(searchParams)
    nextParams.delete('view')

    setSearchParams(nextParams, {
      replace: true,
    })
  }, [searchParams, setSearchParams])

  const handleOpenHistory = useCallback(() => {
    setIsHistoryOpen(true)
  }, [])

  const handleRefetchPolicy = useCallback(async () => {
    const result = await refetch()

    return result.data?.getPhone?.policy
  }, [refetch])

  const handleLockStateChange = useCallback(async () => {
    const result = await refetch()

    return result.data?.getPhone?.policy?.locked
  }, [refetch])

  if (!numericDeviceId) {
    return (
      <section className="device-page">
        <EmptyState
          className="device-page__empty"
          variant="not-found"
          title="Некорректный ID устройства"
          description="Проверьте ссылку или вернитесь к списку устройств."
        >
          <button
            type="button"
            onClick={() => navigate('/devices')}
          >
            Вернуться к списку
          </button>
        </EmptyState>
      </section>
    )
  }

  if (loading && !device) {
    return <DevicePageSkeleton />
  }

  if (error || !device) {
    return (
      <section className="device-page">
        <EmptyState
          className="device-page__empty"
          variant="not-found"
          title="Устройство не найдено"
          description="Возможно, устройство было удалено или ссылка устарела."
        >
          <button
            type="button"
            onClick={() => navigate('/devices')}
          >
            Вернуться к списку
          </button>
        </EmptyState>
      </section>
    )
  }

  return (
    <section className={`device-page ${isAppsView ? 'device-page--apps' : ''}`}>
      <AnimatePresence mode="wait" initial={false}>
        {isAppsView ? (
          <m.div
            key="apps"
            className="device-page__motion-view device-page__apps-motion"
            variants={pageSwapVariants}
            initial="initial"
            animate="animate"
            exit="exit"
            transition={uiFadeTransition}
          >
            <DeviceAppsView
              deviceId={device.id}
              deviceName={device.factoryNumber}
              onBack={handleCloseApps}
            />
          </m.div>
        ) : (
          <m.div
            key="overview"
            className="device-page__motion-view"
            variants={pageSwapVariants}
            initial="initial"
            animate="animate"
            exit="exit"
            transition={uiFadeTransition}
          >
            <div className="device-page__grid">
              <DeviceMainCard
                device={device}
                onOpenHistory={handleOpenHistory}
              />
              <DeviceMapCard device={device} />

              <div className="device-page__details-row">
                <DeviceStatsCards device={device} />
                <DevicePermissionsCard
                  device={device}
                  onEditingChange={handlePermissionsEditingChange}
                  onRefetchPolicy={handleRefetchPolicy}
                />
                <DeviceActionsCard
                  device={device}
                  onOpenApps={handleOpenApps}
                  onLockStateChange={handleLockStateChange}
                />
              </div>
            </div>
          </m.div>
        )}
      </AnimatePresence>
      {shouldMountHistoryModal && (
        <Suspense fallback={null}>
          <DeviceHistoryModal
            open={isHistoryOpen}
            onOpenChange={setIsHistoryOpen}
            device={device}
          />
        </Suspense>
      )}
    </section>
  )
}
