import { useMemo } from 'react';
import { Link, useSearchParams } from 'react-router-dom';

import {
  ALL_VERTICALS,
  EnvironmentGrid,
  FeaturedEnvironment,
  environments,
  filterKeys,
  VerticalFilter,
  type VerticalFilterValue,
} from '@/components/site/EnvironmentPicker';
import { routes } from '@/content/lineup';
import { PROPOSAL_NOTICE } from '@/content/verticals';
import { VERTICAL_LABELS } from '@/lib/demo-kit/registry';
import type { Vertical } from '@/lib/demo-kit/types';
import * as s from '@/content/styles';
import { pageTitle, useSeo } from '@/lib/seo';

/**
 * The fuller list.
 *
 * Home leads with the picker; this is where you go to see everything and cut it
 * by industry. The cards are the same component, so the two pages cannot drift
 * into describing the same environment two different ways — which is how an
 * honest site quietly becomes a dishonest one.
 */

/** What a `spec` has to contain before it is allowed on this page. Contract rule 12. */
const SPEC_PARTS: readonly { label: string; body: string }[] = [
  { label: 'A task', body: 'One unit of work with a beginning and an end.' },
  { label: 'Legal moves', body: 'What the agent is allowed to do, and what is refused.' },
  { label: 'A grader', body: 'Deterministic code that marks the attempt. No judge, no rubric.' },
  {
    label: 'A counterweight',
    body: 'The term that stops the objective being maximised the crude way.',
  },
];

export default function Gallery() {
  useSeo({
    title: pageTitle('Every environment'),
    description:
      'Every environment in the lineup: the ones that are built and playable, and the ones published as a specification with the task, the grader and the counterweight written out.',
    canonical: routes.gallery,
    ogImage: '/og/gallery.png',
  });

  /*
   * The filter lives in the URL, not in component state. A filtered gallery is
   * the thing somebody pastes into a message, and it also means the back button
   * undoes a filter change, which is what people expect it to do.
   */
  const [params, setParams] = useSearchParams();
  const raw = params.get('vertical');
  const active: VerticalFilterValue =
    raw && filterKeys.includes(raw as Vertical) ? (raw as Vertical) : ALL_VERTICALS;

  const shown = useMemo(
    () =>
      active === ALL_VERTICALS
        ? environments
        : environments.filter((entry) => entry.verticalKey === active),
    [active],
  );

  const built = shown.filter((entry) => entry.status === 'live');
  const written = shown.filter((entry) => entry.status === 'spec');

  function select(next: VerticalFilterValue) {
    // `replace` so a run of filter taps leaves one entry in history, not eight.
    if (next === ALL_VERTICALS) setParams({}, { replace: true });
    else setParams({ vertical: next }, { replace: true });
  }

  return (
    <main className={`${s.shell} py-10 sm:py-16`}>
      <p className={s.eyebrow}>Gallery</p>
      <h1 className={`${s.h1} mt-3 max-w-3xl`}>Every environment we have built or specified.</h1>
      <p className={`${s.lede} mt-5 max-w-2xl`}>
        A built environment is playable in this tab, on the same code the repository ships. A spec is
        a written environment — task, action set, grader, counterweight and the command that
        evaluates it — published in full, with no interactive surface yet. There are no coming-soon
        cards here.
      </p>

      {filterKeys.length > 1 ? (
        <div className="mt-8">
          <h2 className="sr-only">Filter by vertical</h2>
          <VerticalFilter active={active} onSelect={select} />
        </div>
      ) : null}

      {/*
        The count changes without a page load and without moving focus, so it
        is announced. Without this, a filter tap is silent to a screen reader.
      */}
      <p aria-live="polite" className="mt-6 text-sm text-muted">
        {shown.length === 1 ? '1 environment' : `${shown.length} environments`}
        {active === ALL_VERTICALS ? '' : ` in ${VERTICAL_LABELS[active]}`}
        {shown.length === 0 ? '' : ` · ${built.length} built · ${written.length} written`}
      </p>

      {shown.length === 0 ? (
        <div className="card mt-4 p-6">
          {/* Two different nothings. Telling a visitor "no environment is filed
              under this vertical" when they have not filtered anything reads as
              a broken page rather than an empty one. */}
          <h2 className={s.h3}>
            {active === ALL_VERTICALS
              ? 'No environments are registered.'
              : 'Nothing under this vertical.'}
          </h2>
          <p className={`${s.prose} mt-2`}>
            {active === ALL_VERTICALS
              ? 'The registry is empty and the lineup is empty with it, which means the site is mid-build rather than hiding something.'
              : 'No environment is filed here yet, built or written.'}
          </p>
          {active === ALL_VERTICALS ? null : (
            <button
              className={`${s.btnSecondary} mt-4`}
              onClick={() => select(ALL_VERTICALS)}
              type="button"
            >
              Show every environment
            </button>
          )}
        </div>
      ) : null}

      {built.length > 0 ? (
        <section aria-labelledby="built" className="mt-4">
          <h2 className={s.h2} id="built">
            {built.length === 1 ? 'The one you can play' : 'The ones you can play'}
          </h2>
          <div className="mt-4 space-y-4">
            {built.map((entry) => (
              <FeaturedEnvironment entry={entry} key={entry.id} />
            ))}
          </div>
        </section>
      ) : null}

      {written.length > 0 ? (
        <section aria-labelledby="written" className="mt-10">
          <div className="flex flex-wrap items-end justify-between gap-3">
            <h2 className={s.h2} id="written">
              {written.length === 1 ? 'The one that is written' : `The ${written.length} that are written`}
            </h2>
            <span className={s.proposalPill}>{PROPOSAL_NOTICE}</span>
          </div>
          <EnvironmentGrid className="mt-4" entries={written} />
        </section>
      ) : null}

      <div className="card mt-12 p-5 sm:p-7">
        <h2 className={s.h2}>What a spec has to contain</h2>
        <p className={`${s.prose} mt-3 max-w-2xl`}>
          A card that says a demo is coming is not a specification, and the build refuses one. Every
          written environment above states all four of these, and the reward weights that go with
          them, before it is allowed on this page.
        </p>
        <ol className="mt-5 grid gap-3 sm:grid-cols-2 lg:grid-cols-4">
          {SPEC_PARTS.map((part, i) => (
            <li className="card bg-surface-2 p-4" key={part.label}>
              <span className="nums text-xs font-semibold text-brand">0{i + 1}</span>
              <h3 className={`${s.h3} mt-1.5`}>{part.label}</h3>
              <p className={`${s.prose} mt-1.5 text-sm`}>{part.body}</p>
            </li>
          ))}
        </ol>
        <Link className={`${s.btnSecondary} mt-6`} to={routes.honesty}>
          What we measured, and what we didn’t
        </Link>
      </div>
    </main>
  );
}
