/**
 * The Motion home, which exists to answer one question: is the motion actually
 * repeating?
 *
 * Every panel here is a different way of asking it. The rail asks whether the
 * library reaches the stages where work is actually sitting; the kind grid asks
 * whether the nine kinds are covered or whether three of them are aspirational;
 * the promotions panel asks whether anything has come *back* out of an
 * engagement, which is the only evidence that the loop closed at all.
 *
 * So the absences are promoted rather than hidden. A stage with live
 * engagements and no template is the most useful finding this page can carry —
 * it names the place where the next deployment will be improvised — and a
 * missing row rendered as a grey zero is a finding nobody reads. Gaps get a
 * warning tone, a count and a link that lands on the library already filtered
 * to the hole.
 *
 * Counting is the server's job: `/api/motion` returns all eight stages and all
 * nine kinds in ontology order, zeros included, so nothing here zero-fills.
 */
import { useQuery } from '@tanstack/react-query';
import { Link } from 'react-router-dom';
import {
  AlertTriangle,
  ArrowUpRight,
  Handshake,
  Library,
  Lock,
  RefreshCw,
  Sparkles,
} from 'lucide-react';
import {
  DEMAND_STAGE_LABELS,
  MOTION_KIND_DESCRIPTIONS,
  MOTION_KIND_LABELS,
  toPiggyPageRoute,
  type DemandStage,
  type EngagementStatus,
  type MotionKind,
} from '@pig/core';
import { StageRail } from '@/components/motion/StageRail';
import { MotionKindBadge } from '@/components/motion/MotionKindBadge';
import { percent } from '@/components/motion/format';
import {
  Badge,
  Button,
  Card,
  CardContent,
  CardHeader,
  CardTitle,
  EmptyState,
  Skeleton,
  Stat,
} from '@/components/ui';
import { get, relativeTime, shortDate } from '@/lib/api';
import { useIdentity } from '@/lib/identity';
import { canAny } from '@/lib/permissions';
import { usePiggyContext } from '@/lib/piggy-context';
import { usePageTitle } from '@/lib/title';

// ------------------------------------------------------------------- shapes

interface StageCoverage {
  stage: DemandStage;
  engagements: number;
  templates: number;
}

interface KindCoverage {
  kind: MotionKind;
  templates: number;
  shared: number;
}

interface EngagementSummary {
  id: string;
  demandDealId: string;
  dealName: string | null;
  stage: DemandStage | null;
  accountId: string | null;
  accountName: string | null;
  status: EngagementStatus;
  summary: string | null;
  openedAt: string;
  artifactCount: number;
  latestScore: { basisPoints: number; band: string } | null;
}

interface Promotion {
  templateId: string;
  slug: string;
  title: string;
  kind: MotionKind;
  version: number;
  promotedAt: string;
  engagementId: string | null;
  dealName: string | null;
}

interface MotionOverview {
  totals: {
    templates: number;
    shared: number;
    private: number;
    engagements: number;
    open: number;
    promotions: number;
  };
  stages: StageCoverage[];
  library: KindCoverage[];
  engagements: EngagementSummary[];
  promotions: Promotion[];
}

// --------------------------------------------------------------------- page

export function Motion() {
  usePageTitle('Motion');
  usePiggyContext({ type: 'page', route: toPiggyPageRoute('/motion'), label: 'Motion' });
  const me = useIdentity();

  const overview = useQuery({
    queryKey: ['motion', 'overview'],
    queryFn: () => get<MotionOverview>('/api/motion'),
    enabled: canAny(me, 'book:read'),
  });

  if (!canAny(me, 'book:read')) {
    return (
      <div className="flex min-w-0 flex-col gap-5">
        <PageHeader />
        <Card>
          <CardContent className="pt-5">
            <EmptyState
              icon={<Lock />}
              title="Motion is restricted"
              description="The library and its engagements sit behind book access. Ask a platform administrator for team membership."
            />
          </CardContent>
        </Card>
      </div>
    );
  }

  const data = overview.data;
  // A stage carrying live work with nothing in the library behind it. Both
  // halves matter: an empty stage with no template is a stage nobody is
  // working, which is not a gap in the motion.
  const gaps = (data?.stages ?? []).filter(
    (stage) => stage.templates === 0 && stage.engagements > 0,
  );
  const uncovered = (data?.stages ?? []).filter((stage) => stage.templates === 0);
  const empty = data ? data.totals.templates === 0 && data.totals.engagements === 0 : false;

  return (
    <div className="flex min-w-0 flex-col gap-5">
      <PageHeader />

      {overview.isLoading ? <Skeleton className="h-72" /> : null}

      {overview.isError ? (
        <Card>
          <CardContent className="pt-5">
            <EmptyState
              icon={<AlertTriangle />}
              title="Motion is unavailable"
              description={
                overview.error instanceof Error
                  ? overview.error.message
                  : 'The motion summary could not be loaded.'
              }
              action={
                <Button variant="outline" onClick={() => void overview.refetch()}>
                  <RefreshCw aria-hidden />
                  Try again
                </Button>
              }
            />
          </CardContent>
        </Card>
      ) : null}

      {empty ? (
        <Card>
          <CardContent className="pt-5">
            <EmptyState
              icon={<Library />}
              title="Nothing in the motion yet"
              description="A motion starts as one template. Author the discovery guide you already run from memory, then instantiate it into the next deal."
              action={
                <Button variant="primary" asChild>
                  <Link to="/motion/library">Open the library</Link>
                </Button>
              }
            />
          </CardContent>
        </Card>
      ) : null}

      {data && !empty ? (
        <>
          <section className="grid grid-cols-2 gap-2 sm:gap-3 xl:grid-cols-4">
            <Stat
              label="Templates"
              value={data.totals.templates}
              hint={`${data.totals.shared} shared · ${data.totals.private} private`}
            />
            <Stat
              label="Open engagements"
              value={data.totals.open}
              hint={`${data.totals.engagements} recorded in total`}
            />
            <Stat
              label="Stages covered"
              value={`${data.stages.length - uncovered.length}/${data.stages.length}`}
              hint={uncovered.length ? 'The library stops short of the motion' : 'Every stage has a template'}
              tone={gaps.length ? 'warning' : uncovered.length ? 'default' : 'positive'}
            />
            <Stat
              label="Promotions"
              value={data.totals.promotions}
              hint={data.totals.promotions ? 'Artefacts that came back as templates' : 'Nothing has closed the loop yet'}
            />
          </section>

          <Card>
            <CardHeader>
              <CardTitle>The motion, stage by stage</CardTitle>
              <p className="text-sm text-muted">
                Live engagements sit on the deal&rsquo;s stage. The second figure is what the
                library has waiting there.
              </p>
            </CardHeader>
            <CardContent className="flex min-w-0 flex-col gap-3">
              <StageRail counts={countsByStage(data.stages)} coverage={templatesByStage(data.stages)} />
              {gaps.length ? (
                <div className="flex min-w-0 gap-3 rounded-lg border border-warning/30 bg-warning/10 p-3 text-sm">
                  <AlertTriangle className="size-4 shrink-0 text-warning" aria-hidden />
                  <div className="min-w-0">
                    <p className="font-medium">
                      {gaps.length === 1 ? 'One stage is' : `${gaps.length} stages are`} being worked
                      without a template
                    </p>
                    <p className="mt-1 text-muted">
                      Whatever gets written there this week is written from memory, and the next
                      deal starts over. Author one from the engagement that is already in it.
                    </p>
                    <div className="mt-2 flex min-w-0 flex-wrap gap-2">
                      {gaps.map((gap) => (
                        <Link
                          key={gap.stage}
                          to={`/motion/library?stage=${gap.stage}`}
                          className="tap inline-flex min-h-11 min-w-0 items-center gap-1 rounded-lg bg-surface px-3 text-sm font-medium underline-offset-4 hover:underline"
                        >
                          <span className="truncate">{DEMAND_STAGE_LABELS[gap.stage]}</span>
                          <span className="nums shrink-0 text-muted">
                            {gap.engagements} live
                          </span>
                        </Link>
                      ))}
                    </div>
                  </div>
                </div>
              ) : null}
            </CardContent>
          </Card>

          <Card>
            <CardHeader className="flex-row items-start justify-between gap-3">
              <div className="min-w-0">
                <CardTitle>Library cover, by kind</CardTitle>
                <p className="text-sm text-muted">
                  Nine kinds. A kind with nothing in it is a conversation that gets improvised
                  every time.
                </p>
              </div>
              <Button variant="outline" size="sm" asChild className="shrink-0">
                <Link to="/motion/library">
                  <Library aria-hidden />
                  Library
                </Link>
              </Button>
            </CardHeader>
            <CardContent className="grid min-w-0 gap-2 sm:grid-cols-2 xl:grid-cols-3">
              {data.library.map((kind) => (
                <KindCoverageCard key={kind.kind} coverage={kind} />
              ))}
            </CardContent>
          </Card>

          <div className="grid min-w-0 gap-5 lg:grid-cols-2">
            <Card className="min-w-0">
              <CardHeader>
                <CardTitle>What got easier</CardTitle>
                <p className="text-sm text-muted">
                  Artefacts an engagement proved, promoted back into the library as a new version.
                </p>
              </CardHeader>
              <CardContent className="flex min-w-0 flex-col gap-2">
                {data.promotions.length === 0 ? (
                  <EmptyState
                    icon={<Sparkles />}
                    title="Nothing promoted yet"
                    description="Finalise an artefact in an engagement and promote it. That is the step that makes the next deployment cheaper than this one."
                  />
                ) : (
                  data.promotions.map((promotion) => (
                    <PromotionRow key={promotion.templateId} promotion={promotion} />
                  ))
                )}
              </CardContent>
            </Card>

            <Card className="min-w-0">
              <CardHeader className="flex-row items-start justify-between gap-3">
                <div className="min-w-0">
                  <CardTitle>Open engagements</CardTitle>
                  <p className="text-sm text-muted">Where the library is being used right now.</p>
                </div>
                <Button variant="outline" size="sm" asChild className="shrink-0">
                  <Link to="/motion/engagements">
                    <Handshake aria-hidden />
                    All
                  </Link>
                </Button>
              </CardHeader>
              <CardContent className="flex min-w-0 flex-col gap-2">
                {data.engagements.length === 0 ? (
                  <EmptyState
                    icon={<Handshake />}
                    title="No open engagements"
                    description="Open one against a demand deal to file discovery, scoping and proposal work against its stages."
                  />
                ) : (
                  data.engagements.map((engagement) => (
                    <EngagementRow key={engagement.id} engagement={engagement} />
                  ))
                )}
              </CardContent>
            </Card>
          </div>
        </>
      ) : null}
    </div>
  );
}

// ---------------------------------------------------------------- fragments

function PageHeader() {
  return (
    <header className="min-w-0">
      <p className="text-xs font-semibold uppercase tracking-[0.16em] text-accent-fg">
        Go-to-market
      </p>
      <h1 className="mt-1 text-xl font-semibold tracking-tight sm:text-2xl">Motion</h1>
      <p className="mt-1 max-w-2xl text-sm leading-6 text-muted">
        The repeatable practice behind the ledger: what the library covers, where engagements
        actually sit, and what came back out of one.
      </p>
    </header>
  );
}

function KindCoverageCard({ coverage }: { coverage: KindCoverage }) {
  const missing = coverage.templates === 0;
  return (
    <Link
      to={`/motion/library?kind=${coverage.kind}`}
      className="tap flex min-h-11 min-w-0 flex-col gap-2 rounded-xl border border-border bg-surface-2/60 p-3 hover:bg-surface-2"
    >
      <div className="flex min-w-0 items-center justify-between gap-2">
        <MotionKindBadge kind={coverage.kind} />
        {missing ? (
          <Badge tone="warning" className="shrink-0">
            No template
          </Badge>
        ) : (
          <span className="nums shrink-0 text-sm font-semibold">{coverage.templates}</span>
        )}
      </div>
      <p className="min-w-0 line-clamp-2 text-xs leading-5 text-muted">
        {missing ? MOTION_KIND_DESCRIPTIONS[coverage.kind] : `${coverage.shared} shared to the book`}
      </p>
      <span className="sr-only">{MOTION_KIND_LABELS[coverage.kind]}</span>
    </Link>
  );
}

function PromotionRow({ promotion }: { promotion: Promotion }) {
  // The whole row is the target, as it is for `EngagementRow` in the card
  // beside it. A one-line text link is about 20px tall on a phone, and two
  // identically-shaped rows where only one can be tapped reads as a dead page
  // rather than as a smaller hit area.
  return (
    <Link
      to={`/motion/library/${promotion.templateId}`}
      className="tap flex min-h-11 min-w-0 items-start justify-between gap-3 rounded-lg bg-surface-2 p-3 hover:bg-border/60"
    >
      <div className="min-w-0">
        <p className="min-w-0 break-words font-medium">{promotion.title}</p>
        <p className="mt-1 flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1 text-xs text-muted">
          <MotionKindBadge kind={promotion.kind} iconOnly />
          <span className="nums whitespace-nowrap">v{promotion.version}</span>
          <span className="min-w-0 truncate">
            {promotion.dealName ? `from ${promotion.dealName}` : 'from an engagement'}
          </span>
        </p>
      </div>
      <span className="nums shrink-0 text-xs text-muted" title={shortDate(promotion.promotedAt)}>
        {relativeTime(promotion.promotedAt)}
      </span>
    </Link>
  );
}

function EngagementRow({ engagement }: { engagement: EngagementSummary }) {
  return (
    <Link
      to={`/motion/engagements/${engagement.id}`}
      className="tap flex min-h-11 min-w-0 items-start justify-between gap-3 rounded-lg bg-surface-2 p-3 hover:bg-border/60"
    >
      <div className="min-w-0">
        <p className="min-w-0 truncate font-medium">
          {engagement.dealName ?? 'Unnamed deal'}
        </p>
        <p className="mt-1 min-w-0 truncate text-xs text-muted">
          {engagement.accountName ?? 'Unknown account'} ·{' '}
          {engagement.stage ? DEMAND_STAGE_LABELS[engagement.stage] : 'No stage'} ·{' '}
          <span className="nums">{engagement.artifactCount}</span> artefact
          {engagement.artifactCount === 1 ? '' : 's'}
        </p>
      </div>
      <div className="flex shrink-0 items-center gap-2">
        {engagement.latestScore ? (
          <Badge tone="neutral" className="nums">
            {percent(engagement.latestScore.basisPoints)}
          </Badge>
        ) : null}
        <ArrowUpRight className="size-4 text-muted" aria-hidden />
      </div>
    </Link>
  );
}

function countsByStage(stages: StageCoverage[]): Partial<Record<DemandStage, number>> {
  return Object.fromEntries(stages.map((stage) => [stage.stage, stage.engagements]));
}

function templatesByStage(stages: StageCoverage[]): Partial<Record<DemandStage, number>> {
  return Object.fromEntries(stages.map((stage) => [stage.stage, stage.templates]));
}
