//  File:    page.tsx
//  Path:    apps/web/app/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: Home page: hero, live search, ranking (most/least exposed), group averages.

import Link from "next/link";
import { prisma } from "@airiskindex/db";
import { OccupationSearch } from "@/components/occupation-search";
import { ScoreBar } from "@/components/score-marks";
import { SOC_MAJOR_GROUPS } from "@/lib/soc-groups";

export const dynamic = "force-dynamic";

const CONCEPTS = [
  {
    name: "Exposure",
    description:
      "AI is technically capable of performing the task. High exposure alone does not mean job loss — it means the occupation's tasks are changing.",
  },
  {
    name: "Substitution",
    description:
      "AI actually replaces the human performing the task, once cost, adoption velocity and real-world barriers are accounted for. This is the headline score.",
  },
  {
    name: "Augmentation",
    description:
      "AI assists the human, increasing productivity. For most occupations today, measured usage is augmentation, not replacement.",
  },
] as const;

interface RankRow {
  occupationCode: string;
  substitution: number;
  substitutionLow: number;
  substitutionHigh: number;
  exposure: number;
  augmentation: number;
  occupation: { code: string; title: string };
}

async function loadHome() {
  try {
    const [occupations, tasks, run] = await Promise.all([
      prisma.occupation.count(),
      prisma.task.count(),
      prisma.scoreRun.findFirst({ orderBy: { createdAt: "desc" } }),
    ]);
    if (!run) return { occupations, tasks, run: null, scored: 0, top: [], bottom: [], groups: [] };

    const [scored, top, bottom, groups] = await Promise.all([
      prisma.occupationScore.count({ where: { runId: run.id } }),
      prisma.occupationScore.findMany({
        where: { runId: run.id },
        orderBy: { substitution: "desc" },
        take: 15,
        include: { occupation: { select: { code: true, title: true } } },
      }),
      prisma.occupationScore.findMany({
        where: { runId: run.id },
        orderBy: { substitution: "asc" },
        take: 15,
        include: { occupation: { select: { code: true, title: true } } },
      }),
      prisma.$queryRaw<Array<{ prefix: string; avg: number; n: bigint }>>`
        SELECT left("occupationCode", 2) AS prefix,
               avg(substitution)::float AS avg,
               count(*) AS n
        FROM "OccupationScore"
        WHERE "runId" = ${run.id}
        GROUP BY 1
        HAVING count(*) >= 3
        ORDER BY 2 DESC
      `,
    ]);
    return { occupations, tasks, run, scored, top, bottom, groups };
  } catch {
    return { occupations: 0, tasks: 0, run: null, scored: 0, top: [], bottom: [], groups: [] };
  }
}

function RankList({ rows, startRank }: { rows: RankRow[]; startRank?: number }): JSX.Element {
  return (
    <ol className="overflow-hidden card">
      {rows.map((row, index) => (
        <li key={row.occupationCode} className="border-b border-[var(--grid)] last:border-b-0">
          <Link
            href={`/occupations/${row.occupationCode}`}
            className="group relative block px-5 py-3.5 transition-colors hover:bg-[var(--wash)]"
          >
            <div className="flex items-baseline gap-3">
              {startRank !== undefined && (
                <span className="w-6 shrink-0 text-right text-sm tabular-nums text-[var(--muted)]">
                  {startRank + index}
                </span>
              )}
              <span className="min-w-0 truncate font-medium">{row.occupation.title}</span>
              <span className="ml-auto pl-3 text-sm font-semibold tabular-nums">
                {row.substitution.toFixed(0)}
              </span>
            </div>
            <div className={`mt-2 ${startRank !== undefined ? "pl-9" : ""}`}>
              <ScoreBar
                band={{
                  low: row.substitutionLow,
                  score: row.substitution,
                  high: row.substitutionHigh,
                }}
                thick={8}
              />
            </div>
            <div className="pointer-events-none absolute right-5 top-1 z-10 hidden rounded-lg border border-[var(--border)] bg-[var(--surface-1)] px-3 py-2 text-xs text-[var(--ink-2)] shadow-sm group-hover:block">
              CI {row.substitutionLow.toFixed(0)}–{row.substitutionHigh.toFixed(0)} · exposure{" "}
              {row.exposure.toFixed(0)} · augmentation {row.augmentation.toFixed(0)}
            </div>
          </Link>
        </li>
      ))}
    </ol>
  );
}

export default async function HomePage(): Promise<JSX.Element> {
  const { occupations, tasks, run, scored, top, bottom, groups } = await loadHome();

  return (
    <main className="hero-wash mx-auto max-w-5xl px-4 py-10 sm:px-6 sm:py-16">
      <p className="inline-flex items-center gap-2 rounded-full border border-[var(--border)] bg-[var(--surface-1)] px-3 py-1 text-xs font-medium text-[var(--ink-2)]">
        <span aria-hidden="true" className="h-1.5 w-1.5 rounded-full bg-[var(--seq)]" />
        The transparent, task-based AI job-exposure index
      </p>
      <h1 className="mt-5 max-w-2xl text-4xl font-bold leading-[1.08] tracking-tight sm:text-6xl">
        How is your occupation exposed to AI — task by task?
      </h1>
      <p className="mt-4 max-w-2xl text-lg leading-relaxed text-[var(--ink-2)]">
        Every occupation is scored from its individual tasks by a multi-model rater panel — three
        sub-scores, confidence intervals from rater disagreement, versioned methodology, public
        API. Built for adaptation planning, not headlines.
      </p>

      <div className="mt-8">
        <OccupationSearch />
      </div>

      <div className="mt-8 grid grid-cols-2 gap-4 sm:grid-cols-4">
        {[
          [occupations.toLocaleString("en-US"), "occupations (O*NET 30.3)"],
          [tasks.toLocaleString("en-US"), "task statements"],
          [scored.toLocaleString("en-US"), "occupations scored"],
          [run ? run.indexVersion : "—", "methodology version"],
        ].map(([value, label]) => (
          <div
            key={label as string}
            className="card card-hover p-4"
          >
            <p className="text-2xl font-semibold">{value}</p>
            <p className="mt-1 text-xs text-[var(--muted)]">{label}</p>
          </div>
        ))}
      </div>

      {run && top.length > 0 && (
        <>
          <section id="ranking" className="mt-14">
            <div className="flex flex-wrap items-baseline justify-between gap-2">
              <h2 className="text-xl font-semibold tracking-tight">Substitution ranking</h2>
              <p className="text-xs text-[var(--muted)]">
                run {run.indexVersion} · {run.createdAt.toISOString().slice(0, 10)} ·{" "}
                <Link href="/ranking" className="underline hover:text-[var(--ink)]">
                  full index — all {scored.toLocaleString("en-US")} ranked →
                </Link>
              </p>
            </div>
            <p className="mt-1 max-w-2xl text-sm text-[var(--ink-2)]">
              Composite substitution pressure, 0–100. The whisker marks the confidence interval
              from rater disagreement — a wide band is a claim we hold loosely.
            </p>
            <div className="mt-6 grid gap-6 lg:grid-cols-2">
              <div className="min-w-0">
                <h3 className="mb-2 text-sm font-medium text-[var(--ink-2)]">Most exposed</h3>
                <RankList rows={top as RankRow[]} startRank={1} />
              </div>
              <div className="min-w-0">
                <h3 className="mb-2 text-sm font-medium text-[var(--ink-2)]">Least exposed</h3>
                <RankList rows={bottom as RankRow[]} />
              </div>
            </div>
          </section>

          {groups.length > 0 && (
            <section className="mt-14">
              <h2 className="text-xl font-semibold tracking-tight">By occupation group</h2>
              <p className="mt-1 text-sm text-[var(--ink-2)]">
                Mean substitution score across scored occupations in each SOC major group.
              </p>
              <div className="mt-5 overflow-hidden card">
                {groups.map((group) => (
                  <Link
                    key={group.prefix}
                    href={`/occupations#g${group.prefix}`}
                    className="block border-b border-[var(--grid)] px-5 py-3 last:border-b-0 hover:bg-[var(--wash)]"
                  >
                    <div className="flex items-baseline gap-3">
                      <span className="min-w-0 truncate text-sm">
                        {SOC_MAJOR_GROUPS[group.prefix] ?? group.prefix}
                      </span>
                      <span className="ml-auto text-xs text-[var(--muted)]">
                        {Number(group.n)} occupations
                      </span>
                      <span className="w-8 text-right text-sm font-semibold tabular-nums">
                        {group.avg.toFixed(0)}
                      </span>
                    </div>
                    <div
                      aria-hidden="true"
                      className="mt-1.5 h-[6px] rounded-r-[3px] bg-[var(--seq-track)]"
                    >
                      <div
                        className="h-full rounded-r-[3px] bg-[var(--seq)]"
                        style={{ width: `${Math.min(100, group.avg)}%` }}
                      />
                    </div>
                  </Link>
                ))}
              </div>
            </section>
          )}
        </>
      )}

      <section id="concepts" className="mt-16">
        <h2 className="text-xl font-semibold tracking-tight">
          Three scores, never collapsed into one
        </h2>
        <div className="mt-5 grid gap-4 sm:grid-cols-3">
          {CONCEPTS.map((concept) => (
            <div
              key={concept.name}
              className="card card-hover p-5"
            >
              <h3 className="font-semibold">{concept.name}</h3>
              <p className="mt-2 text-sm leading-relaxed text-[var(--ink-2)]">
                {concept.description}
              </p>
            </div>
          ))}
        </div>
      </section>
    </main>
  );
}
