import { memo, useEffect, useState } from 'react'
import { AnimatePresence, m, useReducedMotion } from 'framer-motion'
import { useMutation } from '@apollo/client/react'
import { CircleAlert, Unlock } from 'lucide-react'

import { Apps } from '../../../../assets/icons/Apps'
import { BlockIcon } from '../../../../assets/icons/Block'
import { LoadingTwotoneLoopIcon } from '../../../../assets/icons/LoadingTwotoneLoop'
import type { Device } from '../../types'
import {
  CHANGE_PHONE_POLICY_MUTATION,
} from '../../../../entities/device/api/device.graphql'
import type {
  ChangePhonePolicyData,
  ChangePhonePolicyVariables,
} from '../../../../entities/device/model/types'
import {
  normalizeComponentUseOption,
  normalizeServiceUseOption,
} from '../../../../entities/device/lib/phonePolicy'

import './DeviceActionsCard.scss'

type DeviceActionsCardProps = {
  device: Device
  onOpenApps: () => void
  onLockStateChange?: () => Promise<boolean | null | undefined> | boolean | null | void
}

function getChangeLockVariables(
  device: Device,
  locked: boolean,
): ChangePhonePolicyVariables {
  const policy = device.policy
  const permissions = device.permissions

  return {
    id: String(device.id),
    bluetooth: normalizeServiceUseOption(
      policy?.bluetooth ?? policy?.canUseBluetooth,
      permissions?.bluetooth,
    ),
    camera: normalizeComponentUseOption(
      policy?.camera ?? policy?.canUseCamera,
      permissions?.camera,
    ),
    GPS: normalizeServiceUseOption(
      policy?.GPS ?? policy?.canUseGPS,
      permissions?.gps,
    ),
    sim: normalizeServiceUseOption(
      policy?.sim ?? policy?.canUseSim,
      permissions?.sim,
    ),
    locked,
  }
}

function areDeviceActionsCardPropsEqual(
  prev: DeviceActionsCardProps,
  next: DeviceActionsCardProps,
) {
  const prevPolicy = prev.device.policy
  const nextPolicy = next.device.policy
  const prevPermissions = prev.device.permissions
  const nextPermissions = next.device.permissions

  return (
    prev.onOpenApps === next.onOpenApps &&
    prev.onLockStateChange === next.onLockStateChange &&
    prev.device.id === next.device.id &&
    prev.device.connection === next.device.connection &&
    prevPolicy?.bluetooth === nextPolicy?.bluetooth &&
    prevPolicy?.camera === nextPolicy?.camera &&
    prevPolicy?.GPS === nextPolicy?.GPS &&
    prevPolicy?.sim === nextPolicy?.sim &&
    prevPolicy?.locked === nextPolicy?.locked &&
    prevPermissions?.bluetooth === nextPermissions?.bluetooth &&
    prevPermissions?.camera === nextPermissions?.camera &&
    prevPermissions?.gps === nextPermissions?.gps &&
    prevPermissions?.sim === nextPermissions?.sim &&
    prevPermissions?.locked === nextPermissions?.locked
  )
}

export const DeviceActionsCard = memo(function DeviceActionsCard({
  device,
  onOpenApps,
  onLockStateChange,
}: DeviceActionsCardProps) {
  const deviceLocked = device.permissions?.locked ?? device.policy?.locked ?? false
  const isLost = device.connection === 'offlineDanger'
  const [displayLocked, setDisplayLocked] = useState(deviceLocked)
  const [pendingLockTarget, setPendingLockTarget] = useState<boolean | null>(null)
  const [lockError, setLockError] = useState(false)
  const [changePhonePolicy] = useMutation<
    ChangePhonePolicyData,
    ChangePhonePolicyVariables
  >(CHANGE_PHONE_POLICY_MUTATION)
  const shouldReduceMotion = useReducedMotion()
  const isLockLoading = pendingLockTarget !== null
  const lockStateTransition = shouldReduceMotion
    ? { duration: 0 }
    : {
        duration: 0.18,
        ease: [0.34, 1.42, 0.64, 1] as [number, number, number, number],
      }
  const lockLoadingTarget = pendingLockTarget ?? !displayLocked
  const lockButtonView = isLockLoading
    ? {
        key: 'loading',
        icon: (
          <LoadingTwotoneLoopIcon
            className="device-actions__lock-icon device-actions__loading-icon"
            aria-hidden="true"
          />
        ),
        title: lockLoadingTarget ? 'Блокируем...' : 'Разблокируем...',
        description: 'Отправляем команду на устройство',
      }
    : isLost
      ? {
          key: 'lost',
          icon: (
            <BlockIcon
              className="device-actions__lock-icon"
              aria-hidden="true"
            />
          ),
          title: 'Долго не в сети',
          description: 'Управление блокировкой недоступно',
        }
      : displayLocked
        ? {
            key: 'unlock',
            icon: (
              <Unlock
                className="device-actions__lock-icon"
                aria-hidden="true"
              />
            ),
            title: 'Разблокировать',
            description: 'Вернуть устройству доступ к работе',
          }
        : {
            key: 'lock',
            icon: (
              <BlockIcon
                className="device-actions__lock-icon"
                aria-hidden="true"
              />
            ),
            title: 'Заблокировать',
            description: 'Удаленно заблокировать устройство',
          }

  useEffect(() => {
    setDisplayLocked(deviceLocked)
  }, [device.id, deviceLocked])

  async function handleToggleLock() {
    if (isLost || isLockLoading) return

    const nextLocked = !displayLocked
    let changedLocked: boolean

    setLockError(false)
    setPendingLockTarget(nextLocked)

    try {
      const result = await changePhonePolicy({
        variables: getChangeLockVariables(device, nextLocked),
      })
      changedLocked = result.data?.changePhonePolicy.locked ?? nextLocked
    } catch (error) {
      console.error('Не удалось изменить блокировку устройства', error)
      setLockError(true)
      setPendingLockTarget(null)

      return
    }

    try {
      const refetchResult = onLockStateChange?.()
      const refetchedLocked = await Promise.resolve(refetchResult)

      setDisplayLocked(
        typeof refetchedLocked === 'boolean'
          ? refetchedLocked
          : changedLocked,
      )
    } catch (error) {
      console.error('Не удалось обновить данные устройства', error)
      setDisplayLocked(changedLocked)
    } finally {
      setPendingLockTarget(null)
    }
  }

  return (
    <div className="device-card device-actions">
      <h3>Управление</h3>

      <div className="actions-button-container">
        <button
          className={`device-actions__lock-button ${
            displayLocked ? 'is-unlock' : 'is-lock'
          } ${isLockLoading ? 'is-loading' : ''}`}
          type="button"
          disabled={isLost || isLockLoading}
          onClick={handleToggleLock}
        >
          <span className="device-actions__lock-content">
            <AnimatePresence initial={false}>
              <m.span
                key={lockButtonView.key}
                className="device-actions__lock-state"
                initial={{
                  opacity: 0,
                  x: shouldReduceMotion ? 0 : 10,
                }}
                animate={{
                  opacity: 1,
                  x: 0,
                  transition: lockStateTransition,
                }}
                exit={{
                  opacity: 0,
                  x: shouldReduceMotion ? 0 : -10,
                  transition: {
                    duration: shouldReduceMotion ? 0 : 0.14,
                    ease: 'easeOut',
                  },
                }}
              >
                {lockButtonView.icon}

                <span className="actions-button-text">
                  <span>{lockButtonView.title}</span>
                  <small>{lockButtonView.description}</small>
                </span>
              </m.span>
            </AnimatePresence>
          </span>
        </button>

        <button
          className="device-actions__apps-button"
          type="button"
          onClick={onOpenApps}
        >
          <Apps className="device-actions__apps-icon" aria-hidden="true" />
          <div className="actions-button-text">
            <span>Приложения</span>
            <small>Управление установленными приложениями</small>
          </div>
        </button>

        <div className="future-label">
          <CircleAlert />
          Дополнительные функции будут доступны позже
        </div>

        {lockError && (
          <div className="device-actions__error">
            Не удалось изменить блокировку
          </div>
        )}
      </div>
    </div>
  )
}, areDeviceActionsCardPropsEqual)
