import type { Metadata } from 'next';
import { SCORE_COMPONENT_LABELS, type ComponentScores, type ScoreComponent } from '@justice-choice/types';
import { api } from '@/lib/api';
import { fmtDate, fmtNumber } from '@/lib/format';
import { staticMetadata } from '@/lib/meta';
import { Breadcrumbs } from '@/components/Breadcrumbs';
import { COMPONENT_COLOR, LEDGER_GROUPS } from '@/components/ledger';
import { CalendarIcon, CheckIcon, HashIcon, LayersIcon, ScaleIcon, UsersIcon } from '@/components/Icons';

export const dynamic = 'force-dynamic';

// Absolute: the brand is already in the title, so skip the "| Justice Choice" suffix
export const metadata: Metadata = staticMetadata({
  title: 'How Justice Choice Ranks Lawyers — Ranking Methodology',
  absolute: true,
  description: 'How Justice Choice ranks lawyers: nine published components, fixed weights, time decay, source caps, reproducible runs and a strict separation from paid placement.',
  path: '/methodology/',
});

type Methodology = {
  version: string | null;
  description: string | null;
  weights: ComponentScores | null;
  parameters: Record<string, number> | null;
  lastRun: { id: number; finishedAt: string; rankings: number; inputsHash: string } | null;
  counts: { rankings: number; cohorts: number; lawyers: number; sources: number };
  versions: { version: string; description: string | null; createdAt: string; isActive: boolean }[];
  schedule: string;
};

const WHAT: Record<ScoreComponent, string> = {
  credentials: 'Active licences checked against the issuing body, additional jurisdictions, verified education and identity. Suspended or revoked licences remove a lawyer from rankings entirely.',
  experience: 'Years practising in this specific area, on a curve that rewards early years most and levels off at 25 years. When area-specific years are unknown, only half of general experience counts.',
  specialization: 'Whether this is the lawyer’s primary area and how concentrated their practice is. Generalists covering many areas score lower here.',
  reputation: 'Review ratings weighted by recency, whether the reviewer’s engagement was verified, and the authority of the source, then blended with a neutral prior so a handful of reviews cannot dominate.',
  recognition: 'Peer and professional recognition weighted by the issuer’s editorial rigour, relevance to this area and age. Paid-for listings are shown but score zero.',
  trackRecord: 'Matters found in public court records or published judgments, weighted by relevance and age. Self-reported outcomes without a source are not counted.',
  clientExperience: 'How reliably the lawyer responds to enquiries routed through Justice Choice, blended with the volume of verified reviews. Neutral when there is too little data.',
  profileQuality: 'Completeness of the profile: biography, contact details, office address, languages, education and licence data.',
  freshness: 'How recently the profile was verified. Full credit within six months, declining to zero after three years; profiles that were never verified carry a 40% reduction.',
};

const PARAM_LABEL: Record<string, string> = {
  reviewHalfLifeMonths: 'Review half-life (months)',
  awardHalfLifeYears: 'Recognition half-life (years)',
  trackRecordHalfLifeYears: 'Sourced matter half-life (years)',
  maxEffectiveReviewsPerSource: 'Cap on effective reviews from any one source',
  reviewPriorMean: 'Neutral rating prior (out of 5)',
  reviewPriorWeight: 'Prior strength (effective reviews)',
  unverifiedReviewFactor: 'Weight of an unverified review',
  experienceSaturationYears: 'Experience curve levels off at (years)',
  freshnessFullDays: 'Full freshness within (days)',
  freshnessZeroDays: 'Freshness reaches zero after (days)',
  generalExperienceFactor: 'Share of general experience counted',
};

export default async function MethodologyPage() {
  const m = await api<Methodology>('/methodology');
  const w = m.weights;
  return (
    <div className="mx-auto max-w-4xl px-4 py-8 sm:px-6 sm:py-10">
      <Breadcrumbs items={[{ label: 'Home', href: '/' }, { label: 'Methodology', href: '/methodology/' }]} />
      <header className="mt-6 border-b border-rule pb-10">
        <p className="eyebrow">
          <ScaleIcon className="h-3.5 w-3.5" /> Published methodology
        </p>
        <h1 className="mt-3 font-serif text-[2.25rem] font-semibold leading-[1.1] tracking-tight sm:text-[2.75rem]">How Justice Choice ranks lawyers</h1>
        <p className="mt-4 max-w-2xl text-lg leading-relaxed text-ink-2">
          Every ranking on Justice Choice comes from the same published formula. This page lists every component, its weight, the rules that limit it and how you can challenge a result.
        </p>
        <dl className="mt-8 grid grid-cols-2 gap-3 text-sm sm:grid-cols-4">
          {[
            [HashIcon, 'Current version', <span key="v" className="num">{m.version ?? '—'}</span>],
            [CalendarIcon, 'Last recalculated', fmtDate(m.lastRun?.finishedAt)],
            [UsersIcon, 'Lawyers ranked', <span key="l" className="num">{fmtNumber(m.counts.lawyers)}</span>],
            [LayersIcon, 'City × area rankings', <span key="c" className="num">{fmtNumber(m.counts.cohorts)}</span>],
          ].map(([Icon, label, value]) => {
            const I = Icon as typeof HashIcon;
            return (
              <div key={label as string} className="card flex flex-col-reverse p-4">
                <dt className="mt-1 text-xs text-muted">{label as string}</dt>
                <dd className="flex items-center gap-2 font-serif text-xl font-semibold">
                  <I className="h-4 w-4 text-ledger" />
                  {value as React.ReactNode}
                </dd>
              </div>
            );
          })}
        </dl>
      </header>

      <section className="mt-12 space-y-10" aria-labelledby="components-h">
        <h2 id="components-h" className="font-serif text-3xl font-semibold tracking-tight">
          The nine components
        </h2>
        {LEDGER_GROUPS.map((g) => (
          <div key={g.key}>
            <h3 className="flex items-center gap-2 text-xs font-bold uppercase tracking-[0.08em] text-muted">
              <span aria-hidden className="inline-block h-2.5 w-2.5 rounded-sm" style={{ background: COMPONENT_COLOR[g.components[0]] }} />
              {g.label} · {Math.round(g.components.reduce((s, k) => s + (w?.[k] ?? 0), 0) * 100)}% of the score
            </h3>
            <dl className="card mt-3 divide-y divide-rule overflow-hidden">
              {g.components.map((k) => (
                <div key={k} className="grid gap-2 p-5 sm:grid-cols-[13rem_1fr_4rem] sm:gap-6">
                  <dt className="flex items-center gap-2 font-semibold">
                    <span aria-hidden className="inline-block h-3 w-3 rounded-sm" style={{ background: COMPONENT_COLOR[k] }} />
                    {SCORE_COMPONENT_LABELS[k]}
                  </dt>
                  <dd className="text-sm text-ink-2">{WHAT[k]}</dd>
                  <dd className="num font-serif text-2xl font-semibold text-brass sm:text-right">{w ? Math.round(w[k] * 100) : '—'}%</dd>
                </div>
              ))}
            </dl>
          </div>
        ))}
      </section>

      <section className="mt-14" aria-labelledby="rules-h">
        <h2 id="rules-h" className="font-serif text-3xl font-semibold tracking-tight">
          Rules every ranking follows
        </h2>
        <ul className="mt-5 grid gap-3 text-ink-2 sm:grid-cols-2">
          {[
            'Every score is traceable to stored records. Profiles show the evidence behind each component.',
            'Each run stores its version, reference date and a fingerprint of all inputs, so any past ranking can be reproduced exactly.',
            'Older information counts for less, using the half-lives below.',
            'No single review source can contribute more than a fixed amount of evidence, however many reviews it holds.',
            'Payment never enters the calculation. The ranking engine has no access to subscription or advertising data.',
            'We never invent credentials, case results, reviews or awards. Missing data lowers a score; it is not filled in.',
          ].map((rule) => (
            <li key={rule} className="card flex gap-3 p-4 text-sm leading-relaxed">
              <span className="mt-0.5 grid h-5 w-5 shrink-0 place-items-center rounded-full bg-ledger-soft text-ledger">
                <CheckIcon className="h-3 w-3" strokeWidth={2.5} />
              </span>
              {rule}
            </li>
          ))}
        </ul>
        {m.parameters && (
          <div className="card mt-6 overflow-hidden">
            <table className="w-full text-left text-sm">
              <caption className="border-b border-rule px-4 py-2.5 text-left font-semibold">Parameters in {m.version}</caption>
              <tbody className="divide-y divide-rule">
                {Object.entries(m.parameters).map(([k, v]) => (
                  <tr key={k}>
                    <th scope="row" className="px-4 py-2 font-normal text-ink-2">
                      {PARAM_LABEL[k] ?? k}
                    </th>
                    <td className="num px-4 py-2 text-right font-semibold">{v}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        )}
      </section>

      <section id="sponsored" className="mt-14 scroll-mt-20">
        <h2 className="font-serif text-3xl font-semibold tracking-tight">Sponsored placements and conflicts</h2>
        <div className="prose-legal mt-4 text-ink-2">
          <p>
            Lawyers and firms can buy a premium profile or a sponsored slot in a specific city and practice area. Sponsored listings appear in a separate box marked “Sponsored” above the ranking. They are never mixed into the ranked list and never change a score or position.
          </p>
          <p>
            Lead matching uses organic fit only: verified licence, location, practice area and organic rank. Staff who maintain rankings cannot see commercial data, and changes to weights require a new published version.
          </p>
        </div>
      </section>

      <section id="limitations" className="mt-14">
        <h2 className="font-serif text-3xl font-semibold tracking-tight">Limitations</h2>
        <div className="prose-legal mt-4 text-ink-2">
          <p>
            A ranking is a structured summary of available evidence, not a guarantee of quality or outcome, and it is not legal advice. Good lawyers with little public data can rank lower than they deserve. Weights are reviewed per jurisdiction with qualified practitioners before a market launches, and local rules on lawyer advertising apply.
          </p>
        </div>
      </section>

      <section id="corrections" className="mt-14 scroll-mt-20">
        <h2 className="font-serif text-3xl font-semibold tracking-tight">Corrections and disputes</h2>
        <div className="prose-legal mt-4 text-ink-2">
          <p>
            Anyone can report inaccurate information from any profile or ranking page. Lawyers can dispute their own data. Each report is reviewed by the data team, logged in our audit trail and answered, usually within five working days. Corrections take effect at the next recalculation.
          </p>
        </div>
      </section>

      <section className="mt-14">
        <h2 className="font-serif text-3xl font-semibold tracking-tight">Update schedule and version history</h2>
        <p className="mt-4 text-ink-2">{m.schedule}</p>
        {m.lastRun && (
          <p className="mt-2 text-sm text-muted">
            Last run #{m.lastRun.id} produced {fmtNumber(m.lastRun.rankings)} rankings. Input fingerprint <code className="num break-all">{m.lastRun.inputsHash?.slice(0, 16)}…</code>
          </p>
        )}
        <ul className="card mt-5 divide-y divide-rule overflow-hidden text-sm">
          {m.versions.map((v) => (
            <li key={v.version} className="flex flex-wrap items-baseline gap-x-4 gap-y-1 px-4 py-3">
              <span className="num font-semibold">{v.version}</span>
              {v.isActive && <span className="rounded bg-ledger-soft px-1.5 text-xs font-semibold text-ledger">Current</span>}
              <span className="text-muted">{fmtDate(v.createdAt)}</span>
              <span className="w-full text-ink-2">{v.description}</span>
            </li>
          ))}
        </ul>
      </section>
    </div>
  );
}
