import { useEffect, useRef, useState, type ReactNode, type RefObject } from 'react';
import { Link } from 'react-router-dom';
import { ArrowDown, ArrowRight, ArrowUpRight } from 'lucide-react';

import { EnvAnatomy } from '@/components/demo/EnvAnatomy';
import { DefinitionStrip } from '@/components/site/DefinitionStrip';
import {
  EnvironmentList,
  FeaturedEnvironment,
  builtEnvironments,
  environments,
  writtenEnvironments,
} from '@/components/site/EnvironmentPicker';
import { Term } from '@/components/site/Term';
import { TileRow } from '@/components/site/TileRow';
import { Skeleton } from '@/components/ui/skeleton';
import { helloWorldCitations, trainingResult } from '@/content/evidence';
import { CountWord, countWord, pad2, routes } from '@/content/lineup';
import * as s from '@/content/styles';
import { PROPOSAL_NOTICE } from '@/content/verticals';
import { listRuns, loadDemoModule, loadEpisode, verifyEpisode, verifySummary } from '@/lib/demo-kit';
import type { AnyDemoModule, RunRef, VerifyResult } from '@/lib/demo-kit';
import { pageTitle, useSeo } from '@/lib/seo';

/**
 * The landing page. Definition before catalogue, catalogue before reading list.
 *
 * The h1 is the site's thesis and it is jargon on its own, so the lede opens
 * with the plain definition and the four-part strip under it names the parts
 * a visitor will meet again on every Play and Evidence tab. Then the picker,
 * then the machine explained once with the live demo's own numbers, then the
 * twelve written environments as a list, then the evidence.
 *
 * Counts in the copy are derived from the registry and the lineup, never
 * typed. The three figures in "How it works" are read off the featured demo's
 * own module and its newest recorded run, loaded lazily when the section
 * scrolls near — never typed, and never on the first paint's critical path.
 */

/* ------------------------------------------------------------ lazy figures */

/** True once the element has come within a screen of the viewport. */
function useNearViewport<T extends Element>(): [RefObject<T | null>, boolean] {
  const ref = useRef<T | null>(null);
  const [near, setNear] = useState(false);

  useEffect(() => {
    const node = ref.current;
    if (!node || near) return undefined;
    // No observer (a prerender capture, an old engine) means "load now": a
    // missing figure is worse than an early fetch.
    if (typeof IntersectionObserver === 'undefined') {
      setNear(true);
      return undefined;
    }
    const observer = new IntersectionObserver(
      (entries) => {
        if (entries.some((entry) => entry.isIntersecting)) setNear(true);
      },
      { rootMargin: '100% 0px' },
    );
    observer.observe(node);
    return () => observer.disconnect();
  }, [near]);

  return [ref, near];
}

/** The most recently captured run. Ties keep manifest order, last wins. */
function newestRun(runs: readonly RunRef[]): RunRef | undefined {
  let best: RunRef | undefined;
  for (const run of runs) {
    if (!best || run.capturedAt >= best.capturedAt) best = run;
  }
  return best;
}

interface Figures {
  module: AnyDemoModule | null;
  verify: VerifyResult | null;
}

/**
 * The featured demo's module and the verification of its newest run.
 *
 * Both start `null` and stay `null` on any failure: the placeholders print
 * dashes, which is the honest rendering of "not loaded" — a zero here would be
 * a claim about the grader.
 */
function useFigures(slug: string | null, near: boolean): Figures {
  const [figures, setFigures] = useState<Figures>({ module: null, verify: null });

  useEffect(() => {
    if (!slug || !near) return undefined;
    let live = true;

    void loadDemoModule(slug)
      .then(async (module) => {
        if (!live) return;
        setFigures({ module, verify: null });
        const run = newestRun(await listRuns(slug));
        if (!run) return;
        const episode = await loadEpisode(run);
        if (!live) return;
        setFigures({ module, verify: verifyEpisode(module, episode) });
      })
      .catch(() => {
        // The passages still read without their figures. The demo page has
        // its own error card; the landing does not repeat it.
      });

    return () => {
      live = false;
    };
  }, [slug, near]);

  return figures;
}

/** A reward total, three places; `—` for not scored. */
function fixed(value: number | null, digits: number): string {
  if (value === null || !Number.isFinite(value)) return '—';
  // Below the last printed digit a negative rounding residue would print as
  // "-0.0000000", which reads as a disagreement that is not there.
  const clamped = Math.abs(value) < 10 ** -digits / 2 ? 0 : value;
  return clamped.toFixed(digits);
}

/* ------------------------------------------------------------------ pieces */

function SectionLabel({ children, id }: { children: ReactNode; id?: string }) {
  return (
    <p className={s.label} id={id}>
      {children}
    </p>
  );
}

/**
 * One numbered passage in "How it works": a claim, a paragraph and a mono
 * figure. Figure right from `lg`, under the paragraph on a phone.
 */
function Passage({
  index,
  title,
  figure,
  children,
}: {
  index: string;
  title: string;
  figure: ReactNode;
  children: ReactNode;
}) {
  return (
    <li className="grid gap-4 border-t border-border py-6 lg:grid-cols-[minmax(0,1fr)_minmax(0,22rem)] lg:gap-10">
      <div>
        <h3 className={`${s.h3} flex items-baseline gap-3`}>
          <span className="font-mono text-label uppercase text-muted">{index}</span>
          {title}
        </h3>
        <p className={`${s.prose} mt-2`}>{children}</p>
      </div>
      <div className="min-w-0 lg:pt-1">{figure}</div>
    </li>
  );
}

/**
 * A mono figure with a caption. A caption that is data (a call, a run id) is
 * mono; one that is a sentence (the verifier's verdict) is sans, because it
 * has a verb in it.
 */
function Figure({
  caption,
  sentence = false,
  children,
}: {
  caption: ReactNode;
  sentence?: boolean;
  children: ReactNode;
}) {
  return (
    <figure className="rounded-lg border border-border bg-surface-2 p-3">
      <div className="min-w-0">{children}</div>
      <figcaption
        className={
          sentence ? 'mt-2 text-small text-fg-2' : 'mt-2 break-words font-mono text-caption text-muted'
        }
      >
        {caption}
      </figcaption>
    </figure>
  );
}

/** The figure's own line: mono, wrapping at the operators rather than clipping. */
const FIGURE_LINE = 'break-words font-mono text-mono-data text-fg';

/* -------------------------------------------------------------------- page */

export default function Home() {
  // The head is set here as well as baked by `scripts/prerender.mjs`, and the
  // two are not redundant: prerender covers the crawler that fetches the
  // document, this covers the visitor who arrives on another route and clicks
  // through without ever fetching a new one.
  useSeo({
    title: pageTitle(),
    description:
      'Interactive demos of reinforcement-learning environments for executives. Play the environment, watch a recorded model play the same board, then move the reward and watch the ranking change.',
    canonical: routes.home,
    ogImage: '/og/home.png',
  });

  const featured = builtEnvironments[0];
  const builtCount = builtEnvironments.length;
  const writtenCount = writtenEnvironments.length;
  const totalCount = environments.length;

  const [howRef, howNear] = useNearViewport<HTMLElement>();
  const { module, verify } = useFigures(featured?.slug ?? null, howNear);

  // The mono eyebrow. Zero segments are dropped rather than printed as `00`.
  const eyebrow = [
    'Environments',
    builtCount > 0 ? `${pad2(builtCount)} live` : null,
    writtenCount > 0 ? `${pad2(writtenCount)} spec` : null,
  ]
    .filter((part): part is string => part !== null)
    .join(' · ');

  // The second sentence of the lede, in English at every count.
  const builtClause =
    builtCount === 0
      ? 'None of these is built yet'
      : `${CountWord(builtCount)} of these ${builtCount === 1 ? 'is' : 'are'} built and playable`;
  const writtenClause =
    writtenCount === 0
      ? ''
      : `; ${countWord(writtenCount)} more ${writtenCount === 1 ? 'is' : 'are'} written`;

  const pickSentence =
    builtCount === 0
      ? 'Nothing is playable yet; every environment below is a written specification.'
      : `${CountWord(builtCount)} ${builtCount === 1 ? 'environment is' : 'environments are'} built and playable in this tab.${
          writtenCount === 0
            ? ''
            : ` The other ${countWord(writtenCount)} ${writtenCount === 1 ? 'is' : 'are'} written: the task, the grader, the counterweight and the command that evaluates it.`
        }`;

  const equation = module
    ? `${module.reward.components
        .map((component) => `${component.weight.toFixed(2)} · ${component.key}`)
        .join(' + ')} = reward`
    : null;

  return (
    <main>
      {/* ── 1. Masthead ─────────────────────────────────────────────────── */}
      <section className={`${s.shell} pt-10 sm:pt-14`}>
        <SectionLabel>{eyebrow}</SectionLabel>
        <h1 className={`${s.display} mt-3 max-w-[20ch]`}>
          An environment is an eval you can take the gradient of.
        </h1>
        <p className={`${s.lede} mt-5`}>
          A task, the moves that are allowed, a grader that computes instead of judging, and a score
          that moves when the model gets better. {builtClause}
          {writtenClause}.
        </p>

        <DefinitionStrip className="mt-8" />

        <div className="mt-6 flex flex-col gap-3 sm:flex-row sm:items-center">
          {featured ? (
            <Link className={`${s.btnPrimary} min-h-12 sm:min-h-11`} to={featured.href}>
              Play {featured.title}
              <ArrowRight aria-hidden="true" className="size-4" />
            </Link>
          ) : null}
          <a className={`${s.btnSecondary} min-h-12 sm:min-h-11`} href="#how">
            What an environment is
            <ArrowDown aria-hidden="true" className="size-4" />
          </a>
        </div>
      </section>

      {/* ── 2. Pick one ─────────────────────────────────────────────────── */}
      <section aria-labelledby="pick" className={`${s.shell} ${s.section}`} id="play">
        <SectionLabel>Playable</SectionLabel>
        <h2 className={`${s.h2} mt-2`} id="pick">
          Pick one. You are playing it in a click.
        </h2>
        <p className={`${s.prose} mt-3`}>{pickSentence}</p>

        {builtCount > 0 ? (
          <ul
            className={`mt-6 grid gap-4 ${
              builtCount >= 3 ? 'lg:grid-cols-3' : builtCount === 2 ? 'lg:grid-cols-2' : ''
            }`}
          >
            {builtEnvironments.map((entry) => (
              <li className="flex" key={entry.id}>
                <FeaturedEnvironment className="w-full" entry={entry} />
              </li>
            ))}
          </ul>
        ) : null}
      </section>

      {/* ── 3. How it works ─────────────────────────────────────────────── */}
      <section
        aria-labelledby="how-heading"
        className={`${s.shell} ${s.section} scroll-mt-[var(--app-header-h)]`}
        id="how"
        ref={howRef}
      >
        <SectionLabel>
          The same machine, {countWord(totalCount)} times
        </SectionLabel>
        <h2 className={`${s.h2} mt-2`} id="how-heading">
          The same machine, four times over.
        </h2>
        <p className={`${s.prose} mt-3`}>
          Every environment on this site is the four parts above. The task changes and the grader
          changes; the shape never does. Here it is on the {featured ? featured.title : 'first'}{' '}
          board, with that environment’s own text in each box.
        </p>

        <div className="mt-6">
          {module ? (
            <EnvAnatomy anatomy={module.anatomy} rewardLine={module.meta.rewardLine} />
          ) : (
            // The same footprint as the four boxes, so the page does not jump
            // when the module lands.
            <div aria-hidden="true" className="grid gap-3 lg:grid-cols-4">
              {[0, 1, 2, 3].map((i) => (
                <Skeleton className="h-32 rounded-xl" key={i} />
              ))}
            </div>
          )}
        </div>

        <ol className="mt-8 border-b border-border">
          <Passage
            figure={
              <Figure caption="compare(guess, answer) → G Y X X G">
                <TileRow />
              </Figure>
            }
            index="01"
            title="The grader computes."
          >
            Five letters go in, five verdicts come out, and the same guess scores the same way every
            time. No judge reads the reply and no rubric is consulted. A recorded{' '}
            <Term term="rollout">rollout</Term> is a file of those comparisons, one per turn.
          </Passage>

          <Passage
            figure={
              <Figure caption={module ? `${module.meta.title} · shipped weights` : 'shipped weights'}>
                <p className={`min-h-[1.4em] ${FIGURE_LINE}`}>
                  {equation ?? <span className="text-muted">— · — + — · — + — · — = reward</span>}
                </p>
              </Figure>
            }
            index="02"
            title="The reward is the product."
          >
            Pay for winning, take away for wasted guesses, and the model will find whichever
            behaviour you forgot to price. The term that takes points away is the{' '}
            <Term term="counterweight">counterweight</Term>; it is the half of the reward worth an
            argument.
          </Passage>

          <Passage
            figure={
              <Figure
                caption={
                  verify
                    ? verifySummary(verify)
                    : 'Recomputed in your browser from the recorded moves, once they load.'
                }
                sentence
              >
                <p className={FIGURE_LINE}>
                  recomputed {fixed(verify?.recomputed ?? null, 3)} · recorded{' '}
                  {fixed(verify?.recorded ?? null, 3)} · Δ {fixed(verify?.delta ?? null, 7)}
                </p>
              </Figure>
            }
            index="03"
            title="The numbers are receipts."
          >
            Every score on this site is recomputed in your browser from the recorded moves and set
            beside the number in the file. The one published training result is measured on{' '}
            <Term term="held-out">held-out</Term> words the model never trained on.
          </Passage>
        </ol>
      </section>

      {/* ── 4. Written but not built ────────────────────────────────────── */}
      {writtenCount === 0 ? null : (
        <section aria-labelledby="written" className={`${s.shell} ${s.section}`}>
          <div className="flex flex-wrap items-end justify-between gap-3">
            <h2 className={s.h2} id="written">
              The {writtenCount} we have written but not built.
            </h2>
            <span className={s.chipOutline}>{PROPOSAL_NOTICE}</span>
          </div>
          <p className={`${s.prose} mt-3`}>
            Each one is a task, a reward in a number your board already reads, and the counterweight
            that stops that reward being farmed. Every row opens the whole argument, including where
            it stops being honest.
          </p>

          {/* A list, not a wall of cards: twelve rows the eye can scan in one
              pass, each ranked in the order we would build it. */}
          <EnvironmentList className="mt-6" entries={writtenEnvironments} />

          <Link className={`${s.btnSecondary} mt-6`} to={routes.gallery}>
            Filter the lineup by industry
            <ArrowRight aria-hidden="true" className="size-4" />
          </Link>
        </section>
      )}

      {/* ── 5. Evidence ─────────────────────────────────────────────────── */}
      <section aria-labelledby="evidence" className={`${s.shell} ${s.section}`}>
        <h2 className="sr-only" id="evidence">
          Why this environment, and what has been measured on it
        </h2>
        <div className="grid gap-4 lg:grid-cols-2">
          <div className="card p-4 sm:p-5">
            <SectionLabel>Why a word game</SectionLabel>
            <h3 className={`${s.h3} mt-2`}>We didn’t pick a game. We picked theirs.</h3>
            <p className={`${s.prose} mt-3`}>
              The word game is Prime Intellect’s own hello-world: a basic example in their trainer, a
              shipped environment in their library, and the environment their official tutorial
              optimises prompts against.
            </p>
            <ul className="mt-4 divide-y divide-border">
              {helloWorldCitations.map((c) => (
                <li key={c.href}>
                  <a
                    className="tap flex items-start gap-2 py-2.5 transition-colors duration-1 ease-enter hover:text-fg"
                    href={c.href}
                    rel="noreferrer noopener"
                    target="_blank"
                  >
                    <ArrowUpRight
                      aria-hidden="true"
                      className="mt-0.5 size-4 shrink-0 text-border-strong"
                    />
                    <span className="min-w-0">
                      <span className="block font-mono text-mono-data text-fg">{c.label}</span>
                      <span className="block text-small text-fg-2">{c.claim}</span>
                      <span className="sr-only">(opens in a new tab)</span>
                    </span>
                  </a>
                </li>
              ))}
            </ul>
          </div>

          {/* A citation of a published result, worded as one. No logo, no
              "partner", nothing that reads as endorsement. */}
          <div className="card p-4 sm:p-5">
            <SectionLabel>Published by Prime Intellect</SectionLabel>
            <p className="mt-3 flex flex-wrap items-baseline gap-x-3 gap-y-1 font-mono">
              <span className="text-figure text-muted lg:text-figure-lg">{trainingResult.before}</span>
              <ArrowRight aria-hidden="true" className="size-6 shrink-0 self-center text-border-strong" />
              <span className="text-figure text-positive lg:text-figure-lg">{trainingResult.after}</span>
            </p>
            <p className={`${s.prose} mt-3`}>
              <span className="font-mono text-mono-data text-fg">{trainingResult.model}</span>{' '}
              {trainingResult.metric} on this task, before and after training. Out of the box it
              never once guesses the word; after an {trainingResult.method}, it wins about six games
              in ten. Measured on {trainingResult.evalDescription}. Both checkpoints are public, so
              the claim is checkable rather than quotable.
            </p>
            <p className="mt-4 flex flex-wrap gap-2">
              <a
                className="tap inline-flex items-center gap-1 rounded-md border border-border-strong px-3 font-mono text-mono-data text-fg transition-colors duration-1 ease-enter hover:bg-surface-2"
                href={trainingResult.source.href}
                rel="noreferrer noopener"
                target="_blank"
              >
                The write-up
                <ArrowUpRight aria-hidden="true" className="size-3.5" />
              </a>
              {trainingResult.checkpoints.map((c) => (
                <a
                  className="tap inline-flex items-center gap-1 rounded-md border border-border-strong px-3 font-mono text-mono-data text-fg transition-colors duration-1 ease-enter hover:bg-surface-2"
                  href={c.href}
                  key={c.href}
                  rel="noreferrer noopener"
                  target="_blank"
                >
                  {c.label.replace('PrimeIntellect/', '')}
                  <ArrowUpRight aria-hidden="true" className="size-3.5" />
                </a>
              ))}
            </p>
            <p className={`${s.caption} mt-3`}>{trainingResult.caveat}</p>
          </div>
        </div>
      </section>

      {/* ── 6. Close ────────────────────────────────────────────────────── */}
      <section className={`${s.shell} pb-12 sm:pb-16`}>
        <div className="flex flex-col gap-3 border-t border-border pt-8 sm:flex-row sm:items-center">
          <Link className={s.btnSecondary} to={routes.honesty}>
            What we measured, and what we didn’t
          </Link>
          <p className={s.small}>
            Every number on this site, with the one that is ours and the ones that are not.
          </p>
        </div>
      </section>
    </main>
  );
}
