//  File:    page.tsx
//  Path:    apps/web/app/occupations/page.tsx
//  Project: AI Risk Index — airiskindex.io
//  Author:  Simon-Pierre Boucher
//  Contact: contact@spboucher.ai
//  Copyright © 2026 Simon-Pierre Boucher. All rights reserved.
//
//  Description: Browse page: all occupations grouped by SOC major group with score chips.

import Link from "next/link";
import { prisma } from "@airiskindex/db";
import { SOC_MAJOR_GROUPS } from "@/lib/soc-groups";

export const dynamic = "force-dynamic";

export const metadata = {
  title: "All occupations — AI Risk Index",
};

export default async function OccupationsPage(): Promise<JSX.Element> {
  const run = await prisma.scoreRun.findFirst({ orderBy: { createdAt: "desc" } });
  const [occupations, scores] = await Promise.all([
    prisma.occupation.findMany({
      orderBy: { code: "asc" },
      select: { code: true, title: true },
    }),
    run
      ? prisma.occupationScore.findMany({
          where: { runId: run.id },
          select: { occupationCode: true, substitution: true },
        })
      : Promise.resolve([]),
  ]);
  const scoreByCode = new Map(scores.map((s) => [s.occupationCode, s.substitution]));

  const groups = new Map<string, typeof occupations>();
  for (const occupation of occupations) {
    const prefix = occupation.code.slice(0, 2);
    const list = groups.get(prefix) ?? [];
    list.push(occupation);
    groups.set(prefix, list);
  }

  return (
    <main className="mx-auto max-w-5xl px-4 py-10 sm:px-6 sm:py-12">
      <h1 className="text-3xl font-bold tracking-tight">All occupations</h1>
      <p className="mt-2 text-[var(--ink-2)]">
        {occupations.length.toLocaleString("en-US")} O*NET occupations across{" "}
        {groups.size} major groups. Occupations without a chip have not been scored in the latest
        run yet.
      </p>

      <nav className="mt-6 flex flex-wrap gap-2 text-xs">
        {[...groups.keys()].sort().map((prefix) => (
          <a
            key={prefix}
            href={`#g${prefix}`}
            className="rounded-full border border-[var(--border)] bg-[var(--surface-1)] px-3 py-1 text-[var(--ink-2)] hover:border-[var(--seq)]"
          >
            {SOC_MAJOR_GROUPS[prefix] ?? prefix}
          </a>
        ))}
      </nav>

      {[...groups.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([prefix, list]) => (
        <section key={prefix} id={`g${prefix}`} className="mt-10 scroll-mt-6">
          <h2 className="text-lg font-semibold tracking-tight">
            {SOC_MAJOR_GROUPS[prefix] ?? prefix}{" "}
            <span className="text-sm font-normal text-[var(--muted)]">({list.length})</span>
          </h2>
          <ul className="mt-3 grid gap-x-8 sm:grid-cols-2">
            {list.map((occupation) => {
              const score = scoreByCode.get(occupation.code);
              return (
                <li key={occupation.code} className="border-b border-[var(--grid)]">
                  <Link
                    href={`/occupations/${occupation.code}`}
                    className="flex items-baseline gap-2 py-2 hover:bg-[var(--wash)]"
                  >
                    <span className="min-w-0 truncate text-sm">{occupation.title}</span>
                    {score !== undefined && (
                      <span className="ml-auto shrink-0 rounded-full bg-[var(--seq-track)] px-2 py-0.5 text-xs font-semibold tabular-nums">
                        {score.toFixed(0)}
                      </span>
                    )}
                  </Link>
                </li>
              );
            })}
          </ul>
        </section>
      ))}
    </main>
  );
}
