import { useMemo, useState } from 'react'
import * as Accordion from '@radix-ui/react-accordion'
import ReactECharts from 'echarts-for-react'
import {
  AppWindow,
  Box,
  ChevronDown,
  MessageCircle,
  Radio,
  Settings,
  ShieldCheck,
} from 'lucide-react'
import type { LucideIcon } from 'lucide-react'

import type { DevicePackage } from '../../../../entities/device/model/types'
import type { DeviceHistoryPeriodValue } from '../DeviceHistoryPeriodControl/DeviceHistoryPeriodControl'
import { EmptyState } from '../../../../shared/ui/EmptyState/EmptyState'

import './DeviceAppsAccordion.scss'

type DeviceAppsAccordionProps = {
  packages: DevicePackage[]
  period: DeviceHistoryPeriodValue | null
  isLoading?: boolean
  isError?: boolean
}

type AppEventCategory = 'active' | 'background' | 'network'

type MockAppEvent = {
  category: AppEventCategory
  name: string
  start: number
  end: number
  color: string
}

type RectShape = {
  x: number
  y: number
  width: number
  height: number
}

type AppEventRenderParams = {
  coordSys: RectShape
}

type AppEventRenderApi = {
  value: (index: number) => number
  coord: (value: [number, number]) => [number, number]
  style: () => Record<string, unknown>
}

type AppEventTooltipParams = {
  data?: {
    meta?: MockAppEvent
  }
}

const FONT_FAMILY = 'Montserrat Variable, Montserrat, sans-serif'
const DAY = 24 * 60 * 60 * 1000

const EVENT_CATEGORIES: Array<{
  key: AppEventCategory
  label: string
}> = [
  { key: 'active', label: 'Открыто' },
  { key: 'background', label: 'Фон' },
  { key: 'network', label: 'Сеть' },
]

const EVENT_COLORS: Record<AppEventCategory, string> = {
  active: '#031d9a',
  background: '#18b89b',
  network: '#ff8a34',
}

function getHash(value: string) {
  return value.split('').reduce((sum, char) => {
    return sum + char.charCodeAt(0)
  }, 0)
}

function clamp(value: number, min: number, max: number) {
  return Math.min(Math.max(value, min), max)
}

function getPeriodBounds(period: DeviceHistoryPeriodValue | null) {
  const end = period?.periodEnd.getTime() ?? Date.now()
  const start = period?.periodStart.getTime() ?? end - DAY

  return { start, end }
}

function getAppName(app: DevicePackage) {
  return app.name || app.packageName || 'Без названия'
}

function formatDate(timestamp?: number | null) {
  if (!timestamp) return 'Дата установки не указана'

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

function formatTooltipDate(value: number) {
  return new Intl.DateTimeFormat('ru-RU', {
    day: '2-digit',
    month: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
  }).format(new Date(value))
}

function formatDuration(start: number, end: number) {
  const totalMinutes = Math.max(1, Math.round((end - start) / 60000))
  const hours = Math.floor(totalMinutes / 60)
  const minutes = totalMinutes % 60

  if (!hours) return `${minutes} мин`
  if (!minutes) return `${hours} ч`

  return `${hours} ч ${minutes} мин`
}

function getUsagePercent(app: DevicePackage, period: DeviceHistoryPeriodValue | null) {
  const { start, end } = getPeriodBounds(period)
  const periodSeed = Math.round((end - start) / DAY)
  const hash = getHash(`${app.packageName}-${app.name}-${periodSeed}`)

  return clamp(4 + (hash % 72), 1, 96)
}

function getAppVisual(app: DevicePackage): {
  Icon: LucideIcon
  modifier: string
} {
  const value = `${app.name} ${app.packageName}`.toLowerCase()

  if (value.includes('radio') || value.includes('fm')) {
    return { Icon: Radio, modifier: 'is-radio' }
  }

  if (value.includes('sms') || value.includes('ims') || value.includes('message')) {
    return { Icon: MessageCircle, modifier: 'is-message' }
  }

  if (value.includes('settings') || value.includes('android')) {
    return { Icon: Settings, modifier: 'is-system' }
  }

  if (value.includes('security') || value.includes('cts')) {
    return { Icon: ShieldCheck, modifier: 'is-security' }
  }

  if (value.includes('package')) {
    return { Icon: Box, modifier: 'is-package' }
  }

  return { Icon: AppWindow, modifier: 'is-default' }
}

function createMockEvents(
  app: DevicePackage,
  period: DeviceHistoryPeriodValue | null,
  usagePercent: number,
): MockAppEvent[] {
  const { start, end } = getPeriodBounds(period)
  const duration = end - start
  const hash = getHash(app.packageName || app.name || '')
  const eventsCount = clamp(Math.round(usagePercent / 12), 2, 8)

  return Array.from({ length: eventsCount }).map((_, index) => {
    const category = EVENT_CATEGORIES[(hash + index) % EVENT_CATEGORIES.length].key

    const eventStart =
      start +
      duration * ((index + 1) / (eventsCount + 1)) +
      ((hash + index * 17) % 30) * 60 * 1000

    const eventDuration =
      duration * (0.015 + ((hash + index * 13) % 9) / 100)

    const safeStart = clamp(eventStart, start, end)
    const safeEnd = clamp(eventStart + eventDuration, safeStart + 60 * 1000, end)

    return {
      category,
      name:
        category === 'active'
          ? 'Приложение открыто'
          : category === 'background'
            ? 'Фоновая активность'
            : 'Сетевое событие',
      start: safeStart,
      end: safeEnd,
      color: EVENT_COLORS[category],
    }
  })
}

function clipRectByRect(shape: RectShape, rect: RectShape) {
  const x = Math.max(shape.x, rect.x)
  const y = Math.max(shape.y, rect.y)
  const x2 = Math.min(shape.x + shape.width, rect.x + rect.width)
  const y2 = Math.min(shape.y + shape.height, rect.y + rect.height)

  if (x2 < x || y2 < y) return null

  return {
    x,
    y,
    width: x2 - x,
    height: y2 - y,
  }
}

function renderAppEventItem(
  params: AppEventRenderParams,
  api: AppEventRenderApi,
) {
  const categoryIndex = api.value(0)
  const start = api.coord([api.value(1), categoryIndex])
  const end = api.coord([api.value(2), categoryIndex])

  const height = 12

  const rectShape = clipRectByRect(
    {
      x: start[0],
      y: start[1] - height / 2,
      width: end[0] - start[0],
      height,
    },
    {
      x: params.coordSys.x,
      y: params.coordSys.y,
      width: params.coordSys.width,
      height: params.coordSys.height,
    },
  )

  if (!rectShape) return null

  return {
    type: 'rect',
    shape: {
      ...rectShape,
      r: 999,
    },
    style: api.style(),
  }
}

function getChartOption(
  events: MockAppEvent[],
  period: DeviceHistoryPeriodValue | null,
) {
  const { start, end } = getPeriodBounds(period)

  return {
    animation: true,
    animationDuration: 220,

    textStyle: {
      fontFamily: FONT_FAMILY,
      fontWeight: 500,
    },

    tooltip: {
      trigger: 'item',
      borderWidth: 0,
      backgroundColor: '#20263a',
      padding: 12,
      textStyle: {
        color: '#ffffff',
        fontSize: 13,
        fontFamily: FONT_FAMILY,
      },
      extraCssText:
        'border-radius: 14px; box-shadow: 0 12px 32px rgba(16, 24, 40, 0.22);',
      formatter: (params: AppEventTooltipParams) => {
        const event = params.data?.meta

        if (!event) return ''

        return `
          <div style="min-width:260px;">
            <div style="color:#aeb8cc;font-size:12px;margin-bottom:8px;">
              ${formatTooltipDate(event.start)} — ${formatTooltipDate(event.end)}
            </div>

            <div style="display:flex;align-items:center;justify-content:space-between;gap:18px;">
              <div style="display:flex;align-items:center;gap:8px;">
                <span style="width:10px;height:10px;border-radius:999px;background:${event.color};display:inline-block;"></span>
                <span>${event.name}</span>
              </div>

              <strong>${formatDuration(event.start, event.end)}</strong>
            </div>
          </div>
        `
      },
    },

    grid: {
      left: 92,
      right: 24,
      top: 24,
      bottom: 44,
      height: 120,
    },

    dataZoom: [
      {
        type: 'slider',
        height: 22,
        bottom: 8,
        borderColor: 'transparent',
        backgroundColor: '#eff2f7',
        fillerColor: 'rgba(3, 28, 154, 0.15)',
        showDataShadow: false,
        showDetail: false,
        brushSelect: false,
      },
      {
        type: 'inside',
      },
    ],

    xAxis: {
      type: 'time',
      min: start,
      max: end,
      axisLabel: {
        color: '#738098',
        fontSize: 12,
        fontFamily: FONT_FAMILY,
        hideOverlap: true,
      },
      axisLine: {
        lineStyle: {
          color: '#dbe2ee',
        },
      },
      axisTick: {
        show: false,
      },
      splitLine: {
        show: true,
        lineStyle: {
          color: '#edf0f5',
        },
      },
    },

    yAxis: {
      type: 'category',
      inverse: true,
      data: EVENT_CATEGORIES.map((category) => category.label),
      axisLabel: {
        color: '#738098',
        fontSize: 12,
        fontFamily: FONT_FAMILY,
      },
      axisLine: {
        show: false,
      },
      axisTick: {
        show: false,
      },
      splitLine: {
        show: true,
        lineStyle: {
          color: '#edf0f5',
        },
      },
    },

    series: [
      {
        type: 'custom',
        name: 'События приложения',
        renderItem: renderAppEventItem,
        encode: {
          x: [1, 2],
          y: 0,
        },
        data: events.map((event) => {
          const categoryIndex = EVENT_CATEGORIES.findIndex(
            (category) => category.key === event.category,
          )

          return {
            value: [categoryIndex, event.start, event.end],
            itemStyle: {
              color: event.color,
            },
            meta: event,
          }
        }),
      },
    ],
  }
}

export function DeviceAppsAccordion({
  packages,
  period,
  isLoading = false,
  isError = false,
}: DeviceAppsAccordionProps) {
  const [openedApps, setOpenedApps] = useState<string[]>([])

  const apps = useMemo(() => {
    return packages
      .map((app) => {
        const usagePercent = getUsagePercent(app, period)
        const events = createMockEvents(app, period, usagePercent)

        return {
          app,
          usagePercent,
          events,
        }
      })
      .sort((a, b) => b.usagePercent - a.usagePercent)
  }, [packages, period])

  return (
    <section className="device-apps">
      <div className="device-apps__header">
        <div>
          <h3>Приложения</h3>
          <p>Использование приложений за выбранный период</p>
        </div>

        {isLoading && <span>Загрузка...</span>}
        {isError && <span className="is-error">Не удалось загрузить</span>}
      </div>

      {!isLoading && apps.length === 0 && (
        <EmptyState
          className="device-apps__empty"
          variant="no-items"
          title="Данных по приложениям пока нет"
          description="На устройстве пока нет данных по использованию приложений за выбранный период."
        />
      )}

      {apps.length > 0 && (
        <Accordion.Root
          className="device-apps__list"
          type="multiple"
          value={openedApps}
          onValueChange={setOpenedApps}
        >
          {apps.map(({ app, usagePercent, events }) => {
            const appName = getAppName(app)
            const appKey = `${app.packageName}-${app.version ?? app.versionName ?? appName}`
            const { Icon, modifier } = getAppVisual(app)
            const isOpen = openedApps.includes(appKey)

            return (
              <Accordion.Item
                key={appKey}
                className="device-app-item"
                value={appKey}
              >
                <Accordion.Header className="device-app-item__header">
                  <Accordion.Trigger className="device-app-item__trigger">
                    <div className="device-app-item__main">
                      <span className={`device-app-item__icon ${modifier}`}>
                        <Icon size={22} />
                      </span>

                      <span className="device-app-item__info">
                        <strong>{appName}</strong>
                        <small>
                          {app.packageName}
                          {app.versionName ? ` · v${app.versionName}` : ''}
                        </small>
                      </span>
                    </div>

                    <div className="device-app-item__right">
                      <div className="device-app-item__usage">
                        <strong>{usagePercent}%</strong>
                        <span>за период</span>
                      </div>

                      <ChevronDown className="device-app-item__chevron" size={20} />
                    </div>
                  </Accordion.Trigger>
                </Accordion.Header>

                <Accordion.Content className="device-app-item__content">
                  <div className="device-app-item__body">
                    <div className="device-app-item__meta">
                      <span>
                        Категория: <b>{app.category || 'Не указана'}</b>
                      </span>

                      <span>
                        Установлено: <b>{formatDate(app.installDate)}</b>
                      </span>

                      <span>
                        Код версии: <b>{app.version ?? '—'}</b>
                      </span>
                    </div>

                    <div className="device-app-chart">
                      {isOpen && (
                        <ReactECharts
                          option={getChartOption(events, period)}
                          notMerge
                          lazyUpdate
                        />
                      )}
                    </div>
                  </div>
                </Accordion.Content>
              </Accordion.Item>
            )
          })}
        </Accordion.Root>
      )}
    </section>
  )
}
