import { useQuery, useQueryClient } from '@tanstack/react-query'
import { AlertTriangle, RefreshCw } from 'lucide-react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import type { StorageArea, StorageAreaDto } from '@/shared/api/types'
import { qk } from '@/shared/api/query-keys'
import { cn } from '@/shared/lib/cn'
import { Button } from '@/shared/ui/button'
import { Card, CardContent } from '@/shared/ui/card'
import { StoragePanelSkeleton } from './StoragePanelSkeleton'
import { getStorageStats } from './api'
import { formatBytes, percentOf } from './format'

/** Доли тома: своё, чужое и свободное. Свободное краснеет у порога — там его и надо заметить. */
const VOLUME_COLORS = {
  ours: 'bg-primary',
  foreign: 'bg-muted-foreground/50',
  free: 'bg-emerald-500/25',
  lowFree: 'bg-destructive/50',
}

/** Цвета областей — те же и в полосе, и в списке: полоса без легенды не читается. */
const AREA_COLORS: Record<StorageArea, string> = {
  Assets: 'bg-emerald-500',
  BumperAssets: 'bg-violet-500',
  Originals: 'bg-sky-500',
  Inbox: 'bg-amber-500',
  ManualInbox: 'bg-orange-500',
  Uploads: 'bg-rose-500',
  BumperSources: 'bg-fuchsia-500',
  Images: 'bg-teal-500',
  Other: 'bg-muted-foreground',
}

/** Порядок вывода: сверху то, что реально занимает место, снизу — служебное. */
const AREA_ORDER: StorageArea[] = [
  'Assets',
  'BumperAssets',
  'Originals',
  'Inbox',
  'ManualInbox',
  'Uploads',
  'BumperSources',
  'Images',
  'Other',
]

/**
 * Чем занят диск. Два разных вопроса на одном экране: сколько осталось на томе (его делят все,
 * включая базу и систему) и на что ушло место у самого TeleWave.
 */
export function StoragePanel() {
  const { t } = useTranslation()
  const queryClient = useQueryClient()
  const [refreshing, setRefreshing] = useState(false)

  const { data, isLoading } = useQuery({
    queryKey: qk.storage.all,
    queryFn: () => getStorageStats(),
  })

  const refresh = async () => {
    setRefreshing(true)
    try {
      const fresh = await getStorageStats(true)
      queryClient.setQueryData(qk.storage.all, fresh)
    } finally {
      setRefreshing(false)
    }
  }

  if (isLoading || !data) return <StoragePanelSkeleton />

  const { volumeTotalBytes: total, volumeFreeBytes: free, storageBytes: storage } = data
  const volumeKnown = total > 0
  const used = Math.max(0, total - free)
  // На томе есть и чужое: база, система, чей-то бэкап. Показываем это отдельной долей, иначе
  // «занято 80%» выглядит как вина медиатеки.
  const foreign = Math.max(0, used - storage)
  const lowSpace = volumeKnown && free < data.minFreeSpaceBytes

  const areas = [...data.areas]
    .filter((a) => a.bytes > 0)
    .sort((a, b) => AREA_ORDER.indexOf(a.area) - AREA_ORDER.indexOf(b.area))

  return (
    <div className="flex flex-col gap-4">
      <Card>
        <CardContent className="flex flex-col gap-4">
          <div className="flex flex-wrap items-center justify-between gap-2">
            <div className="flex flex-col">
              <span className="text-sm font-medium">{t('admin.storage.volume')}</span>
              <span className="font-mono text-xs text-muted-foreground">{data.rootPath}</span>
            </div>
            <div className="flex items-center gap-3">
              <span className="text-xs text-muted-foreground">
                {t('admin.storage.computedAt', {
                  time: new Date(data.computedAt).toLocaleTimeString([], {
                    hour: '2-digit',
                    minute: '2-digit',
                  }),
                })}
              </span>
              <Button size="sm" variant="outline" disabled={refreshing} onClick={refresh}>
                <RefreshCw className={cn('h-4 w-4', refreshing && 'animate-spin')} />
                {t('admin.storage.recount')}
              </Button>
            </div>
          </div>

          {volumeKnown ? (
            <>
              {/* Полоса тома: своё, чужое, свободное. Свободное — такая же доля, а не пустой
                  остаток полосы: иначе непонятно, кончился ли диск или просто кончилась заливка. */}
              <div className="flex h-4 overflow-hidden rounded-full bg-muted/40">
                <div
                  className={VOLUME_COLORS.ours}
                  style={{ width: `${percentOf(storage, total)}%` }}
                  title={`${t('admin.storage.ours')} · ${formatBytes(storage)}`}
                />
                <div
                  className={VOLUME_COLORS.foreign}
                  style={{ width: `${percentOf(foreign, total)}%` }}
                  title={`${t('admin.storage.foreign')} · ${formatBytes(foreign)}`}
                />
                <div
                  className={lowSpace ? VOLUME_COLORS.lowFree : VOLUME_COLORS.free}
                  style={{ width: `${percentOf(free, total)}%` }}
                  title={`${t('admin.storage.free')} · ${formatBytes(free)}`}
                />
              </div>

              <div className="grid gap-3 text-sm sm:grid-cols-4">
                <Metric label={t('admin.storage.totalVolume')} value={formatBytes(total)} />
                <Metric
                  label={t('admin.storage.ours')}
                  value={formatBytes(storage)}
                  hint={`${percentOf(storage, total).toFixed(1)}%`}
                  accent="text-primary"
                  swatch={VOLUME_COLORS.ours}
                />
                <Metric
                  label={t('admin.storage.foreign')}
                  value={formatBytes(foreign)}
                  hint={t('admin.storage.foreignHint')}
                  swatch={VOLUME_COLORS.foreign}
                />
                <Metric
                  label={t('admin.storage.free')}
                  value={formatBytes(free)}
                  hint={`${percentOf(free, total).toFixed(1)}%`}
                  accent={lowSpace ? 'text-destructive' : 'text-emerald-500'}
                  swatch={lowSpace ? VOLUME_COLORS.lowFree : VOLUME_COLORS.free}
                />
              </div>
            </>
          ) : (
            <p className="text-sm text-muted-foreground">{t('admin.storage.volumeUnknown')}</p>
          )}

          {lowSpace && (
            <div className="flex items-center gap-2 rounded-md border border-destructive/50 bg-destructive/10 px-3 py-2 text-sm">
              <AlertTriangle className="h-4 w-4 shrink-0 text-destructive" />
              <span>
                {t('admin.storage.lowSpace', { threshold: formatBytes(data.minFreeSpaceBytes) })}
              </span>
            </div>
          )}
        </CardContent>
      </Card>

      <Card>
        <CardContent className="flex flex-col gap-4">
          <div className="flex flex-wrap items-baseline justify-between gap-2">
            <span className="text-sm font-medium">{t('admin.storage.breakdown')}</span>
            <span className="text-xs text-muted-foreground">
              {t('admin.storage.filesTotal', { count: data.storageFiles })} · {formatBytes(storage)}
            </span>
          </div>

          {/* Полоса состава хранилища — доли областей друг относительно друга. */}
          {storage > 0 && (
            <div className="flex h-3 overflow-hidden rounded-full bg-muted/40">
              {areas.map((area) => {
                const name = t(`admin.storage.areas.${area.area}`)
                return (
                  <div
                    key={area.area}
                    className={AREA_COLORS[area.area]}
                    style={{ width: `${percentOf(area.bytes, storage)}%` }}
                    title={`${name} · ${formatBytes(area.bytes)}`}
                  />
                )
              })}
            </div>
          )}

          <div className="flex flex-col divide-y divide-border">
            {areas.map((area) => (
              <AreaRow key={area.area} area={area} total={storage} />
            ))}
            {areas.length === 0 && (
              <p className="text-sm text-muted-foreground">{t('admin.storage.empty')}</p>
            )}
          </div>
        </CardContent>
      </Card>
    </div>
  )
}

function Metric({
  label,
  value,
  hint,
  accent,
  swatch,
}: Readonly<{
  label: string
  value: string
  hint?: string
  accent?: string
  /** Цвет доли на полосе тома: без него подпись и сегмент не связать глазами. */
  swatch?: string
}>) {
  return (
    <div className="flex flex-col">
      <span className="flex items-center gap-1.5 text-xs uppercase tracking-wide text-muted-foreground">
        {swatch && <span className={cn('h-2.5 w-2.5 shrink-0 rounded-sm', swatch)} />}
        {label}
      </span>
      <span className={cn('font-mono text-lg tabular-nums', accent)}>{value}</span>
      {hint && <span className="text-xs text-muted-foreground">{hint}</span>}
    </div>
  )
}

function AreaRow({ area, total }: Readonly<{ area: StorageAreaDto; total: number }>) {
  const { t } = useTranslation()
  const share = percentOf(area.bytes, total)

  return (
    <div className="flex flex-wrap items-center gap-x-3 gap-y-1 py-2 text-sm">
      <span className={cn('h-3 w-3 shrink-0 rounded-sm', AREA_COLORS[area.area])} />
      <div className="flex min-w-48 flex-col">
        <span>{t(`admin.storage.areas.${area.area}`)}</span>
        <span className="text-xs text-muted-foreground">
          {t(`admin.storage.areaHints.${area.area}`)}
        </span>
      </div>
      <span className="ml-auto font-mono tabular-nums">{formatBytes(area.bytes)}</span>
      <span className="w-14 text-right font-mono text-xs tabular-nums text-muted-foreground">
        {share.toFixed(1)}%
      </span>
      <span className="w-24 text-right text-xs text-muted-foreground">
        {t('admin.storage.files', { count: area.files })}
      </span>
    </div>
  )
}
