<script lang="ts">
  import Board from "./Board.svelte";
  import { humanize } from "./net.svelte";
  import { fxForEvents, fxTtl, type BoardFx } from "./fx";
  import type { GameEvent, GameView } from "@wizwar/engine";

  let {
    steps,
    onclose,
  }: {
    steps: { seq: number; actor: string; events: GameEvent[]; view: GameView; chat?: { player: string; text: string }[] }[];
    onclose: () => void;
  } = $props();

  let idx = $state(0);
  let playing = $state(true);
  let speed = $state(1);
  const step = $derived(steps[Math.min(idx, steps.length - 1)]!);
  const lines = $derived(
    step.events.map(humanize).filter((l): l is string => l !== null),
  );
  const atEnd = $derived(idx >= steps.length - 1);

  /** Each step's spells flare on the reel exactly as they did at the table. */
  let boardFx = $state<BoardFx[]>([]);
  $effect(() => {
    const step = steps[Math.min(idx, steps.length - 1)];
    if (!step) return;
    const timers: ReturnType<typeof setTimeout>[] = [];
    for (const { fx, delay } of fxForEvents(step.events, step.view)) {
      timers.push(setTimeout(() => {
        boardFx = [...boardFx, fx];
        timers.push(setTimeout(() => (boardFx = boardFx.filter((f) => f.id !== fx.id)), fxTtl(fx.kind)));
      }, delay));
    }
    return () => { timers.forEach(clearTimeout); boardFx = []; };
  });

  $effect(() => {
    if (!playing) return;
    const t = setInterval(() => {
      if (idx < steps.length - 1) idx += 1;
      else playing = false;
    }, 1500 / speed);
    return () => clearInterval(t);
  });

  function onkeydown(e: KeyboardEvent) {
    if (e.key === "Escape") onclose();
    if (e.key === "ArrowRight") { playing = false; idx = Math.min(idx + 1, steps.length - 1); }
    if (e.key === "ArrowLeft") { playing = false; idx = Math.max(idx - 1, 0); }
    if (e.key === " ") { e.preventDefault(); playing = !playing; }
  }
</script>

<svelte:window {onkeydown} />

<div class="replay-scrim">
  <div class="replay" role="dialog" aria-modal="true" aria-label="what happened while you were away">
    <header class="replay-head">
      <span class="replay-title">{steps[0]?.seq === 0 ? "The whole tale, from the deal" : "While you were away"}</span>
      <span class="replay-count">move {idx + 1} of {steps.length}</span>
      <button class="replay-skip" onclick={onclose}>{atEnd ? "back to the game" : "skip to now"}</button>
    </header>
    <div class="replay-board">
      <Board view={step.view} effects={boardFx} />
    </div>
    <div class="replay-caption">
      <strong>{step.actor}</strong>
      {#each lines as line, i (i)}<div>{line}</div>{/each}
      {#if lines.length === 0 && !step.chat?.length}<div>…considers the maze.</div>{/if}
      {#each step.chat ?? [] as c, i (i)}
        <div class="reel-talk">💬 {c.player}: {c.text}</div>
      {/each}
    </div>
    <div class="replay-controls">
      <button onclick={() => { playing = false; idx = Math.max(0, idx - 1); }} aria-label="previous move">◀</button>
      <button class="playpause" onclick={() => (playing = !playing)} aria-label={playing ? "pause" : "play"}>
        {playing ? "❚❚" : "▶"}
      </button>
      <button onclick={() => { playing = false; idx = Math.min(steps.length - 1, idx + 1); }} aria-label="next move">▶</button>
      {#each [1, 2, 4] as x (x)}
        <button class="speed" class:current={speed === x} onclick={() => (speed = x)}>{x}×</button>
      {/each}
    </div>
  </div>
</div>

<style>
  .replay-scrim {
    position: fixed;
    inset: 0;
    background: rgba(10, 12, 16, 0.88);
    display: grid;
    place-items: center;
    z-index: 50;
    padding: 1rem;
  }
  .replay {
    background: #171a20;
    border: 1px solid rgba(233, 225, 203, 0.25);
    border-radius: 8px;
    width: min(46rem, 100%);
    max-height: calc(100dvh - 2rem);
    display: flex;
    flex-direction: column;
    padding: 0.8rem 1rem 1rem;
    color: #d8d2c0;
    font-family: "Archivo Narrow", sans-serif;
  }
  .replay-head {
    display: flex;
    align-items: baseline;
    gap: 0.8rem;
    margin-bottom: 0.6rem;
  }
  .replay-title {
    font-family: "Oswald", sans-serif;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    font-size: 0.85rem;
    color: #e9e1cb;
  }
  .replay-count { font-size: 0.8rem; color: #8d8672; }
  .replay-skip {
    margin-left: auto;
    background: none;
    border: none;
    color: #a49c86;
    text-decoration: underline;
    cursor: pointer;
    font-size: 0.85rem;
  }
  .replay-board {
    min-height: 0;
    display: flex;
    justify-content: center;
  }
  .replay-board :global(svg.board) {
    max-height: calc(100dvh - 15rem);
    width: auto;
    max-width: 100%;
  }
  .reel-talk { font-style: italic; opacity: 0.85; }
  .replay-caption {
    background: #efe8d4;
    color: #3a2f1f;
    border-radius: 3px;
    padding: 0.5rem 0.75rem;
    margin-top: 0.7rem;
    font-family: "Courier Prime", monospace;
    font-size: 0.78rem;
    line-height: 1.45;
    min-height: 3.4rem;
  }
  .replay-controls .speed {
    font-size: 0.75rem;
    opacity: 0.7;
  }
  .replay-controls .speed.current { opacity: 1; text-decoration: underline; }
  .replay-controls {
    display: flex;
    justify-content: center;
    gap: 0.6rem;
    margin-top: 0.6rem;
  }
  .replay-controls button {
    background: #e9e1cb;
    color: #43331f;
    border: 1.5px solid #43331f;
    border-radius: 3px;
    min-width: 2.6rem;
    padding: 0.35rem 0.6rem;
    cursor: pointer;
    font-size: 0.9rem;
  }
  .playpause { min-width: 3.4rem; }
</style>
