'use client';

import { useEffect, useId, useMemo, useState } from 'react';
import styles from './evidence-explorer.module.css';

type Job = {
  id: string;
  study: string;
  family?: string;
  condition?: string;
  profile?: string;
  profiles?: string[];
  rep: number;
};
type Entry = {
  id: string;
  phase: string;
  job: Job;
  status: string;
  data_path: string;
};
type Decision = {
  invocation_id: string;
  profile: string;
  requested_model: string;
  started_at: string;
  elapsed_seconds: number;
  status: string;
  failure?: string | null;
  replayed_without_new_inference: boolean;
  prompt: string;
  answer: string;
};
type Episode = {
  job: Job;
  status: { status: string; error?: string; metrics?: Record<string, unknown> };
  market_history?: {
    period: number;
    prices: number[];
    profits: number[];
    consumer_surplus: number;
    actions: { price: number; message: string; note: string }[];
  }[];
  transcript?: {
    turn: number;
    role: string;
    action: string;
    price: number | null;
    claimed_limit: number | null;
    message: string;
  }[];
  decisions: Decision[];
};

export function EvidenceExplorer({ base }: { base: string }) {
  const id = useId();
  const [entries, setEntries] = useState<Entry[]>([]);
  const [phase, setPhase] = useState('all');
  const [study, setStudy] = useState('all');
  const [selected, setSelected] = useState('');
  const [loaded, setLoaded] = useState<{
    id: string;
    data: Episode | null;
    error: string;
  } | null>(null);
  const [error, setError] = useState('');
  const [loading, setLoading] = useState(true);
  useEffect(() => {
    const controller = new AbortController();
    fetch(`${base}/experiment-index.json`, { signal: controller.signal })
      .then((response) => {
        if (!response.ok)
          throw new Error('The evidence index could not be loaded.');
        return response.json();
      })
      .then((raw: unknown) => {
        const data = raw as { episodes: Entry[] };
        if (controller.signal.aborted) return;
        setEntries(data.episodes);
        setLoading(false);
      })
      .catch((reason: Error) => {
        if (reason.name !== 'AbortError') {
          setError(reason.message);
          setLoading(false);
        }
      });
    return () => controller.abort();
  }, [base]);
  const choices = useMemo(
    () =>
      entries.filter(
        (entry) =>
          (phase === 'all' || entry.phase === phase) &&
          (study === 'all' || entry.job.study === study),
      ),
    [entries, phase, study],
  );
  const active = choices.find((entry) => entry.id === selected) ?? choices[0];
  const activeId = active?.id;
  const activePath = active?.data_path;
  const episode = loaded?.id === activeId ? loaded?.data : null;
  const episodeError = loaded && loaded.id === activeId ? loaded.error : '';
  useEffect(() => {
    if (!activeId || !activePath) return;
    const controller = new AbortController();
    fetch(`${base}/${activePath}`, { signal: controller.signal })
      .then((response) => {
        if (!response.ok)
          throw new Error(
            'This episode could not be loaded. Its ZIP remains available below.',
          );
        return response.json();
      })
      .then((raw: unknown) => {
        if (controller.signal.aborted) return;
        setLoaded({ id: activeId, data: raw as Episode, error: '' });
      })
      .catch((reason: Error) => {
        if (reason.name !== 'AbortError')
          setLoaded({ id: activeId, data: null, error: reason.message });
      });
    return () => controller.abort();
  }, [activeId, activePath, base]);

  return (
    <section
      className={styles.reader}
      aria-label="Recorded experimental evidence"
    >
      <div className={styles.filters}>
        <label htmlFor={`${id}-phase`}>
          Phase
          <select
            id={`${id}-phase`}
            value={phase}
            onChange={(event) => setPhase(event.target.value)}
          >
            <option value="all">All phases</option>
            {[...new Set(entries.map((entry) => entry.phase))].map((value) => (
              <option key={value}>{value}</option>
            ))}
          </select>
        </label>
        <label htmlFor={`${id}-study`}>
          Study
          <select
            id={`${id}-study`}
            value={study}
            onChange={(event) => setStudy(event.target.value)}
          >
            <option value="all">All studies</option>
            <option value="market">Repeated pricing</option>
            <option value="negotiation">Bilateral bargaining</option>
            <option value="calibration">Numeric calibration</option>
          </select>
        </label>
      </div>
      <label className={styles.episodeLabel} htmlFor={`${id}-episode`}>
        Episode · {choices.length} available
        <select
          id={`${id}-episode`}
          value={active?.id ?? ''}
          onChange={(event) => setSelected(event.target.value)}
        >
          {choices.map((entry) => (
            <option key={entry.id} value={entry.id}>
              {entry.phase} / {entry.job.id} · {entry.status}
            </option>
          ))}
        </select>
      </label>
      {error && <p role="alert">{error}</p>}
      {episodeError && <p role="alert">{episodeError}</p>}
      {loading && <output>Loading the evidence index…</output>}
      {!loading && !choices.length && <p>No episodes match these filters.</p>}
      {active && !episode && !episodeError && (
        <output>Loading this episode…</output>
      )}
      {episode && active && (
        <div>
          <p className={styles.meta}>
            <strong>{episode.job.id}</strong> · saved controller status:{' '}
            {episode.status.status} ·{' '}
            <a href={`${base}/${active.data_path}`}>Download episode JSON</a>
          </p>
          {episode.status.status === 'running' && (
            <p>
              This archived episode has an unfinished saved status. It is
              incomplete and is not an active experiment.
            </p>
          )}
          {episode.status.error && (
            <p>Recorded execution error: {episode.status.error}</p>
          )}
          {episode.status.metrics?.valid === false && (
            <p>
              The controller finished with an invalid business action. This is
              not a complete valid economic episode.
            </p>
          )}
          {!episode.decisions.length && (
            <p>No model response record is available for this episode.</p>
          )}
          {episode.market_history && (
            <div className={styles.tableWrap}>
              <table>
                <caption>
                  Executed prices and profits. Each row is one dependent period.
                </caption>
                <thead>
                  <tr>
                    <th scope="col">Period</th>
                    <th scope="col">Seller 0 price</th>
                    <th scope="col">Seller 1 price</th>
                    <th scope="col">Profits (0 / 1)</th>
                  </tr>
                </thead>
                <tbody>
                  {episode.market_history.map((row) => (
                    <tr key={row.period}>
                      <th scope="row">{row.period}</th>
                      <td>{row.prices[0]}</td>
                      <td>{row.prices[1]}</td>
                      <td>
                        {row.profits
                          .map((value) => value.toFixed(2))
                          .join(' / ')}
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          )}
          {episode.transcript && (
            <ol className={styles.transcript}>
              {episode.transcript.map((row) => (
                <li key={row.turn}>
                  <strong>
                    Decision {row.turn} · {row.role}: {row.action}
                    {row.price !== null ? ` ${row.price}` : ''}
                  </strong>
                  <p>{row.message || '[No message]'}</p>
                  {row.claimed_limit !== null && (
                    <small>Nonbinding numeric claim: {row.claimed_limit}</small>
                  )}
                </li>
              ))}
            </ol>
          )}
          <p>
            Open any saved response to inspect the complete experimental input.
            These are final outputs and requested short action notes;
            nonconforming final outputs are retained. Responses copied during
            recovery are marked; they are not new samples. The interrupted
            prefix preserves its old status and is not a live experiment.
          </p>
          {episode.decisions.map((decision, index) => (
            <details
              className={styles.decision}
              key={`${decision.invocation_id}-${index}`}
            >
              <summary>
                {index + 1}. {decision.requested_model} ·{' '}
                {decision.elapsed_seconds.toFixed(1)} s
                {decision.status !== 'succeeded' ? ' · failed invocation' : ''}
                {decision.replayed_without_new_inference
                  ? ' · preserved earlier response'
                  : ''}
              </summary>
              <p className={styles.meta}>
                Invocation {decision.invocation_id}
                <br />
                {decision.started_at}
              </p>
              {decision.failure && <p>Recorded failure: {decision.failure}</p>}
              <h4>Final response</h4>
              <pre>{decision.answer}</pre>
              <h4>Complete experimental input</h4>
              <pre>{decision.prompt}</pre>
            </details>
          ))}
          <details className={styles.decision}>
            <summary>Recorded settlement metrics</summary>
            <pre>{JSON.stringify(episode.status.metrics ?? {}, null, 2)}</pre>
          </details>
        </div>
      )}
    </section>
  );
}
