'use client'

import { useState } from 'react'

import { Button, Notice, Select } from '@/components/ui/controls'
import {
  Badge,
  EmptyState,
  Panel,
  PanelBody,
  PanelHeader,
  StatCard,
} from '@/components/ui/display'
import { useI18n } from '@/components/providers/i18n-provider'
import { api } from '@/lib/api/client'
import { formatDateTime, formatNumber } from '@/lib/i18n/translate'
import type { Locale } from '@/lib/i18n/config'
import { cn } from '@/lib/cn'

/**
 * The verification board (§7, §63, §85).
 *
 * Three things on one page, in order of how uncomfortable they are:
 *
 *   1. What has been checked, and what has not. The pending count is a headline figure,
 *      not a footnote — a coverage number computed only over checked predictions is a
 *      survivorship number.
 *   2. Per-channel error for each verification, so "1.6 % out on average" cannot hide
 *      being 0.2 °C out on temperature and 14 % out on load.
 *   3. Calibration: whether an 80 %-confidence claim comes true 80 % of the time.
 */

export interface VerificationRow {
  code: string
  assetCode: string
  twinCode: string
  status: string
  horizonMin: number
  predictedAt: number
  horizonAt: number
  observedAt: number | null
  predictedRisk: number
  observedRisk: number | null
  predictedConfidence: number
  meanAbsError: number | null
  worstChannel: string | null
  worstError: number | null
  bias: number | null
  withinInterval: boolean | null
  fidelityBefore: number
  fidelityAfter: number | null
  modelVersion: string
  twinVersion: string
  channels: Array<{
    channel: string
    unit: string
    predicted: number
    observed: number
    absError: number
    pctError: number
    intervalLow: number | null
    intervalHigh: number | null
    withinInterval: boolean
  }>
}

export interface QualityView {
  sampleCount: number
  verifiedCount: number
  pendingCount: number
  expiredCount: number
  meanAbsPctError: number
  bias: number
  coveragePct: number
  calibrationGap: number
  leadMinutesMean: number
  falsePositiveRate: number
  missRate: number
  precision: number
  fidelityMean: number
  underpowered: boolean
}

export interface CalibrationView {
  bins: Array<{ lower: number; upper: number; claimed: number; observed: number; sampleCount: number }>
  meanGap: number
  bias: number
  sampleCount: number
}

const STATUS_TONE: Record<string, 'brand' | 'info' | 'accent' | 'muted'> = {
  verified: 'brand',
  pending: 'info',
  expired: 'muted',
  superseded: 'muted',
}

export function VerificationBoard({
  verifications,
  quality,
  calibration,
  series,
  twins,
  canRun,
}: {
  verifications: VerificationRow[]
  quality: QualityView
  calibration: CalibrationView
  series: Array<{
    ts: number
    meanAbsPctError: number | null
    fidelity: number | null
    predictedRisk: number
    observedRisk: number | null
  }>
  twins: Array<{ code: string; name: string; nameAr: string }>
  canRun: boolean
}) {
  const { t, locale } = useI18n()
  const [expanded, setExpanded] = useState<string | null>(null)
  const [assetCode, setAssetCode] = useState('T-108')
  const [twinCode, setTwinCode] = useState(twins[0]?.code ?? '')
  const [horizon, setHorizon] = useState('30')
  const [busy, setBusy] = useState(false)
  const [notice, setNotice] = useState<string | null>(null)

  const num = (value: number, digits = 1) =>
    formatNumber(locale, value, { maximumFractionDigits: digits })

  const record = async () => {
    setBusy(true)
    setNotice(null)
    try {
      await api.post('/api/verification', {
        assetCode,
        twinCode,
        horizonMin: Number(horizon),
      })
      setNotice(t('verification.recorded'))
      // A recorded prediction changes the pending count, which is a headline figure on
      // this page; refreshing is the honest response rather than leaving a stale number.
      window.location.reload()
    } catch (error) {
      setNotice(error instanceof Error ? error.message : t('common.error'))
    } finally {
      setBusy(false)
    }
  }

  const errorPoints = series.filter((point) => point.meanAbsPctError !== null)
  const fidelityPoints = series.filter((point) => point.fidelity !== null)

  return (
    <div className="space-y-4">
      {/* ── Headline ───────────────────────────────────────────────────────── */}
      <div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
        <StatCard
          label={t('verification.verified')}
          value={`${quality.verifiedCount} / ${quality.sampleCount}`}
          hint={`${quality.pendingCount} ${t('verification.pending').toLowerCase()}`}
        />
        <StatCard
          label={t('verification.meanError')}
          value={`${num(quality.meanAbsPctError, 2)}%`}
          hint={t('verification.worstChannel')}
        />
        <StatCard
          label={t('verification.coverage')}
          value={`${num(quality.coveragePct)}%`}
          hint={t('verification.coverageHint')}
        />
        <StatCard
          label={t('verification.calibrationGap')}
          value={`${quality.calibrationGap >= 0 ? '+' : ''}${num(quality.calibrationGap)}`}
          hint={t('verification.calibrationHint')}
        />
      </div>

      {quality.underpowered ? (
        <Notice tone="info">{t('verification.underpowered')}</Notice>
      ) : (
        <div className="grid gap-3 sm:grid-cols-3">
          <StatCard
            label={t('verification.falsePositive')}
            value={`${num(quality.falsePositiveRate)}%`}
          />
          <StatCard label={t('verification.missRate')} value={`${num(quality.missRate)}%`} />
          <StatCard label={t('verification.precision')} value={`${num(quality.precision)}%`} />
        </div>
      )}

      {/* ── Record a prediction ─────────────────────────────────────────────── */}
      {canRun ? (
        <Panel>
          <PanelHeader title={t('verification.record')} />
          <PanelBody>
            <div className="flex flex-wrap items-end gap-3">
              <label className="min-w-0 flex-1 basis-40">
                <span className="mb-1 block text-[11px] text-text-muted">{t('common.asset')}</span>
                <input
                  value={assetCode}
                  onChange={(event) => setAssetCode(event.target.value.toUpperCase())}
                  className="w-full rounded-lg border border-border bg-surface px-3 py-2 font-mono text-sm outline-none focus:border-brand"
                />
              </label>
              <label className="min-w-0 flex-1 basis-52">
                <span className="mb-1 block text-[11px] text-text-muted">{t('twin.title')}</span>
                <Select value={twinCode} onChange={(event) => setTwinCode(event.target.value)}>
                  {twins.map((twin) => (
                    <option key={twin.code} value={twin.code}>
                      {locale === 'ar' ? twin.nameAr : twin.name}
                    </option>
                  ))}
                </Select>
              </label>
              <label className="min-w-0 basis-32">
                <span className="mb-1 block text-[11px] text-text-muted">
                  {t('verification.horizon')}
                </span>
                <Select value={horizon} onChange={(event) => setHorizon(event.target.value)}>
                  {['15', '30', '60', '180'].map((value) => (
                    <option key={value} value={value}>
                      {value} {t('common.minShort')}
                    </option>
                  ))}
                </Select>
              </label>
              <Button onClick={record} disabled={busy || !twinCode}>
                {busy ? t('common.running') : t('common.run')}
              </Button>
            </div>
            {notice ? <p className="mt-2 text-[11px] text-text-muted">{notice}</p> : null}
          </PanelBody>
        </Panel>
      ) : null}

      <div className="grid gap-4 xl:grid-cols-2">
        {/* ── Error over time ──────────────────────────────────────────────── */}
        <div className="min-w-0">
          <Panel>
            <PanelHeader title={t('verification.errorOverTime')} />
            <PanelBody>
              {errorPoints.length < 2 ? (
                <p className="text-[11px] text-text-faint">{t('verification.noSamples')}</p>
              ) : (
                <Sparkline
                  points={errorPoints.map((point) => point.meanAbsPctError!)}
                  labels={errorPoints.map((point) => formatDateTime(locale, point.ts))}
                  suffix="%"
                  locale={locale}
                  invert
                />
              )}
            </PanelBody>
          </Panel>
        </div>

        {/* ── Fidelity over time ───────────────────────────────────────────── */}
        <div className="min-w-0">
          <Panel>
            <PanelHeader title={t('verification.fidelityOverTime')} />
            <PanelBody>
              {fidelityPoints.length < 2 ? (
                <p className="text-[11px] text-text-faint">{t('verification.noSamples')}</p>
              ) : (
                <Sparkline
                  points={fidelityPoints.map((point) => point.fidelity!)}
                  labels={fidelityPoints.map((point) => formatDateTime(locale, point.ts))}
                  suffix="%"
                  locale={locale}
                />
              )}
            </PanelBody>
          </Panel>
        </div>
      </div>

      {/* ── Calibration (§85) ───────────────────────────────────────────────── */}
      <Panel>
        <PanelHeader
          title={t('verification.calibrationCurve')}
          subtitle={t('verification.calibrationHint')}
        />
        <PanelBody>
          {calibration.bins.length === 0 ? (
            <p className="text-[11px] text-text-faint">{t('verification.noSamples')}</p>
          ) : (
            <ul className="space-y-2">
              {calibration.bins.map((bin) => (
                <li key={bin.lower} className="min-w-0">
                  <div className="flex items-baseline justify-between gap-2 text-[11px]">
                    <span className="text-text-muted">
                      {bin.lower}–{bin.upper}%
                    </span>
                    <span className="font-mono tabular-nums text-text-faint">
                      {t('verification.claimed')} {num(bin.claimed)} ·{' '}
                      {t('verification.achieved')} {num(bin.observed)} · n={bin.sampleCount}
                    </span>
                  </div>
                  <div className="relative mt-1 h-2 overflow-hidden rounded-full bg-surface-3">
                    <div
                      className="absolute inset-y-0 rounded-full bg-ai/40"
                      style={{ insetInlineStart: 0, width: `${Math.min(100, bin.claimed)}%` }}
                    />
                    <div
                      className="absolute inset-y-0 rounded-full bg-brand"
                      style={{ insetInlineStart: 0, width: `${Math.min(100, bin.observed)}%` }}
                    />
                  </div>
                </li>
              ))}
            </ul>
          )}
        </PanelBody>
      </Panel>

      {/* ── The record ──────────────────────────────────────────────────────── */}
      <Panel>
        <PanelHeader title={t('verification.title')} subtitle={t('verification.subtitle')} />
        <PanelBody>
          {verifications.length === 0 ? (
            <EmptyState title={t('uiState.empty')} body={t('verification.noSamples')} />
          ) : (
            <ul className="space-y-1.5">
              {verifications.map((row) => {
                const open = expanded === row.code
                return (
                  <li key={row.code} className="min-w-0 rounded-lg border border-border">
                    <button
                      type="button"
                      onClick={() => setExpanded(open ? null : row.code)}
                      aria-expanded={open}
                      className="flex w-full min-w-0 flex-wrap items-center gap-2 px-3 py-2 text-start transition-colors hover:bg-surface-2"
                    >
                      <Badge tone={STATUS_TONE[row.status] ?? 'muted'} dot>
                        {t(`verification.${row.status}`)}
                      </Badge>
                      <span className="font-mono text-[12px] font-medium">{row.assetCode}</span>
                      <span className="text-[11px] text-text-muted">
                        +{row.horizonMin} {t('common.minShort')}
                      </span>
                      <span className="ms-auto shrink-0 font-mono text-[11px] tabular-nums text-text-faint">
                        {row.status === 'verified' && row.meanAbsError !== null
                          ? `${t('verification.deviation')} ${num(row.worstError ?? 0, 2)}%`
                          : formatDateTime(locale, row.horizonAt)}
                      </span>
                    </button>

                    {open ? (
                      <div className="border-t border-border px-3 py-2">
                        {row.channels.length === 0 ? (
                          <p className="text-[11px] text-text-faint">{t('uiState.empty')}</p>
                        ) : (
                          <div className="overflow-x-auto">
                            <table className="w-full min-w-[28rem] text-[11px]">
                              <thead>
                                <tr className="text-text-faint">
                                  <th className="py-1 text-start font-medium">
                                    {t('verification.channel')}
                                  </th>
                                  <th className="py-1 text-end font-medium">
                                    {t('verification.predicted')}
                                  </th>
                                  <th className="py-1 text-end font-medium">
                                    {t('verification.observedCol')}
                                  </th>
                                  <th className="py-1 text-end font-medium">
                                    {t('verification.deviation')}
                                  </th>
                                </tr>
                              </thead>
                              <tbody>
                                {row.channels.map((channel) => (
                                  <tr key={channel.channel} className="border-t border-border/60">
                                    <td className="py-1 text-text-muted">{channel.channel}</td>
                                    <td className="py-1 text-end font-mono tabular-nums">
                                      {num(channel.predicted, 2)} {channel.unit}
                                    </td>
                                    <td className="py-1 text-end font-mono tabular-nums">
                                      {row.status === 'verified'
                                        ? `${num(channel.observed, 2)} ${channel.unit}`
                                        : '—'}
                                    </td>
                                    <td
                                      className={cn(
                                        'py-1 text-end font-mono tabular-nums',
                                        row.status !== 'verified'
                                          ? 'text-text-faint'
                                          : channel.withinInterval
                                            ? 'text-normal'
                                            : 'text-warning',
                                      )}
                                    >
                                      {row.status === 'verified' ? `${num(channel.pctError, 2)}%` : '—'}
                                    </td>
                                  </tr>
                                ))}
                              </tbody>
                            </table>
                          </div>
                        )}

                        <p className="mt-2 text-[10px] text-text-faint">
                          {row.modelVersion || '—'} · {row.twinVersion || row.twinCode} ·{' '}
                          {formatDateTime(locale, row.predictedAt)}
                          {row.fidelityAfter !== null
                            ? ` · ${t('uncertainty.twinFidelity')} ${num(row.fidelityBefore)} → ${num(row.fidelityAfter)}`
                            : ''}
                        </p>
                      </div>
                    ) : null}
                  </li>
                )
              })}
            </ul>
          )}
        </PanelBody>
      </Panel>
    </div>
  )
}

/**
 * A minimal line chart.
 *
 * Deliberately not a library: two dozen points with a min/max label is all this needs,
 * and an axis-and-legend chart here would be more furniture than information.
 */
function Sparkline({
  points,
  labels,
  suffix,
  locale,
  invert = false,
}: {
  points: number[]
  labels: string[]
  suffix: string
  locale: Locale
  invert?: boolean
}) {
  const min = Math.min(...points)
  const max = Math.max(...points)
  const span = max - min || 1
  const width = 100
  const height = 32

  const path = points
    .map((value, index) => {
      const x = (index / Math.max(1, points.length - 1)) * width
      const y = height - ((value - min) / span) * height
      return `${index === 0 ? 'M' : 'L'}${x.toFixed(2)},${y.toFixed(2)}`
    })
    .join(' ')

  const last = points[points.length - 1]

  return (
    <div>
      <svg
        viewBox={`0 0 ${width} ${height}`}
        preserveAspectRatio="none"
        className="h-16 w-full"
        role="img"
        aria-label={`${labels[0]} – ${labels[labels.length - 1]}`}
      >
        <path
          d={path}
          fill="none"
          stroke={invert ? 'var(--color-warning)' : 'var(--color-brand)'}
          strokeWidth="1.5"
          vectorEffect="non-scaling-stroke"
        />
      </svg>
      <div className="mt-1 flex items-baseline justify-between text-[10px] text-text-faint">
        <span className="font-mono tabular-nums">
          {formatNumber(locale, min, { maximumFractionDigits: 2 })}
          {suffix}
        </span>
        <span className="font-mono tabular-nums text-text-muted">
          {formatNumber(locale, last, { maximumFractionDigits: 2 })}
          {suffix}
        </span>
        <span className="font-mono tabular-nums">
          {formatNumber(locale, max, { maximumFractionDigits: 2 })}
          {suffix}
        </span>
      </div>
    </div>
  )
}
