import {
  memo,
  type ChangeEvent,
  useCallback,
  useDeferredValue,
  useMemo,
  useState,
} from 'react'
import { useQuery } from '@apollo/client/react'
import * as Accordion from '@radix-ui/react-accordion'
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
import SimpleBar from 'simplebar-react'
import 'simplebar-react/dist/simplebar.min.css'
import {
  AppWindow,
  ArrowUpDown,
  CalendarDays,
  ChevronDown,
  Download,
  Hash,
  Package,
  Search,
  Square,
  Tags,
  Trash2,
} from 'lucide-react'

import { GET_PHONE_PACKAGES_QUERY } from '../../../../entities/device/api/device.graphql'
import type {
  DevicePackage,
  GetPhonePackagesData,
  GetPhonePackagesVariables,
} from '../../../../entities/device/model/types'
import { ListSkeleton } from '../../../../shared/ui/ListSkeleton/ListSkeleton'
import { RevealContent } from '../../../../shared/ui/RevealContent/RevealContent'
import { EmptyState } from '../../../../shared/ui/EmptyState/EmptyState'

import './DeviceAppsView.scss'

type DeviceAppsViewProps = {
  deviceId: number
  deviceName: string
  onBack: () => void
}

type DeviceAppIconProps = {
  app: DevicePackage
}

type DeviceAppsListProps = {
  apps: DevicePackage[]
  openedApp?: string
  onOpenedAppChange: (value: string) => void
}

type DeviceAppDetailsProps = {
  app: DevicePackage
}

type DeviceAppListItemProps = {
  app: DevicePackage
  isOpen: boolean
}

type AppsSortValue =
  | 'name-asc'
  | 'name-desc'
  | 'package-asc'
  | 'install-desc'
  | 'install-asc'

type AppsSortOption = {
  value: AppsSortValue
  label: string
}

const appsSortOptions: AppsSortOption[] = [
  {
    value: 'name-asc',
    label: 'Название А-Я',
  },
  {
    value: 'name-desc',
    label: 'Название Я-А',
  },
  {
    value: 'package-asc',
    label: 'Пакет А-Я',
  },
  {
    value: 'install-desc',
    label: 'Сначала новые',
  },
  {
    value: 'install-asc',
    label: 'Сначала старые',
  },
]

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

function getAppCategory(app: DevicePackage) {
  if (!app.category || app.category === 'Undefined') return 'Не указана'

  return app.category
}

function getAppVersion(app: DevicePackage) {
  if (app.versionName && app.version) {
    return `${app.versionName} (${app.version})`
  }

  return app.versionName || app.version || 'Не указана'
}

function formatInstallDate(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 compareText(a: string, b: string) {
  return a.localeCompare(b, 'ru', {
    sensitivity: 'base',
    numeric: true,
  })
}

function compareInstallDate(
  a: DevicePackage,
  b: DevicePackage,
  direction: 'ASC' | 'DESC',
) {
  const firstDate = a.installDate ?? 0
  const secondDate = b.installDate ?? 0

  if (firstDate === secondDate) {
    return compareText(getAppName(a), getAppName(b))
  }

  return direction === 'ASC'
    ? firstDate - secondDate
    : secondDate - firstDate
}

const DeviceAppIcon = memo(function DeviceAppIcon({ app }: DeviceAppIconProps) {
  const [isImageFailed, setIsImageFailed] = useState(false)
  const appName = getAppName(app)

  if (app.iconUrl && !isImageFailed) {
    return (
      <span className="device-apps-view__app-icon">
        <img
          src={app.iconUrl}
          alt=""
          onError={() => setIsImageFailed(true)}
        />
      </span>
    )
  }

  return (
    <span className="device-apps-view__app-icon is-fallback">
      <AppWindow size={22} aria-label={appName} />
    </span>
  )
})

const DeviceAppDetails = memo(function DeviceAppDetails({
  app,
}: DeviceAppDetailsProps) {
  return (
    <div className="device-apps-view__details">
      <div className="device-apps-view__details-grid">
        <div className="device-apps-view__detail-card">
          <span className="device-apps-view__detail-icon">
            <Package size={17} />
          </span>
          <span className="device-apps-view__detail-text">
            <small>Пакет</small>
            <b>{app.packageName}</b>
          </span>
        </div>

        <div className="device-apps-view__detail-card">
          <span className="device-apps-view__detail-icon">
            <Tags size={17} />
          </span>
          <span className="device-apps-view__detail-text">
            <small>Категория</small>
            <b>{getAppCategory(app)}</b>
          </span>
        </div>

        <div className="device-apps-view__detail-card">
          <span className="device-apps-view__detail-icon">
            <CalendarDays size={17} />
          </span>
          <span className="device-apps-view__detail-text">
            <small>Установлено</small>
            <b>{formatInstallDate(app.installDate)}</b>
          </span>
        </div>

        <div className="device-apps-view__detail-card">
          <span className="device-apps-view__detail-icon">
            <Hash size={17} />
          </span>
          <span className="device-apps-view__detail-text">
            <small>Версия</small>
            <b>{getAppVersion(app)}</b>
          </span>
        </div>
      </div>

      <div className="device-apps-view__stats">
        <h3>Статистика</h3>
        <p>Сводных данных по использованию пока нет</p>
      </div>

      <div className="device-apps-view__actions">
        <button type="button" disabled>
          <Square size={17} />
          Остановить
        </button>

        <button type="button" disabled>
          <Trash2 size={17} />
          Удалить
        </button>

        <button type="button" disabled>
          <Download size={17} />
          Установить обновление
        </button>
      </div>
    </div>
  )
})

const DeviceAppListItem = memo(function DeviceAppListItem({
  app,
  isOpen,
}: DeviceAppListItemProps) {
  const appName = getAppName(app)
  const appKey = app.packageName || appName

  return (
    <Accordion.Item
      className="device-apps-view__item"
      value={appKey}
    >
      <Accordion.Header className="device-apps-view__item-header">
        <Accordion.Trigger className="device-apps-view__trigger">
          <span className="device-apps-view__app-main">
            <DeviceAppIcon app={app} />

            <span className="device-apps-view__app-text">
              <strong>{appName}</strong>
              <small>{app.packageName}</small>
            </span>
          </span>

          <span className="device-apps-view__app-meta">
            <span>{getAppCategory(app)}</span>
            <ChevronDown
              className="device-apps-view__chevron"
              size={20}
            />
          </span>
        </Accordion.Trigger>
      </Accordion.Header>

      <Accordion.Content className="device-apps-view__content">
        {isOpen && <DeviceAppDetails app={app} />}
      </Accordion.Content>
    </Accordion.Item>
  )
})

const DeviceAppsList = memo(function DeviceAppsList({
  apps,
  openedApp,
  onOpenedAppChange,
}: DeviceAppsListProps) {
  return (
    <Accordion.Root
      className="device-apps-view__list"
      type="single"
      collapsible
      value={openedApp}
      onValueChange={onOpenedAppChange}
    >
      {apps.map((app) => {
        const appName = getAppName(app)
        const appKey = app.packageName || appName

        return (
          <DeviceAppListItem
            key={appKey}
            app={app}
            isOpen={openedApp === appKey}
          />
        )
      })}
    </Accordion.Root>
  )
})

export function DeviceAppsView({
  deviceId,
  onBack,
}: DeviceAppsViewProps) {
  const [search, setSearch] = useState('')
  const deferredSearch = useDeferredValue(search)
  const [openedApp, setOpenedApp] = useState<string>()
  const [sortValue, setSortValue] = useState<AppsSortValue>('name-asc')

  const { data, loading, error } = useQuery<
    GetPhonePackagesData,
    GetPhonePackagesVariables
  >(GET_PHONE_PACKAGES_QUERY, {
    variables: {
      phoneId: String(deviceId),
    },
    fetchPolicy: 'network-only',
  })

  const packages = useMemo(
    () => data?.getPhonePackages ?? [],
    [data?.getPhonePackages],
  )
  const searchablePackages = useMemo(() => {
    return packages.map((app) => ({
      app,
      searchText: [
        app.name,
        app.packageName,
        app.category,
        app.versionName,
      ]
        .filter(Boolean)
        .join(' ')
        .toLowerCase(),
    }))
  }, [packages])

  const normalizedSearch = useMemo(() => {
    return deferredSearch.trim().toLowerCase()
  }, [deferredSearch])
  const selectedSort = useMemo(() => {
    return (
      appsSortOptions.find((option) => option.value === sortValue) ??
      appsSortOptions[0]
    )
  }, [sortValue])

  const handleSearchChange = useCallback((
    event: ChangeEvent<HTMLInputElement>,
  ) => {
    setSearch(event.target.value)
  }, [])

  const filteredPackages = useMemo(() => {
    return searchablePackages
      .filter(({ searchText }) => {
        if (!normalizedSearch) return true

        return searchText.includes(normalizedSearch)
      })
      .map(({ app }) => app)
      .sort((a, b) => {
        if (sortValue === 'name-desc') {
          return compareText(getAppName(b), getAppName(a))
        }

        if (sortValue === 'package-asc') {
          return compareText(a.packageName, b.packageName)
        }

        if (sortValue === 'install-desc') {
          return compareInstallDate(a, b, 'DESC')
        }

        if (sortValue === 'install-asc') {
          return compareInstallDate(a, b, 'ASC')
        }

        return compareText(getAppName(a), getAppName(b))
      })
  }, [normalizedSearch, searchablePackages, sortValue])

  return (
    <section className="device-apps-view">
      <div className="device-apps-view__header">
        <button
          className="device-apps-view__back"
          type="button"
          onClick={onBack}
        >
          <svg viewBox="0 0 24 24" aria-hidden="true">
            <path
              d="M15 18L9 12L15 6"
              fill="none"
              stroke="currentColor"
              strokeLinecap="round"
              strokeLinejoin="round"
              strokeWidth="2"
            />
          </svg>
          К устройству
        </button>

        <div className="device-apps-view__title">
          <h2>Приложения устройства</h2>
          {/* <span>{deviceName}</span> */}
        </div>

        <button className="device-apps-view__install" type="button" disabled>
          <Download size={18} />
          Установить
        </button>
      </div>

      <div className="device-apps-view__toolbar">
        <label className="device-apps-view__search">
          <Search size={18} />
          <input
            value={search}
            onChange={handleSearchChange}
            placeholder="Поиск по названию или пакету"
          />
        </label>

        <div className="device-apps-view__toolbar-right">
          <DropdownMenu.Root>
            <DropdownMenu.Trigger asChild>
              <button className="device-apps-view__sort" type="button">
                <span>{selectedSort.label}</span>
                <ArrowUpDown size={17} />
              </button>
            </DropdownMenu.Trigger>

            <DropdownMenu.Portal>
              <DropdownMenu.Content
                className="device-apps-view__sort-menu"
                align="end"
                sideOffset={8}
              >
                {appsSortOptions.map((option) => (
                  <DropdownMenu.Item
                    key={option.value}
                    className="device-apps-view__sort-item"
                    onSelect={() => setSortValue(option.value)}
                  >
                    <span>{option.label}</span>

                    {selectedSort.value === option.value && (
                      <span className="device-apps-view__sort-check" />
                    )}
                  </DropdownMenu.Item>
                ))}
              </DropdownMenu.Content>
            </DropdownMenu.Portal>
          </DropdownMenu.Root>

          <div className="device-apps-view__summary">
            <span>
              Всего <b>{packages.length}</b>
            </span>
            <span>
              Показано <b>{filteredPackages.length}</b>
            </span>
          </div>
        </div>
      </div>

      <div className="device-apps-view__panel">
        <SimpleBar className="device-apps-view__scroll">
          <RevealContent
            className="device-apps-view__reveal"
            loading={loading}
            fallback={<ListSkeleton />}
          >
            {error && (
              <div className="device-apps-view__state is-error">
                Не удалось загрузить список приложений
              </div>
            )}

            {!error && filteredPackages.length === 0 && (
              <EmptyState
                className="device-apps-view__state"
                variant={normalizedSearch ? 'not-found' : 'no-items'}
                title={
                  normalizedSearch
                    ? 'Не найдено результатов'
                    : 'Приложений пока нет'
                }
                description={
                  normalizedSearch
                    ? 'По заданному поиску не найдено ни одного приложения. Попробуйте изменить запрос.'
                    : 'На устройстве пока нет данных по установленным приложениям.'
                }
              />
            )}

            {!error && filteredPackages.length > 0 && (
              <DeviceAppsList
                apps={filteredPackages}
                openedApp={openedApp}
                onOpenedAppChange={setOpenedApp}
              />
            )}
          </RevealContent>
        </SimpleBar>
      </div>
    </section>
  )
}
