/**
 * Browser-side networking: thin REST wrappers, per-game session storage, and
 * a NetworkTransport wrapping the authenticated WebSocket. UI code (index.html)
 * never touches fetch()/WebSocket directly — it goes through this module.
 */

// Re-exported purely so the UI can decide what to show (enable the Roll
// button, render choice options, ...). Authoritative state always comes from
// the server's `state` broadcast — the UI never re-derives it locally.
export { getLegalIntents, isPlayersTurn } from '/shared/game.js';
export { board } from '/shared/board.js';

async function postJson(url, body) {
  const res = await fetch(url, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(body),
  });
  const data = await res.json().catch(() => ({}));
  if (!res.ok) throw new Error(data.error || `Request failed (${res.status})`);
  return data;
}

export function createGame(hostName) {
  return postJson('/api/games', { hostName });
}

export function joinGame(code, name) {
  return postJson(`/api/games/${encodeURIComponent(code)}/join`, { name });
}

export async function fetchGame(code) {
  const res = await fetch(`/api/games/${encodeURIComponent(code)}`);
  const data = await res.json().catch(() => ({}));
  if (!res.ok) throw new Error(data.error || `Request failed (${res.status})`);
  return data;
}

// --- Per-game session persistence (localStorage), so a reload/reconnect
// resumes as the same player instead of joining again. ---
const STORAGE_PREFIX = 'lifegame:session:';

export function saveSession(code, session) {
  localStorage.setItem(STORAGE_PREFIX + code, JSON.stringify(session));
}

export function loadSession(code) {
  const raw = localStorage.getItem(STORAGE_PREFIX + code);
  return raw ? JSON.parse(raw) : null;
}

// --- WebSocket transport ---
export class NetworkTransport {
  constructor() {
    this.ws = null;
    this._stateHandlers = [];
    this._errorHandlers = [];
  }

  /** Opens the socket, authenticates with the session token, and resolves
   * once the first authoritative state has been received. */
  connect(sessionToken) {
    return new Promise((resolve, reject) => {
      const proto = location.protocol === 'https:' ? 'wss' : 'ws';
      const ws = new WebSocket(`${proto}://${location.host}/ws`);
      this.ws = ws;
      let settled = false;

      ws.onopen = () => ws.send(JSON.stringify({ type: 'AUTH', token: sessionToken }));

      ws.onmessage = (event) => {
        const msg = JSON.parse(event.data);
        if (msg.type === 'state') {
          if (!settled) {
            settled = true;
            resolve();
          }
          this._stateHandlers.forEach((cb) => cb(msg));
        } else if (msg.type === 'error') {
          this._errorHandlers.forEach((cb) => cb(msg));
        }
      };

      ws.onerror = () => {
        if (!settled) {
          settled = true;
          reject(new Error('WebSocket connection failed'));
        }
      };

      ws.onclose = (event) => {
        if (!settled) {
          settled = true;
          reject(new Error(`Connection closed (${event.code})`));
        }
      };
    });
  }

  sendIntent(intent) {
    if (this.ws?.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify(intent));
    }
  }

  onState(cb) {
    this._stateHandlers.push(cb);
  }

  onError(cb) {
    this._errorHandlers.push(cb);
  }

  close() {
    this.ws?.close();
  }
}
