import { useEffect, useMemo, useState } from 'react'
import { createFileRoute, useNavigate } from '@tanstack/react-router'
import { useTranslation } from 'react-i18next'
import {
  Badge,
  Button,
  DateRangePicker,
  EmptyState,
  IconButton,
  LoadingBlock,
  PageHeader,
  QueryErrorState,
  SingleSelect,
  Table,
  TableBody,
  TableCell,
  TableEmpty,
  TableHead,
  TableHeaderCell,
  TableRow,
  TextInput,
} from '@/ui'
import {
  ArrowsClockwiseIcon,
  CaretDownIcon,
  CaretRightIcon,
  DownloadIcon,
  XIcon,
} from '@phosphor-icons/react'
import { buildAuditExportUrl, useAudit, useDictionaries } from '@/api/queries'
import { UserCell } from '@/lib/useUserDisplay'
import type { AuditAction, AuditEntry, AuditFilters } from '@/api/client'
import { localTzOffset, parseFormDate } from '@/lib/dates'

const ACTIONS: AuditAction[] = ['CREATE', 'UPDATE', 'CLOSE']
const ACTION_VALUES: ReadonlySet<AuditAction> = new Set(ACTIONS)

const PAGE_SIZE_OPTIONS = [
  { id: '50', label: '50' },
  { id: '100', label: '100' },
  { id: '200', label: '200' },
]

const PAGE_SIZE_VALUES = new Set([50, 100, 200])

/**
 * URL search params для /audit. Сериализуем фильтры чтобы:
 * - reload страницы не сбрасывал фильтр
 * - админ мог поделиться ссылкой "audit за вчера на ground_station"
 * - browser back/forward работал по фильтрам как навигация
 *
 * Все поля optional. Validate'им: action — enum, page/size — number.
 * Остальное — строка как есть (длина не лимитируем — backend всё равно режет).
 */
type AuditSearch = {
  dict?: string
  action?: AuditAction
  user?: string
  bk?: string
  from?: string
  to?: string
  page?: number
  size?: number
}

const validateSearch = (raw: Record<string, unknown>): AuditSearch => {
  const out: AuditSearch = {}
  if (typeof raw.dict === 'string' && raw.dict.length > 0) out.dict = raw.dict
  if (typeof raw.action === 'string' && ACTION_VALUES.has(raw.action as AuditAction)) {
    out.action = raw.action as AuditAction
  }
  if (typeof raw.user === 'string' && raw.user.length > 0) out.user = raw.user
  if (typeof raw.bk === 'string' && raw.bk.length > 0) out.bk = raw.bk
  if (typeof raw.from === 'string' && raw.from.length > 0) out.from = raw.from
  if (typeof raw.to === 'string' && raw.to.length > 0) out.to = raw.to
  if (typeof raw.page === 'number' && Number.isInteger(raw.page) && raw.page >= 0) {
    out.page = raw.page
  } else if (typeof raw.page === 'string') {
    const p = Number.parseInt(raw.page, 10)
    if (Number.isInteger(p) && p >= 0) out.page = p
  }
  if (typeof raw.size === 'number' && PAGE_SIZE_VALUES.has(raw.size)) out.size = raw.size
  else if (typeof raw.size === 'string') {
    const s = Number.parseInt(raw.size, 10)
    if (PAGE_SIZE_VALUES.has(s)) out.size = s
  }
  return out
}

export const Route = createFileRoute('/audit')({
  component: AuditPage,
  validateSearch,
})

/** URL search → AuditFilters (API contract). */
const searchToFilters = (s: AuditSearch): AuditFilters => ({
  dictionaryName: s.dict,
  action: s.action ?? '',
  userId: s.user,
  businessKey: s.bk,
  from: s.from,
  to: s.to,
  page: s.page ?? 0,
  size: s.size ?? 50,
})

/** AuditFilters → URL search (для navigate({search})). undefined дропаем. */
const filtersToSearch = (f: AuditFilters): AuditSearch => {
  const out: AuditSearch = {}
  if (f.dictionaryName) out.dict = f.dictionaryName
  if (f.action) out.action = f.action as AuditAction
  if (f.userId) out.user = f.userId
  if (f.businessKey) out.bk = f.businessKey
  if (f.from) out.from = f.from
  if (f.to) out.to = f.to
  if (f.page && f.page > 0) out.page = f.page
  if (f.size && f.size !== 50) out.size = f.size
  return out
}

function AuditPage() {
  const { t } = useTranslation()
  const urlSearch = Route.useSearch()
  const navigate = useNavigate({ from: Route.fullPath })
  const dictionariesQuery = useDictionaries()

  // filters: derived из URL — single source of truth.
  // draft: локальный staging фильтра до Apply — не идёт в URL до явного push.
  const filters = useMemo(() => searchToFilters(urlSearch), [urlSearch])
  const [draft, setDraft] = useState<AuditFilters>(filters)

  // Sync draft когда URL поменялся снаружи (browser back/forward, ссылка).
  useEffect(() => {
    setDraft(filters)
  }, [filters])

  const { data, isLoading, error, refetch, isFetching } = useAudit(filters)

  const dictionaryOptions = useMemo(() => {
    const opts: { id: string; label: string }[] = [{ id: '', label: '—' }]
    for (const d of dictionariesQuery.data ?? []) {
      opts.push({ id: d.name, label: d.displayName ?? d.name })
    }
    return opts
  }, [dictionariesQuery.data])

  const actionOptions = useMemo(
    () => [
      { id: '', label: '—' },
      ...ACTIONS.map((a) => ({ id: a, label: t(`audit.action.${a}`) })),
    ],
    [t],
  )

  // navigate с replace=false — каждый Apply пишет history entry.
  // Reset → пустой URL без params.
  const apply = () =>
    navigate({ search: filtersToSearch({ ...draft, page: 0 }), replace: false })

  const reset = () => {
    setDraft({ page: 0, size: filters.size ?? 50 })
    navigate({
      search: filters.size && filters.size !== 50 ? { size: filters.size } : {},
      replace: false,
    })
  }

  const setPage = (page: number) =>
    navigate({ search: filtersToSearch({ ...filters, page }), replace: false })

  const setSize = (size: number) =>
    navigate({ search: filtersToSearch({ ...filters, page: 0, size }), replace: false })

  const totalPages = data?.totalPages ?? 0
  const currentPage = data?.number ?? 0

  // Активные фильтры для chip-list. Action='' → нет фильтра.
  // page/size исключаем — это пагинация, не фильтр.
  const activeFilters = useMemo(() => {
    const chips: { key: keyof AuditFilters; label: string; value: string }[] = []
    if (filters.dictionaryName) {
      const dict = dictionariesQuery.data?.find((d) => d.name === filters.dictionaryName)
      chips.push({
        key: 'dictionaryName',
        label: t('audit.filter.dictionary'),
        value: dict?.displayName ?? filters.dictionaryName,
      })
    }
    if (filters.action) {
      chips.push({
        key: 'action',
        label: t('audit.filter.action'),
        value: t(`audit.action.${filters.action}`),
      })
    }
    if (filters.userId) {
      chips.push({ key: 'userId', label: t('audit.filter.user'), value: filters.userId })
    }
    if (filters.businessKey) {
      chips.push({
        key: 'businessKey',
        label: t('audit.filter.businessKey'),
        value: filters.businessKey,
      })
    }
    if (filters.from) {
      chips.push({ key: 'from', label: t('audit.filter.from'), value: filters.from.slice(0, 10) })
    }
    if (filters.to) {
      chips.push({ key: 'to', label: t('audit.filter.to'), value: filters.to.slice(0, 10) })
    }
    return chips
  }, [filters, dictionariesQuery.data, t])

  const removeFilter = (key: keyof AuditFilters) => {
    const next: AuditFilters = { ...filters, page: 0 }
    if (key === 'action') next.action = ''
    else next[key] = undefined
    navigate({ search: filtersToSearch(next), replace: false })
  }

  return (
    <div className="space-y-6">
      <PageHeader
        title={t('audit.title')}
        description={t('audit.subtitle')}
        actions={
          <div className="flex gap-2">
            <Button
              type="button"
              variant="secondary"
              leftIcon={<ArrowsClockwiseIcon size={16} />}
              onClick={() => refetch()}
              disabled={isFetching}
            >
              {t('audit.filter.apply')}
            </Button>
            <a
              href={buildAuditExportUrl(filters)}
              target="_blank"
              rel="noreferrer"
              className="inline-flex"
            >
              <Button
                type="button"
                variant="primary"
                leftIcon={<DownloadIcon size={16} />}
              >
                {t('audit.action.export')}
              </Button>
            </a>
          </div>
        }
      />

      <FilterPanel
        filters={draft}
        onChange={setDraft}
        dictionaryOptions={dictionaryOptions}
        actionOptions={actionOptions}
        onApply={apply}
        onReset={reset}
      />

      {activeFilters.length > 0 && (
        <ActiveFilterChips filters={activeFilters} onRemove={removeFilter} />
      )}

      {error ? (
        <QueryErrorState error={error} onRetry={() => refetch()} />
      ) : isLoading ? (
        <LoadingBlock size="md" label={t('loading')} />
      ) : !data || data.content.length === 0 ? (
        <EmptyState title={t('audit.empty')} />
      ) : (
        <>
          <AuditTable rows={data.content} />
          <Pagination
            page={currentPage}
            totalPages={totalPages}
            size={filters.size ?? 50}
            onPrev={() => setPage(Math.max(0, currentPage - 1))}
            onNext={() => setPage(currentPage + 1)}
            onSizeChange={setSize}
          />
        </>
      )}
    </div>
  )
}

type ActiveFilterChipsProps = {
  filters: { key: keyof AuditFilters; label: string; value: string }[]
  onRemove: (key: keyof AuditFilters) => void
}

function ActiveFilterChips({ filters, onRemove }: ActiveFilterChipsProps) {
  const { t } = useTranslation()
  return (
    <div className="flex flex-wrap items-center gap-2">
      <span className="text-cap text-ink-2">
        {t('audit.filter.active')}
      </span>
      {filters.map((f) => (
        <button
          key={f.key}
          type="button"
          onClick={() => onRemove(f.key)}
          className={[
            'inline-flex items-center gap-1.5 px-2 py-1 rounded-sm border text-cell',
            'bg-surface border-line hover:border-ink/40 hover:bg-line/30',
            'transition-colors group',
          ].join(' ')}
          title={t('audit.filter.removeChip')}
        >
          <span className="text-ink-2">{f.label}:</span>
          <span className="font-medium text-black">{f.value}</span>
          <XIcon size={12} className="text-mute group-hover:text-mars" />
        </button>
      ))}
    </div>
  )
}

type FilterPanelProps = {
  filters: AuditFilters
  onChange: (f: AuditFilters) => void
  dictionaryOptions: { id: string; label: string }[]
  actionOptions: { id: string; label: string }[]
  onApply: () => void
  onReset: () => void
}

function FilterPanel({
  filters,
  onChange,
  dictionaryOptions,
  actionOptions,
  onApply,
  onReset,
}: FilterPanelProps) {
  const { t } = useTranslation()
  const set = <K extends keyof AuditFilters>(k: K, v: AuditFilters[K]) =>
    onChange({ ...filters, [k]: v })

  const fromDate = parseFormDate(filters.from)
  const toDate = parseFormDate(filters.to)

  // Apply на Enter в TextInput'ах — быстрее чем клик мышкой
  const handleEnter = (e: React.KeyboardEvent<HTMLInputElement>) => {
    if (e.key === 'Enter') {
      e.preventDefault()
      onApply()
    }
  }

  return (
    <div className="bg-surface border border-line rounded-lg p-4 space-y-3">
      <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
        <SingleSelect
          label={t('audit.filter.dictionary')}
          options={dictionaryOptions}
          value={filters.dictionaryName ?? ''}
          onChange={(id) => set('dictionaryName', id || undefined)}
        />
        <SingleSelect
          label={t('audit.filter.action')}
          options={actionOptions}
          value={filters.action ?? ''}
          onChange={(id) => set('action', (id as AuditAction) || '')}
        />
        <TextInput
          label={t('audit.filter.user')}
          value={filters.userId ?? ''}
          onChange={(e) => set('userId', e.target.value || undefined)}
          onKeyDown={handleEnter}
        />
        <TextInput
          label={t('audit.filter.businessKey')}
          value={filters.businessKey ?? ''}
          onChange={(e) => set('businessKey', e.target.value || undefined)}
          onKeyDown={handleEnter}
        />
        {/* DateRangePicker заменяет пару отдельных DatePicker'ов — один
          * popover с двумя месяцами, выбор start/end в одном UI. ISO sтроки
          * c local TZ offset формируются здесь, чтобы backend получал
          * правильный bounds (T00:00:00 для from, T23:59:59 для to). */}
        <div className="md:col-span-2">
          <DateRangePicker
            label={t('audit.filter.range', { defaultValue: 'Период' })}
            value={{ from: fromDate ?? undefined, to: toDate ?? undefined }}
            onChange={({ from, to }) => {
              const fromIso = from
                ? `${from.getFullYear()}-${String(from.getMonth() + 1).padStart(2, '0')}-${String(
                    from.getDate(),
                  ).padStart(2, '0')}T00:00:00${localTzOffset(from)}`
                : undefined
              const toIso = to
                ? `${to.getFullYear()}-${String(to.getMonth() + 1).padStart(2, '0')}-${String(
                    to.getDate(),
                  ).padStart(2, '0')}T23:59:59${localTzOffset(to)}`
                : undefined
              onChange({ ...filters, from: fromIso, to: toIso })
            }}
          />
        </div>
      </div>
      <div className="flex justify-end gap-2 pt-2 border-t border-line">
        <Button type="button" variant="secondary" onClick={onReset}>
          {t('audit.filter.reset')}
        </Button>
        <Button type="button" variant="primary" onClick={onApply}>
          {t('audit.filter.apply')}
        </Button>
      </div>
    </div>
  )
}

function AuditTable({ rows }: { rows: AuditEntry[] }) {
  const { t } = useTranslation()
  return (
    <Table>
      <TableHead>
        <TableRow>
          <TableHeaderCell>{t('audit.col.time')}</TableHeaderCell>
          <TableHeaderCell>{t('audit.col.action')}</TableHeaderCell>
          <TableHeaderCell>{t('audit.col.dictionary')}</TableHeaderCell>
          <TableHeaderCell>{t('audit.col.businessKey')}</TableHeaderCell>
          <TableHeaderCell>{t('audit.col.user')}</TableHeaderCell>
          <TableHeaderCell>{t('audit.col.scope')}</TableHeaderCell>
          <TableHeaderCell>{t('audit.col.trace')}</TableHeaderCell>
          <TableHeaderCell>{t('audit.col.diff')}</TableHeaderCell>
        </TableRow>
      </TableHead>
      <TableBody>
        {rows.length === 0 ? (
          <TableEmpty colSpan={8}>{t('audit.empty')}</TableEmpty>
        ) : (
          rows.map((r) => <AuditRow key={r.id} row={r} />)
        )}
      </TableBody>
    </Table>
  )
}

function AuditRow({ row }: { row: AuditEntry }) {
  const { t } = useTranslation()
  const [open, setOpen] = useState(false)
  const time = new Date(row.eventTime)
  const timeLabel = time.toLocaleString(undefined, {
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit',
  })
  const actionVariant =
    row.action === 'CREATE' ? 'success' : row.action === 'CLOSE' ? 'error' : 'info'

  return (
    <>
      <TableRow>
        <TableCell>
          <span className="text-cell tabular-nums">{timeLabel}</span>
        </TableCell>
        <TableCell>
          <Badge variant={actionVariant}>
            {t(`audit.action.${row.action}`, row.action)}
          </Badge>
        </TableCell>
        <TableCell>{row.dictionaryName ?? '—'}</TableCell>
        <TableCell>
          <span className=" text-mono">{row.businessKey ?? '—'}</span>
        </TableCell>
        <TableCell><UserCell uuid={row.userId} /></TableCell>
        <TableCell>
          {row.userScope ? <Badge variant="info">{row.userScope}</Badge> : null}
        </TableCell>
        <TableCell>
          <span
            className="text-mono text-ink-2"
            title={row.traceId ?? ''}
          >
            {row.traceId ? row.traceId.slice(0, 8) : '—'}
          </span>
        </TableCell>
        <TableCell>
          <IconButton
            type="button"
            icon={open ? <CaretDownIcon /> : <CaretRightIcon />}
            label={t('audit.action.expand')}
            onClick={() => setOpen((v) => !v)}
          />
        </TableCell>
      </TableRow>
      {open && (
        <TableRow>
          <TableCell colSpan={8}>
            <div className="grid grid-cols-1 md:grid-cols-2 gap-3 bg-line/30 rounded p-3">
              <div>
                <div className="text-cap text-ink-2 mb-1">
                  {t('audit.diff.before')}
                </div>
                <pre className="text-mono whitespace-pre-wrap break-all bg-surface border border-line rounded p-2 max-h-60 overflow-auto">
                  {row.payloadBefore
                    ? JSON.stringify(row.payloadBefore, null, 2)
                    : '—'}
                </pre>
              </div>
              <div>
                <div className="text-cap text-ink-2 mb-1">
                  {t('audit.diff.after')}
                </div>
                <pre className="text-mono whitespace-pre-wrap break-all bg-surface border border-line rounded p-2 max-h-60 overflow-auto">
                  {row.payloadAfter
                    ? JSON.stringify(row.payloadAfter, null, 2)
                    : '—'}
                </pre>
              </div>
              {(row.ipAddress || row.userAgent || row.requestId) && (
                <div className="md:col-span-2 text-cell text-ink-2 flex flex-wrap gap-x-4 gap-y-1 pt-1">
                  {row.ipAddress && <span>IP: {row.ipAddress}</span>}
                  {row.requestId && <span>req: {row.requestId}</span>}
                  {row.userAgent && (
                    <span className="truncate max-w-md">UA: {row.userAgent}</span>
                  )}
                </div>
              )}
            </div>
          </TableCell>
        </TableRow>
      )}
    </>
  )
}

type PaginationProps = {
  page: number
  totalPages: number
  size: number
  onPrev: () => void
  onNext: () => void
  onSizeChange: (size: number) => void
}

function Pagination({
  page,
  totalPages,
  size,
  onPrev,
  onNext,
  onSizeChange,
}: PaginationProps) {
  const { t } = useTranslation()
  return (
    <div className="flex items-center justify-between flex-wrap gap-3">
      <div className="flex items-center gap-2 text-body">
        <span className="text-ink-2">{t('audit.page.size')}</span>
        <SingleSelect
          options={PAGE_SIZE_OPTIONS}
          value={String(size)}
          onChange={(id) => onSizeChange(Number(id))}
        />
      </div>
      <div className="flex items-center gap-3 text-body">
        <Button
          type="button"
          variant="secondary"
          onClick={onPrev}
          disabled={page <= 0}
        >
          {t('audit.page.prev')}
        </Button>
        <span className="tabular-nums">
          {t('audit.page.of', { cur: page + 1, total: Math.max(totalPages, 1) })}
        </span>
        <Button
          type="button"
          variant="secondary"
          onClick={onNext}
          disabled={page + 1 >= totalPages}
        >
          {t('audit.page.next')}
        </Button>
      </div>
    </div>
  )
}
