
import * as ActionIcons from '@/components/Icons';
import { SCORE_COMPONENT_LABELS, type ScoreComponent } from '@justice-choice/types';
import { api, ApiError, isAdmin, me } from '@/lib/api';
import { date, n } from '@/lib/format';
import { Card, Empty, Flash, PageHeader, Pill, SearchField, type SP } from '@/components/ui';
import { SubmitButton } from '@/components/Actions';
import { WeightsForm } from '@/components/WeightsForm';
import { AlertIcon, ChartIcon, CheckCircleIcon, HistoryIcon, LayersIcon, PlusIcon, RefreshIcon, SearchIcon } from '@/components/Icons';
import { activateVersion, createVersion, recalculate, previewVersion, approveVersion, rollbackVersion, decideRankingAppeal } from '../../actions';

type Run = { id: number; version: string; status: string; scope: { cityIds: number[] | null } | null; referenceDate: string; inputsHash: string | null; rankingsCount: number; triggeredBy: string | null; error: string | null; startedAt: string | null; finishedAt: string | null; createdAt: string };
type Version = { status: string; preview: { lawyersAffected: number; cohorts: number; largeChanges: { lawyerId: number; oldPosition: number | null; newPosition: number; scoreChange: number }[] } | null;  version: string; weights: Record<string, number>; parameters: Record<string, number>; description: string | null; isActive: boolean; createdAt: string };
type Audit = {
  lawyer: { id: number; slug: string; name: string };
  rankings: { ranking: { id: number; rankPosition: number; score: string; rankingVersion: string; isCurrent: boolean; calculatedAt: string }; scores: Record<string, unknown> & { evidence: { component: string; score: number; weight: number; contribution: number; explanation: string }[] }; city: string; area: string; run: { id: number; inputsHash: string } | null }[];
};
const KEYS = ['credentials', 'experience', 'specialization', 'reputation', 'recognition', 'trackRecord', 'clientExperience', 'profileQuality', 'freshness'];
const label = (k: string) => SCORE_COMPONENT_LABELS[k as ScoreComponent] ?? k;

/** Distinguish "no such lawyer" from other failures instead of silently showing nothing */
async function loadAudit(q: string): Promise<Audit | 'missing' | 'error'> {
  try {
    return await api<Audit>(`/admin/rankings/audit?lawyer=${encodeURIComponent(q)}`);
  } catch (e) {
    if (e instanceof ApiError) return e.status === 404 ? 'missing' : 'error';
    // Session expiry must still redirect to sign-in; the helper's notFound() means "no such lawyer"
    if (e && typeof e === 'object' && 'digest' in e && String((e as { digest: unknown }).digest).startsWith('NEXT_REDIRECT')) throw e;
    return 'missing';
  }
}

export default async function Rankings({ searchParams }: { searchParams: SP }) {
  const sp = await searchParams;
  const [runs, versions, ref, audit, repro, user] = await Promise.all([
    api<Run[]>('/admin/rankings/runs'),
    api<Version[]>('/admin/rankings/versions'),
    api<{ cities: { id: number; name: string }[] }>('/admin/reference'),
    sp.audit ? loadAudit(sp.audit) : null,
    sp.repro && /^\d+$/.test(sp.repro) ? api<{ runId: number; storedHash: string; currentHash: string; inputsUnchanged: boolean }>(`/admin/rankings/runs/${sp.repro}/reproducibility`).catch(() => null) : null,
    me(),
  ]);
  const active = versions.find((v) => v.isActive);
  const admin = isAdmin(user.role);
  const appeals = await api<{ items: { id: number; lawyer: string; reason: string; evidence: string; status: string; evidenceFileId: number | null }[] }>('/admin/ranking-appeals').catch((e) => { if (e instanceof ApiError && e.status === 403) return { items: [] }; throw e; });
  return (
    <>
      <PageHeader
        title="Rankings"
        icon={<ChartIcon className="h-5 w-5" />}
        description="Deterministic, versioned runs. Each run stores its reference date and a SHA-256 fingerprint of every input so results can be reproduced and audited."
      />
      <Flash ok={sp.ok} err={sp.err} />
      {repro && (
        <div className={`fade-up mb-5 flex items-start gap-3 rounded-lg border px-4 py-3 text-sm ${repro.inputsUnchanged ? 'border-ledger/30 bg-ledger-soft text-ledger' : 'border-brass/40 bg-brass-soft text-ink'}`}>
          {repro.inputsUnchanged ? <CheckCircleIcon className="mt-0.5 h-4 w-4" /> : <AlertIcon className="mt-0.5 h-4 w-4 text-brass" />}
          <span>
            <strong className="font-semibold">Run #{repro.runId}:</strong>{' '}
            {repro.inputsUnchanged
              ? 'inputs are unchanged — recomputing would give identical rankings.'
              : 'evidence has changed since this run (new reviews, verifications or edits). Recalculate to publish an updated ranking; the stored run remains reproducible from its archived inputs.'}
          </span>
        </div>
      )}
      <div className="grid gap-6 xl:grid-cols-[1.5fr_1fr]">
        <Card title="Recent runs" icon={<HistoryIcon className="h-4 w-4" />}>
          {runs.length ? (
            <div className="max-h-[30rem] overflow-auto">
              <table className="tbl">
                <thead>
                  <tr>
                    <th>Run</th>
                    <th>Status</th>
                    <th>Version</th>
                    <th>Scope</th>
                    <th className="text-right">Rankings</th>
                    <th>Finished</th>
                    <th>By</th>
                    <th />
                  </tr>
                </thead>
                <tbody>
                  {runs.map((r) => (
                    <tr key={r.id}>
                      <td className="num font-semibold">#{r.id}</td>
                      <td>
                        <Pill value={r.status} />
                        {r.error && <span className="mt-1 block max-w-48 truncate text-[11px] text-danger" title={r.error}>{r.error}</span>}
                      </td>
                      <td className="num">{r.version}</td>
                      <td>{r.scope?.cityIds?.length ? `${r.scope.cityIds.length} ${r.scope.cityIds.length === 1 ? 'city' : 'cities'}` : 'all'}</td>
                      <td className="num text-right">{n(r.rankingsCount)}</td>
                      <td className="whitespace-nowrap">{date(r.finishedAt, true)}</td>
                      <td className="text-muted">{r.triggeredBy ?? 'scheduler'}</td>
                      <td>
                        {r.status === 'completed' && (
                          <a href={`?repro=${r.id}`} className="btn btn-ghost btn-xs whitespace-nowrap">
                            Check reproducibility
                          </a>
                        )}
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          ) : (
            <Empty>No ranking runs yet.</Empty>
          )}
        </Card>
        <div className="space-y-6">
          <Card title="Recalculate" icon={<RefreshIcon className="h-4 w-4" />}>
            {admin ? (
              <form action={recalculate} className="space-y-3 p-4 text-sm">
                <label className="block">
                  <span className="field-label"><ActionIcons.ScaleIcon />Scope</span>
                  <select name="cityId" className="field">
                    <option value="">All cities</option>
                    {ref.cities.map((c) => (
                      <option key={c.id} value={c.id}>
                        {c.name}
                      </option>
                    ))}
                  </select>
                </label>
                <p className="text-xs text-muted">Runs on the ranking worker when Redis is available, otherwise inline. SEO eligibility is re-evaluated afterwards.</p>
                <SubmitButton pendingLabel="Starting…" confirm="Start a recalculation now?">
                  <RefreshIcon className="h-4 w-4" /> Recalculate with {active?.version ?? 'active version'}
                </SubmitButton>
              </form>
            ) : (
              <p className="p-4 text-sm text-muted">Only admins can start a recalculation.</p>
            )}
          </Card>
          <Card title="Ranking audit" icon={<SearchIcon className="h-4 w-4" />}>
            <form className="flex gap-2 p-4">
              <SearchField name="audit" defaultValue={sp.audit} placeholder="Lawyer slug or id" className="flex-1" />
              <button className="btn btn-quiet"><ActionIcons.FileTextIcon />Audit</button>
            </form>
          </Card>
        </div>
      </div>

      {audit === 'missing' || audit === 'error' ? (
        <div className="mt-6 flex items-center gap-2 rounded-lg border border-brass/40 bg-brass-soft px-4 py-3 text-sm">
          <AlertIcon className="h-4 w-4 text-brass" />
          {audit === 'missing' ? `No lawyer found for “${sp.audit}”. Use the profile slug (e.g. jane-doe) or numeric id.` : 'The audit could not be loaded. Try again.'}
        </div>
      ) : (
        audit && (
          <Card title={`Audit: ${audit.lawyer.name}`} icon={<SearchIcon className="h-4 w-4" />} className="mt-6">
            {audit.rankings.length ? (
              audit.rankings.map((r) => (
                <div key={r.ranking.id} className="border-b border-rule p-4 last:border-b-0">
                  <p className="flex flex-wrap items-center gap-2 text-sm">
                    <span className="rank-figure text-2xl">{r.ranking.rankPosition}</span>
                    <span className="font-semibold">
                      {r.area} in {r.city}
                    </span>
                    <span className="text-muted">·</span> score <span className="num font-semibold">{Number(r.ranking.score).toFixed(3)}</span>
                    <span className="text-muted">·</span> <span className="num">{r.ranking.rankingVersion}</span>
                    <span className="text-muted">·</span> run #{r.run?.id ?? '—'}
                    {r.ranking.isCurrent ? <Pill value="current" tone="good" /> : <Pill value="historical" />}
                  </p>
                  <div className="mt-3 overflow-x-auto rounded-lg border border-rule">
                    <table className="tbl">
                      <thead>
                        <tr>
                          <th>Component</th>
                          <th className="text-right">Score</th>
                          <th className="text-right">Weight</th>
                          <th className="text-right">Points</th>
                          <th>Explanation</th>
                        </tr>
                      </thead>
                      <tbody>
                        {(r.scores.evidence ?? []).map((e) => (
                          <tr key={e.component}>
                            <td className="whitespace-nowrap font-medium">{label(e.component)}</td>
                            <td className="num text-right">{e.score.toFixed(1)}</td>
                            <td className="num text-right">{Math.round(e.weight * 100)}%</td>
                            <td className="num text-right font-semibold">{e.contribution.toFixed(2)}</td>
                            <td className="text-muted">{e.explanation}</td>
                          </tr>
                        ))}
                      </tbody>
                    </table>
                  </div>
                </div>
              ))
            ) : (
              <p className="p-4 text-sm text-muted">Not ranked — check licences and profile status.</p>
            )}
          </Card>
        )
      )}

      <div className="mt-6 grid gap-6 xl:grid-cols-2">
        <Card title="Weight versions" icon={<LayersIcon className="h-4 w-4" />}>
          <ul className="divide-y divide-rule">
            {versions.map((v) => (
              <li key={v.version} className="p-4">
                <div className="flex items-center justify-between gap-3">
                  <p className="flex items-center gap-2">
                    <span className="num font-semibold">{v.version}</span>
                    <span className="text-xs text-muted">{date(v.createdAt)}</span>
                  </p>
                  {v.isActive ? (
                    <Pill value="active" />
                  ) : (
                    admin && (
                      <div className="flex flex-wrap gap-2">
                      <form action={previewVersion}><input type="hidden" name="version" value={v.version} /><SubmitButton className="btn btn-quiet btn-xs">Preview</SubmitButton></form>
                      {v.status === 'preview' && <form action={approveVersion}><input type="hidden" name="version" value={v.version} /><SubmitButton className="btn btn-quiet btn-xs" confirm="Approve this preview after reviewing its impact?">Approve</SubmitButton></form>}
                      {v.status === 'archived' && <form action={rollbackVersion}><input type="hidden" name="version" value={v.version} /><SubmitButton className="btn btn-quiet btn-xs" confirm="Restore this previously active model?">Rollback</SubmitButton></form>}
                      {v.status === 'approved' && <form action={activateVersion}>
                        <input type="hidden" name="version" value={v.version} />
                        <SubmitButton className="btn btn-quiet btn-xs" confirm={`Publish ${v.version} and recalculate rankings?`}>
                          <ActionIcons.CheckCircleIcon />Publish
                        </SubmitButton>
                      </form>}
                      </div>
                    )
                  )}
                </div>
                <p className="mt-2 text-xs text-muted">Status: {v.status}</p>
                {v.preview && <div className="mt-2 rounded-lg border border-rule p-3 text-xs">
                  <p>{v.preview.lawyersAffected} lawyers affected across {v.preview.cohorts} cohorts</p>
                  <ul className="mt-2 space-y-1">{v.preview.largeChanges.slice(0, 20).map((c, i) => <li key={i}>Lawyer #{c.lawyerId}: {c.oldPosition ?? 'unranked'} to {c.newPosition}; score change {c.scoreChange.toFixed(3)}</li>)}</ul>
                </div>}
                {v.description && <p className="mt-1 text-xs text-muted">{v.description}</p>}
                <div className="mt-3 flex h-2 overflow-hidden rounded-full bg-sunken" title="Weights">
                  {KEYS.map((k, i) => (
                    <span key={k} className="h-full border-r border-surface last:border-r-0" style={{ width: `${(v.weights[k] ?? 0) * 100}%`, background: i < 3 ? 'var(--g-expertise)' : i < 6 ? 'var(--g-standing)' : 'var(--g-service)' }} />
                  ))}
                </div>
                <ul className="mt-2 flex flex-wrap gap-x-3 gap-y-1 text-[11px] text-ink-2">
                  {KEYS.map((k) => (
                    <li key={k}>
                      {label(k)} <span className="num font-semibold">{Math.round((v.weights[k] ?? 0) * 100)}%</span>
                    </li>
                  ))}
                </ul>
              </li>
            ))}
          </ul>
        </Card>
        <Card title="New version" icon={<PlusIcon className="h-4 w-4" />}>
          {admin ? <WeightsForm action={createVersion} initial={active?.weights ?? {}} /> : <p className="p-4 text-sm text-muted">Only admins can create weight versions.</p>}
        </Card>
      </div>
      <Card title="Professional ranking reviews" className="mt-6">
        {appeals.items.length ? appeals.items.map((r) => <div key={r.id} className="space-y-2 border-b border-rule p-4 text-sm">
          <p className="font-semibold">{r.lawyer}: {r.reason.replaceAll('_', ' ')} ({r.status})</p><p className="whitespace-pre-wrap">{r.evidence}</p>
          {r.evidenceFileId && <a href={`/files/${r.evidenceFileId}`} className="text-ledger">Private evidence</a>}
          <form action={decideRankingAppeal} className="grid gap-2"><input type="hidden" name="id" value={r.id} />
            <select name="status" className="field"><option value="in_review">In review</option><option value="resolved">Resolved after evidence correction</option><option value="rejected">Rejected</option></select>
            <textarea name="note" minLength={10} maxLength={2000} required className="field" placeholder="Decision and any verified data correction" /><SubmitButton>Save decision</SubmitButton>
          </form>
        </div>) : <p className="p-4 text-sm text-muted">No pending ranking reviews.</p>}
      </Card>
    </>
  );
}
