import { useEffect, useMemo, useState } from 'react'
import * as Dialog from '@radix-ui/react-dialog'
import { QRCodeSVG } from 'qrcode.react'
import { Clock3, QrCode, RefreshCw, X } from 'lucide-react'
import { useMutation } from '@apollo/client/react'

import { CREATE_PHONE_REGISTRATION_TOKEN_MUTATION } from '../../../../entities/device/api/device.graphql'
import type {
  CreatePhoneRegistrationTokenData,
  CreatePhoneRegistrationTokenVariables,
} from '../../../../entities/device/model/types'
import { MotionDialogContent } from '../../../../shared/ui/MotionDialog/MotionDialog'

import './AddDeviceModal.scss'

type AddDeviceModalProps = {
  open: boolean
  onOpenChange: (open: boolean) => void
}

const SECOND_MS = 1000
const DAY_SECONDS = 24 * 60 * 60
const UNIX_SECONDS_THRESHOLD = 1_000_000_000
const UNIX_MILLISECONDS_THRESHOLD = 1_000_000_000_000

function getExpiresAt(expiresIn: number, receivedAt: number) {
  if (!Number.isFinite(expiresIn) || expiresIn <= 0) return 0

  if (expiresIn >= UNIX_MILLISECONDS_THRESHOLD) {
    return expiresIn
  }

  if (expiresIn >= UNIX_SECONDS_THRESHOLD) {
    return expiresIn * SECOND_MS
  }

  if (expiresIn > DAY_SECONDS) {
    return receivedAt + expiresIn
  }

  return receivedAt + expiresIn * SECOND_MS
}

function formatTokenTimeLeft(milliseconds: number) {
  const totalSeconds = Math.max(0, Math.ceil(milliseconds / SECOND_MS))
  const minutes = Math.floor(totalSeconds / 60)
  const seconds = totalSeconds % 60

  return `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`
}

export function AddDeviceModal({ open, onOpenChange }: AddDeviceModalProps) {
  const [tokenReceivedAt, setTokenReceivedAt] = useState(0)
  const [now, setNow] = useState(() => Date.now())

  const [createToken, { data, loading, error }] = useMutation<
    CreatePhoneRegistrationTokenData,
    CreatePhoneRegistrationTokenVariables
  >(CREATE_PHONE_REGISTRATION_TOKEN_MUTATION, {
    fetchPolicy: 'network-only',
  })

  const registrationData = data?.createPhoneRegistrationToken
  const expiresAt = registrationData
    ? getExpiresAt(registrationData.expiresIn, tokenReceivedAt)
    : 0
  const timeLeft = expiresAt ? Math.max(0, expiresAt - now) : 0
  const isTokenExpired = Boolean(registrationData && timeLeft <= 0)

  const qrValue = useMemo(() => {
    if (!registrationData) return ''

    return JSON.stringify({
      address: registrationData.address,
      token: registrationData.token,
    })
  }, [registrationData])

  useEffect(() => {
    if (!open) return

    createToken()
      .then(() => {
        const receivedAt = Date.now()

        setTokenReceivedAt(receivedAt)
        setNow(receivedAt)
      })
      .catch(() => {
        setTokenReceivedAt(0)
      })
  }, [open, createToken])

  useEffect(() => {
    if (!open || !registrationData || isTokenExpired) return

    const intervalId = window.setInterval(() => {
      setNow(Date.now())
    }, SECOND_MS)

    return () => {
      window.clearInterval(intervalId)
    }
  }, [open, registrationData, isTokenExpired])

  async function handleRefreshToken() {
    setNow(Date.now())

    try {
      await createToken()
      const receivedAt = Date.now()

      setTokenReceivedAt(receivedAt)
      setNow(receivedAt)
    } catch {
      setTokenReceivedAt(0)
    }
  }

  return (
    <Dialog.Root open={open} onOpenChange={onOpenChange}>
      <MotionDialogContent
        open={open}
        overlayClassName="add-device-modal__overlay"
        contentClassName="add-device-modal"
        motionPreset="fade"
      >
          <div className="add-device-modal__header">
            <div>
              <Dialog.Title className="add-device-modal__title">
                Добавить устройство
              </Dialog.Title>

              <Dialog.Description className="add-device-modal__description">
                Отсканируйте QR-код на устройстве для привязки к системе
              </Dialog.Description>
            </div>

            <Dialog.Close className="add-device-modal__close" type="button">
              <X size={20} />
            </Dialog.Close>
          </div>

          <div className="add-device-qr">
            <div className="add-device-qr__box">
              {loading && (
                <div className="add-device-qr__state">
                  <QrCode size={64} />
                  <span>Генерация QR-кода...</span>
                </div>
              )}

              {error && !loading && (
                <div className="add-device-qr__state add-device-qr__state--error">
                  <QrCode size={64} />
                  <span>Не удалось получить QR-код</span>
                </div>
              )}

              {!loading && !error && qrValue && (
                <div className="add-device-qr__content">
                  <QRCodeSVG
                    value={qrValue}
                    size={220}
                    level="M"
                    bgColor='transparent'
                    fgColor="currentColor"
                    includeMargin
                  />
                </div>
              )}
            </div>

            {registrationData && !loading && !error && (
              <div
                className={`add-device-token-timer ${
                  isTokenExpired ? 'is-expired' : ''
                }`}
              >
                <Clock3 size={18} />
                <span>
                  {isTokenExpired
                    ? 'QR-код истек'
                    : `QR-код активен еще ${formatTokenTimeLeft(timeLeft)}`}
                </span>

                {isTokenExpired && (
                  <button
                    type="button"
                    onClick={handleRefreshToken}
                    disabled={loading}
                  >
                    <RefreshCw size={16} />
                    Обновить
                  </button>
                )}
              </div>
            )}

            <p>
              После сканирования устройство появится в списке и будет доступно
              для назначения сотруднику.
            </p>
          </div>

          <div className="add-device-modal__footer">
            <Dialog.Close className="add-device-cancel" type="button">
              Закрыть
            </Dialog.Close>
          </div>
      </MotionDialogContent>
    </Dialog.Root>
  )
}
