<script lang="ts">
  import type { GameView } from "@wizwar/engine";
  import type { Side } from "@wizwar/engine";

  const CELL = 48;
  const WALL = 7;

  let {
    view,
    edgeSelectMode = false,
    selectedCreatureId = null,
    onCellClick,
    onEdgeClick,
    onPlayerClick,
    onCreatureClick,
    onWarpClick,
  }: {
    view: GameView;
    edgeSelectMode?: boolean;
    selectedCreatureId?: string | null;
    onCellClick?: (cell: { x: number; y: number }) => void;
    onEdgeClick?: (cell: { x: number; y: number }, side: Side) => void;
    onPlayerClick?: (playerId: string) => void;
    onCreatureClick?: (creatureId: string) => void;
    onWarpClick?: (cell: { x: number; y: number }, side: Side) => void;
  } = $props();

  const PLAYER_COLORS = ["#1a9c46", "#d3352b", "#c9308f", "#3a3ac0", "#2ab0c9", "#c9a72a"];

  function playerColor(id: string): string {
    const idx = view.players.findIndex((p) => p.id === id);
    return PLAYER_COLORS[idx % PLAYER_COLORS.length]!;
  }

  const cells = $derived(
    Object.keys(view.board.cells).map((k) => {
      const [x, y] = k.split(",").map(Number);
      return { x: x!, y: y! };
    }),
  );

  const edges = $derived(
    Object.entries(view.board.edges)
      .filter(([, state]) => state !== "open")
      .map(([key, state]) => {
        const [kind, coords] = key.split(":") as [string, string];
        const [x, y] = coords.split(",").map(Number) as [number, number];
        return { kind, x, y, state };
      }),
  );

  // Candidate edges for create/destroy wall clicks: every interior boundary.
  const edgeHitboxes = $derived.by(() => {
    if (!edgeSelectMode) return [];
    const boxes: { cell: { x: number; y: number }; side: Side; x: number; y: number; w: number; h: number }[] = [];
    for (const c of cells) {
      if (view.board.cells[`${c.x + 1},${c.y}`]) {
        boxes.push({ cell: c, side: "E", x: (c.x + 1) * CELL - 6, y: c.y * CELL + 4, w: 12, h: CELL - 8 });
      }
      if (view.board.cells[`${c.x},${c.y + 1}`]) {
        boxes.push({ cell: c, side: "S", x: c.x * CELL + 4, y: (c.y + 1) * CELL - 6, w: CELL - 8, h: 12 });
      }
    }
    return boxes;
  });

  // Group players by cell so co-located wizards fan out.
  const wizardsByCell = $derived.by(() => {
    const map = new Map<string, typeof view.players>();
    for (const p of view.players) {
      if (!p.alive) continue;
      const k = `${p.position.x},${p.position.y}`;
      map.set(k, [...(map.get(k) ?? []), p]);
    }
    return map;
  });
</script>

<svg
  viewBox={`-8 -8 ${view.board.width * CELL + 16} ${view.board.height * CELL + 16}`}
  class="board"
>
  <!-- floor -->
  {#each cells as c (`${c.x},${c.y}`)}
    <rect
      x={c.x * CELL} y={c.y * CELL} width={CELL} height={CELL}
      class="floor"
      role="button" tabindex="-1"
      onclick={() => onCellClick?.(c)}
      onkeydown={() => {}}
    />
  {/each}

  <!-- homes & treasure spaces -->
  {#each view.players as p (p.id)}
    {@const hx = p.home.x * CELL + CELL / 2}
    {@const hy = p.home.y * CELL + CELL / 2}
    {@const R = CELL * 0.36}
    {@const r = CELL * 0.14}
    <polygon
      points={Array.from({ length: 16 }, (_, i) => {
        const rad = (Math.PI / 8) * i - Math.PI / 2;
        const len = i % 2 === 0 ? R : r;
        return `${hx + Math.cos(rad) * len},${hy + Math.sin(rad) * len}`;
      }).join(" ")}
      class="home-star" fill={playerColor(p.id)}
    />
  {/each}
  {#each view.treasures as t (t.id)}
    {#if t.position}
      {@const tx = t.position.x * CELL + CELL / 2}
      {@const ty = t.position.y * CELL + CELL * 0.72}
      <g class="treasure-g">
        <circle cx={tx} cy={ty} r={8} class="treasure" fill={playerColor(t.owner)} />
        <circle cx={tx} cy={ty} r={4.5} class="treasure-swirl" />
        <circle cx={tx} cy={ty} r={1.6} class="treasure-core" />
      </g>
    {/if}
  {/each}

  <!-- terrain: solid stone and thornbushes -->
  {#each Object.entries(view.squareContents) as [key, content] (key)}
    {@const sx = Number(key.split(",")[0])}
    {@const sy = Number(key.split(",")[1])}
    {#if content.kind === "stone"}
      <rect x={sx * CELL + 2} y={sy * CELL + 2} width={CELL - 4} height={CELL - 4} class="stone" rx="4" />
    {:else if content.kind === "thornbush" || content.kind === "rosebush"}
      <circle cx={sx * CELL + CELL / 2} cy={sy * CELL + CELL / 2} r={CELL * 0.36}
        class={content.kind === "rosebush" ? "rose" : "bush"} />
    {:else if content.kind === "ooze" || content.kind === "slime"}
      <rect x={sx * CELL + 5} y={sy * CELL + 5} width={CELL - 10} height={CELL - 10} rx="10"
        class={content.kind === "ooze" ? "ooze" : "slime"} />
    {:else if content.kind === "dust"}
      <circle cx={sx * CELL + CELL / 2} cy={sy * CELL + CELL / 2} r={CELL * 0.4} class="dust" />
    {:else if content.kind === "pit"}
      <rect x={sx * CELL + 7} y={sy * CELL + 7} width={CELL - 14} height={CELL - 14} class="pit" />
    {:else if content.kind === "tacks"}
      <text x={sx * CELL + CELL / 2} y={sy * CELL + CELL / 2 + 4} class="tacks">✻✻</text>
    {:else if content.kind === "safe"}
      <rect x={sx * CELL + 10} y={sy * CELL + 12} width={CELL - 20} height={CELL - 22} rx="3" class="safe" />
    {/if}
  {/each}

  <!-- dimensional warp tokens -->
  {#each view.dimWarps as w, wi (wi)}
    {#each [w.a, w.b] as tok, i (i)}
      <circle cx={tok.x * CELL + CELL * 0.5} cy={tok.y * CELL + CELL * 0.5} r={CELL * 0.3}
        class="dimwarp" />
    {/each}
  {/each}

  <!-- boobytrap tokens: face-down for everyone (the caster knows the real one) -->
  {#each view.boobytraps as trap, ti (ti)}
    {#each trap.cells as tc, i (i)}
      <circle cx={tc.x * CELL + CELL * 0.26} cy={tc.y * CELL + CELL * 0.26} r="6"
        class="trap-token" class:trap-real={trap.realCell != null && tc.x === trap.realCell.x && tc.y === trap.realCell.y} />
    {/each}
  {/each}

  <!-- ground objects -->
  {#each Object.entries(view.groundObjects) as [key, objects] (key)}
    {@const gx = Number(key.split(",")[0])}
    {@const gy = Number(key.split(",")[1])}
    {#each objects as o, i (o.instanceId)}
      <rect
        x={gx * CELL + 6 + i * 8} y={gy * CELL + CELL - 16}
        width={12} height={10} rx="2" class="ground-object"
      >
        <title>{o.cardId}</title>
      </rect>
    {/each}
  {/each}

  <!-- walls & doors & firewalls -->
  {#each edges as e (`${e.kind}:${e.x},${e.y}`)}
    {@const cls = e.state === "door" ? "door" : e.state === "firewall" ? "firewall" : "wall"}
    {#if e.kind === "V"}
      <rect
        x={(e.x + 1) * CELL - WALL / 2} y={e.y * CELL - WALL / 2}
        width={WALL} height={CELL + WALL}
        class={cls}
      />
    {:else}
      <rect
        x={e.x * CELL - WALL / 2} y={(e.y + 1) * CELL - WALL / 2}
        width={CELL + WALL} height={WALL}
        class={cls}
      />
    {/if}
  {/each}

  <!-- illusions YOU know are fake: ghostly dashed lines -->
  {#each view.knownIllusionEdges as key (key)}
    {@const kind = key.split(":")[0]}
    {@const ix = Number(key.split(":")[1]?.split(",")[0])}
    {@const iy = Number(key.split(":")[1]?.split(",")[1])}
    {#if kind === "V"}
      <line x1={(ix + 1) * CELL} y1={iy * CELL} x2={(ix + 1) * CELL} y2={(iy + 1) * CELL} class="illusion" />
    {:else}
      <line x1={ix * CELL} y1={(iy + 1) * CELL} x2={(ix + 1) * CELL} y2={(iy + 1) * CELL} class="illusion" />
    {/if}
  {/each}

  <!-- warp openings: click the arrow (while standing on it) to step through -->
  {#each view.board.warps as w, i (i)}
    {@const wx = w.from.cell.x * CELL + CELL / 2 +
      (w.from.side === "E" ? CELL * 0.42 : w.from.side === "W" ? -CELL * 0.42 : 0)}
    {@const wy = w.from.cell.y * CELL + CELL / 2 +
      (w.from.side === "S" ? CELL * 0.42 : w.from.side === "N" ? -CELL * 0.42 : 0)}
    {@const standing = view.players.some((p) =>
      p.id === view.you && p.position.x === w.from.cell.x && p.position.y === w.from.cell.y)}
    <g
      class="warp-g" class:standing
      role="button" tabindex="-1"
      onclick={(ev) => { ev.stopPropagation(); onWarpClick?.(w.from.cell, w.from.side); }}
      onkeydown={() => {}}
    >
      <circle cx={wx} cy={wy} r={11} class="warp-hit" />
      <text x={wx} y={wy + 4.5} class="warp"
      >{w.from.side === "N" ? "↑" : w.from.side === "S" ? "↓" : w.from.side === "E" ? "→" : "←"}</text>
    </g>
  {/each}

  <!-- wizards -->
  {#each [...wizardsByCell.entries()] as [key, group] (key)}
    {#each group as p, i (p.id)}
      {@const cx = p.position.x * CELL + CELL / 2 + (group.length > 1 ? (i - (group.length - 1) / 2) * 14 : 0)}
      {@const cy = p.position.y * CELL + CELL * 0.36}
      <g
        role="button" tabindex="-1"
        onclick={(ev) => { ev.stopPropagation(); onPlayerClick?.(p.id); }}
        onkeydown={() => {}}
        class="wizard"
      >
        <circle {cx} {cy} r={12} fill={playerColor(p.id)} stroke="#2b2218" stroke-width="2" />
        <text x={cx} y={cy + 4} class="wizard-label">{p.id[0]?.toUpperCase()}</text>
        {#if p.carriedTreasureId}
          <circle cx={cx + 9} cy={cy + 9} r={5} class="carried" />
        {/if}
      </g>
    {/each}
  {/each}

  <!-- creatures -->
  {#each view.creatures as c (c.id)}
    {@const ccx = c.position.x * CELL + CELL * 0.72}
    {@const ccy = c.position.y * CELL + CELL * 0.7}
    <g
      role="button" tabindex="-1" class="creature"
      onclick={(ev) => { ev.stopPropagation(); onCreatureClick?.(c.id); }}
      onkeydown={() => {}}
    >
      <rect
        x={ccx - 10} y={ccy - 10} width={20} height={20} rx="3"
        transform={`rotate(45 ${ccx} ${ccy})`}
        class="creature-body"
        class:selected={c.id === selectedCreatureId}
        stroke={playerColor(c.controllerId)}
      >
        <title>{c.kind} ({c.controllerId}) — {c.damage}/{Number.isFinite(c.maxDamage) ? c.maxDamage : "∞"} dmg</title>
      </rect>
      <text x={ccx} y={ccy + 4} class="creature-label">{c.kind === "fire-imp" ? "I" : c.kind === "democratic-monster" ? "D" : c.kind[0]?.toUpperCase()}</text>
    </g>
  {/each}

  <!-- edge selection hitboxes -->
  {#each edgeHitboxes as h (`${h.cell.x},${h.cell.y},${h.side}`)}
    <rect
      x={h.x} y={h.y} width={h.w} height={h.h}
      class="edge-hit"
      role="button" tabindex="-1"
      onclick={(ev) => { ev.stopPropagation(); onEdgeClick?.(h.cell, h.side); }}
      onkeydown={() => {}}
    />
  {/each}
</svg>

<style>
  .board {
    width: 100%;
    max-width: 680px;
    background: #e3dcc7;
    border: 7px solid #2b2218;
    border-radius: 3px;
    box-shadow:
      0 0 0 2px #b7ad92,
      0 12px 34px rgba(0, 0, 0, 0.6);
  }
  .floor {
    fill: #e3dcc7;
    stroke: #b7ad92;
    stroke-width: 1.1;
    stroke-dasharray: 4 2.5;
    cursor: pointer;
  }
  .floor:hover { fill: #efe8d2; }
  .wall {
    fill: #f2ecd8;
    stroke: #2b2218;
    stroke-width: 1.6;
  }
  .door {
    fill: #8b5a2b;
    stroke: #2b2218;
    stroke-width: 1.4;
    rx: 2;
  }
  .firewall {
    fill: #d0342c;
    stroke: #7c1a14;
    stroke-width: 1.2;
  }
  .stone { fill: #6a6458; stroke: #3a362e; stroke-width: 2; }
  .bush { fill: #2e7d32; stroke: #1b4d1e; stroke-width: 2; }
  .rose { fill: #2e7d32; stroke: #b0245a; stroke-width: 3; }
  .ooze { fill: #58a12b; opacity: 0.85; }
  .slime { fill: #8ec52e; opacity: 0.85; }
  .dust { fill: #9b9184; opacity: 0.75; }
  .pit { fill: #171512; }
  .tacks { font-size: 15px; text-anchor: middle; fill: #444; }
  .safe { fill: #7d8894; stroke: #2f3844; stroke-width: 2; }
  .trap-token { fill: #513c22; stroke: #201709; stroke-width: 1.5; }
  .trap-real { stroke: #d3352b; stroke-width: 2.5; }
  .dimwarp { fill: none; stroke: #5b3f9e; stroke-width: 3.5; stroke-dasharray: 4 3; }
  .ground-object { fill: #a6812e; stroke: #4a3a10; stroke-width: 1; }
  .illusion { stroke: #7a6f9a; stroke-width: 4; stroke-dasharray: 6 5; opacity: 0.7; }
  .creature { cursor: pointer; }
  .creature-body { fill: #3b3428; stroke-width: 2.5; }
  .creature-body.selected { fill: #6b5a34; }
  .creature-label { font-size: 11px; font-weight: bold; fill: #f4ead2; text-anchor: middle; pointer-events: none; }
  .home-star { stroke: #2b2218; stroke-width: 1.4; opacity: 0.92; }
  .treasure { stroke: #2b2218; stroke-width: 1.3; }
  .treasure-swirl { fill: none; stroke: #2b2218; stroke-width: 1.2; stroke-dasharray: 5 2.2; }
  .treasure-core { fill: #2b2218; }
  .warp { font-size: 14px; text-anchor: middle; fill: #2b2218; font-weight: bold; opacity: 0.65; pointer-events: none; }
  .warp-g { cursor: pointer; }
  .warp-hit { fill: transparent; }
  .warp-g:hover .warp-hit { fill: rgba(43, 34, 24, 0.12); }
  .warp-g.standing .warp-hit {
    fill: rgba(46, 125, 50, 0.25);
    stroke: #2e7d32;
    stroke-width: 2;
  }
  .warp-g.standing .warp { opacity: 1; }
  .wizard { cursor: pointer; }
  .wizard-label {
    font-family: "Oswald", sans-serif;
    font-size: 13px; font-weight: 600; fill: #f4ead2;
    text-anchor: middle; pointer-events: none;
  }
  .carried { fill: gold; stroke: #111; stroke-width: 1; }
  .edge-hit { fill: rgba(30, 120, 240, 0.15); cursor: crosshair; }
  .edge-hit:hover { fill: rgba(30, 120, 240, 0.5); }
</style>
