import { useState } from 'react';
import type { ComponentType } from 'react';
import { Eye } from 'lucide-react';
import type { DemoStep } from '@/lib/demo-kit/types';
import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';
import { InterventionChip, Label } from './chrome';
import { formatOrDash } from './format';

export interface BlindCompareRun<T> {
  runId: string;
  /** The identity, revealed only after the visitor commits. */
  label: string;
  model: string;
  /** Required by the contract on an `intervened` run; shown at reveal. */
  intervention?: string;
  steps: DemoStep<T>[];
  total: number | null;
}

export type BlindVote = 'A' | 'B' | 'tie';

export interface BlindCompareProps<T> {
  /** Both runs must be the same seed or the comparison is meaningless. */
  seed: number;
  a: BlindCompareRun<T>;
  b: BlindCompareRun<T>;
  Surface: ComponentType<{ state: T; compact?: boolean }>;
  question?: string;
  onVote?: (vote: BlindVote) => void;
  className?: string;
}

/**
 * Two runs on the same seed, unlabelled, until you commit.
 *
 * The point is not the vote. The point is that the visitor forms an opinion
 * from the behaviour BEFORE they learn which one had the better prompt, the
 * bigger model or the training run — because once they know, they cannot
 * unknow it, and every "obviously the trained one looks better" is worthless
 * after the fact.
 *
 * The two vote buttons are OUTLINE, not green: two green fills on the Watch
 * tab would compete with ▶, and a vote is a question, not a call to action.
 *
 * There is a "reveal without voting" escape on purpose: a visitor who does not
 * want to play should not be held hostage by a modal-shaped page.
 */
export function BlindCompare<T>({
  seed,
  a,
  b,
  Surface,
  question = 'Which agent would you rather have running this?',
  onVote,
  className,
}: BlindCompareProps<T>) {
  const [vote, setVote] = useState<BlindVote | null>(null);
  const [revealed, setRevealed] = useState(false);

  const commit = (choice: BlindVote) => {
    setVote(choice);
    setRevealed(true);
    onVote?.(choice);
  };

  const winner: 'A' | 'B' | 'tie' =
    a.total === null || b.total === null
      ? 'tie'
      : a.total > b.total
        ? 'A'
        : b.total > a.total
          ? 'B'
          : 'tie';

  const sides: { id: 'A' | 'B'; run: BlindCompareRun<T> }[] = [
    { id: 'A', run: a },
    { id: 'B', run: b },
  ];

  return (
    <section aria-label="Blind comparison" className={cn('space-y-3', className)}>
      <div className="flex flex-wrap items-baseline gap-x-3 gap-y-1">
        <h3 className="text-h3 text-fg">{question}</h3>
        <p className="font-mono text-label uppercase tracking-label text-muted">
          Same puzzle · seed {seed}
        </p>
      </div>

      <div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
        {sides.map(({ id, run }) => {
          const last = run.steps[run.steps.length - 1];
          const picked = vote === id;
          const won = revealed && winner === id;
          return (
            <div
              key={id}
              className={cn(
                'flex flex-col gap-3 rounded-xl border bg-surface p-3 transition-colors duration-2 ease-enter sm:p-4',
                won ? 'border-accent-fg' : picked ? 'border-border-strong' : 'border-border',
              )}
            >
              <div className="flex items-center justify-between gap-2">
                <Label as="h4" className="text-fg">
                  Agent {id}
                </Label>
                <span className="font-mono text-label uppercase tracking-label text-muted">
                  {run.steps.length} steps
                </span>
              </div>

              <div className="max-w-full overflow-x-auto rounded-md bg-surface-2 p-3">
                {last ? (
                  <div className="mx-auto w-fit max-w-full sm:max-w-[20rem]">
                    <Surface state={last.state} />
                  </div>
                ) : (
                  <p className="text-ui text-muted">This run recorded no steps.</p>
                )}
              </div>

              {!revealed ? (
                <Button
                  variant="outline"
                  size="touch"
                  className="w-full sm:min-h-12"
                  onClick={() => commit(id)}
                >
                  Agent {id} is better
                </Button>
              ) : (
                <dl className="space-y-1 text-ui">
                  <div className="flex items-baseline justify-between gap-2">
                    <dt className="text-muted">Identity</dt>
                    <dd className="text-right font-medium text-fg">{run.label}</dd>
                  </div>
                  <div className="flex items-baseline justify-between gap-2">
                    <dt className="text-muted">Model</dt>
                    <dd className="min-w-0 break-all text-right font-mono text-mono-data text-fg">
                      {run.model}
                    </dd>
                  </div>
                  <div className="flex items-baseline justify-between gap-2">
                    <dt className="text-muted">Intervention</dt>
                    <dd className="text-right">
                      {run.intervention ? (
                        <InterventionChip>{run.intervention}</InterventionChip>
                      ) : (
                        <span className="text-small text-muted">none — plain rollout</span>
                      )}
                    </dd>
                  </div>
                  <div className="flex items-baseline justify-between gap-2 border-t border-border pt-1">
                    <dt className="text-muted">Reward</dt>
                    <dd className="flex items-center gap-1.5 text-right font-mono text-stat text-fg">
                      {won ? (
                        <span className="font-mono text-label uppercase tracking-label text-positive">
                          higher
                        </span>
                      ) : null}
                      {formatOrDash(run.total)}
                    </dd>
                  </div>
                </dl>
              )}
            </div>
          );
        })}
      </div>

      {!revealed ? (
        <div className="flex flex-wrap gap-2">
          <Button variant="outline" size="touch" onClick={() => commit('tie')}>
            Too close to call
          </Button>
          <Button variant="ghost" size="touch" className="text-muted hover:text-fg" onClick={() => setRevealed(true)}>
            <Eye aria-hidden="true" />
            Just show me
          </Button>
        </div>
      ) : (
        <p
          role="status"
          className="max-w-measure rounded-xl border border-border bg-surface-2 p-3 text-prose text-fg-2 sm:p-4"
        >
          {vote === null ? (
            <>Revealed without a vote. </>
          ) : vote === winner ? (
            <>
              <span className="font-medium text-positive">You picked the higher-scoring run.</span>{' '}
            </>
          ) : (
            <>
              <span className="font-medium text-warning">You picked the lower-scoring run.</span>{' '}
            </>
          )}
          The environment scored these two with the same grader, on the same seed. The difference
          between them is stated above — and if it is a prompt change rather than a training run,
          it says so, because a prompt change presented as a training result is the oldest trick
          in this business.
        </p>
      )}
    </section>
  );
}
