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

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

const ACTIONS: AuditAction[] = ['CREATE', 'UPDATE', 'CLOSE']

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

function AuditPage() {
  const { t } = useTranslation()
  const dictionariesQuery = useDictionaries()

  const [filters, setFilters] = useState<AuditFilters>({ page: 0, size: 50 })
  const [draft, setDraft] = useState<AuditFilters>(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],
  )

  const apply = () => setFilters({ ...draft, page: 0 })
  const reset = () => {
    const empty: AuditFilters = { page: 0, size: filters.size ?? 50 }
    setDraft(empty)
    setFilters(empty)
  }

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

  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}
      />

      {error ? (
        <Alert variant="error" title={t('error.failed')}>
          {String(error)}
        </Alert>
      ) : 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={() =>
              setFilters((f) => ({ ...f, page: Math.max(0, (f.page ?? 0) - 1) }))
            }
            onNext={() =>
              setFilters((f) => ({ ...f, page: (f.page ?? 0) + 1 }))
            }
            onSizeChange={(size) =>
              setFilters((f) => ({ ...f, page: 0, size }))
            }
          />
        </>
      )}
    </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)

  return (
    <div className="bg-white border border-regolith 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)}
        />
        <TextInput
          label={t('audit.filter.businessKey')}
          value={filters.businessKey ?? ''}
          onChange={(e) => set('businessKey', e.target.value || undefined)}
        />
        <DatePicker
          label={t('audit.filter.from')}
          value={fromDate}
          onChange={(d) =>
            set(
              'from',
              d
                ? `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(
                    d.getDate(),
                  ).padStart(2, '0')}T00:00:00${localTzOffset(d)}`
                : undefined,
            )
          }
        />
        <DatePicker
          label={t('audit.filter.to')}
          value={toDate}
          onChange={(d) =>
            set(
              'to',
              d
                ? `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(
                    d.getDate(),
                  ).padStart(2, '0')}T23:59:59${localTzOffset(d)}`
                : undefined,
            )
          }
        />
      </div>
      <div className="flex justify-end gap-2 pt-2 border-t border-regolith">
        <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-2xs 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="font-mono text-xs">{row.businessKey ?? '—'}</span>
        </TableCell>
        <TableCell>{row.userId ?? 'anonymous'}</TableCell>
        <TableCell>
          {row.userScope ? <Badge variant="info">{row.userScope}</Badge> : null}
        </TableCell>
        <TableCell>
          <span
            className="font-mono text-2xs text-carbon/70"
            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-regolith/30 rounded p-3">
              <div>
                <div className="text-2xs uppercase tracking-label text-carbon/70 mb-1">
                  {t('audit.diff.before')}
                </div>
                <pre className="text-2xs font-mono whitespace-pre-wrap break-all bg-white border border-regolith rounded p-2 max-h-60 overflow-auto">
                  {row.payloadBefore
                    ? JSON.stringify(row.payloadBefore, null, 2)
                    : '—'}
                </pre>
              </div>
              <div>
                <div className="text-2xs uppercase tracking-label text-carbon/70 mb-1">
                  {t('audit.diff.after')}
                </div>
                <pre className="text-2xs font-mono whitespace-pre-wrap break-all bg-white border border-regolith 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-2xs text-carbon/70 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-sm">
        <span className="text-carbon/70">{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-sm">
        <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>
  )
}
