// Turnix — Multiplayer via Supabase Realtime
// Fill in your project URL and anon key from supabase.com → Settings → API

const SUPABASE_URL      = "https://YOUR_PROJECT_REF.supabase.co";
const SUPABASE_ANON_KEY = "YOUR_ANON_KEY_HERE";

const _sb = window.supabase.createClient(SUPABASE_URL, SUPABASE_ANON_KEY);

// ── Persistent player identity ────────────────────────────────────────────
function getMyId() {
  let id = localStorage.getItem("tx_pid");
  if (!id) { id = crypto.randomUUID(); localStorage.setItem("tx_pid", id); }
  return id;
}
const MY_ID = getMyId();

const PLAYER_COLORS = [
  "#f5f6f8", "#b48bff", "#4ea1ff", "#ff8b4a",
  "#3ddc8a", "#ffb547", "#ff5470", "#7be0ff",
];

// ── Shared game data ──────────────────────────────────────────────────────
const WRONG_QUESTIONS_DATA = [
  { q: "Name a fruit.",                  canon: ["apple","banana","orange","grape","mango","strawberry","pear"] },
  { q: "Name a country in Europe.",      canon: ["france","spain","italy","germany","portugal","england","poland"] },
  { q: "Name something in a kitchen.",   canon: ["fork","knife","spoon","fridge","stove","oven","plate","cup"] },
  { q: "Name an animal with four legs.", canon: ["dog","cat","horse","cow","lion","tiger","elephant","sheep"] },
  { q: "Name a colour.",                 canon: ["red","blue","green","yellow","black","white","orange","purple"] },
  { q: "Name a sport.",                  canon: ["football","soccer","basketball","tennis","swimming","golf"] },
];

// ── Pure game logic (runs on host, produces new roomState) ────────────────

function initGameState(roomState) {
  const { game, players, round = 1 } = roomState;
  let gameState;

  if (game === "impostor") {
    const impostorIdx = (round * 3) % players.length;
    gameState = {
      phase: "clue",
      impostorId: players[impostorIdx].id,
      word: "BANANA",
      clues: {},
      votes: {},
      lockedVotes: [],
    };
  } else if (game === "letter") {
    gameState = {
      phase: "play",
      letters: ["S", "T", "R"],
      turnPlayerIdx: 0,
      challengedId: null,
      challengeWord: null,
      challengeResult: null,
    };
  } else if (game === "wrong") {
    gameState = {
      phase: "answer",
      questionIdx: (round - 1) % WRONG_QUESTIONS_DATA.length,
      answers: {},
    };
  }

  return { ...roomState, phase: "game", gameState };
}

function scoreRound(roomState) {
  const { game, players, gameState: gs } = roomState;
  if (!gs) return roomState;
  const deltas = {};

  if (game === "impostor") {
    const counts = {};
    Object.values(gs.votes || {}).forEach(id => { counts[id] = (counts[id] || 0) + 1; });
    const topId = Object.entries(counts).sort((a, b) => b[1] - a[1])[0]?.[0];
    const caught = topId === gs.impostorId;
    players.forEach(p => {
      if (p.id === gs.impostorId) {
        deltas[p.id] = caught ? 0 : 3;
      } else {
        deltas[p.id] = caught ? (gs.votes?.[p.id] === gs.impostorId ? 2 : 1) : 0;
      }
    });
  } else if (game === "wrong") {
    const q = WRONG_QUESTIONS_DATA[gs.questionIdx % WRONG_QUESTIONS_DATA.length];
    const answers = gs.answers || {};
    const vals = Object.values(answers).map(a => a.trim().toLowerCase());
    const counts = {};
    vals.forEach(a => { counts[a] = (counts[a] || 0) + 1; });
    players.forEach(p => {
      const a = (answers[p.id] || "").trim().toLowerCase();
      const dup = counts[a] > 1;
      const canon = q.canon.includes(a);
      deltas[p.id] = a && !dup && !canon ? 1 : 0;
    });
  } else if (game === "letter") {
    const challengedId = gs.challengedId;
    const valid = gs.challengeResult;
    players.forEach(p => {
      deltas[p.id] = valid
        ? (p.id !== challengedId ? 1 : 0)
        : (p.id === challengedId ? 1 : 0);
    });
  }

  return {
    ...roomState,
    players: players.map(p => ({
      ...p,
      score: (p.score || 0) + (deltas[p.id] || 0),
      delta: deltas[p.id] || 0,
    })),
  };
}

function processAction(roomState, action) {
  const gs = roomState.gameState || {};
  const { players, round = 1, rounds = 5 } = roomState;

  switch (action.type) {

    case "toggleReady":
      return {
        ...roomState,
        players: players.map(p =>
          p.id === action.playerId ? { ...p, ready: !p.ready } : p
        ),
      };

    case "startGame":
      return initGameState(roomState);

    // ── Word Impostor ──────────────────────────────────────────────────────
    case "submitClue": {
      const clues = { ...gs.clues, [action.playerId]: action.clue };
      const allIn = players.every(p => clues[p.id]);
      return { ...roomState, gameState: { ...gs, clues, phase: allIn ? "vote" : "clue" } };
    }

    case "submitVote": {
      const votes = { ...gs.votes, [action.playerId]: action.targetId };
      return { ...roomState, gameState: { ...gs, votes } };
    }

    case "lockVote": {
      const lockedVotes = [...(gs.lockedVotes || [])];
      if (!lockedVotes.includes(action.playerId)) lockedVotes.push(action.playerId);
      const allLocked = players.every(p => lockedVotes.includes(p.id));
      return { ...roomState, gameState: { ...gs, lockedVotes, phase: allLocked ? "reveal" : "vote" } };
    }

    // ── Letter Game ────────────────────────────────────────────────────────
    case "addLetter": {
      const letters = [...gs.letters, action.letter.toUpperCase()];
      const turnPlayerIdx = (gs.turnPlayerIdx + 1) % players.length;
      return { ...roomState, gameState: { ...gs, letters, turnPlayerIdx } };
    }

    case "challenge": {
      const challengedIdx = (gs.turnPlayerIdx - 1 + players.length) % players.length;
      const word = gs.letters.join("");
      // A "valid" word means the challenged player had a real continuation
      const challengeResult = word.length >= 4;
      return {
        ...roomState,
        gameState: {
          ...gs,
          phase: "challenge",
          challengedId: players[challengedIdx].id,
          challengeWord: word,
          challengeResult,
        },
      };
    }

    // ── Wrong Answer Wins ──────────────────────────────────────────────────
    case "submitAnswer": {
      const answers = { ...gs.answers, [action.playerId]: action.answer };
      const allIn = players.every(p => answers[p.id]);
      return { ...roomState, gameState: { ...gs, answers, phase: allIn ? "reveal" : "answer" } };
    }

    // ── Game flow ──────────────────────────────────────────────────────────
    case "advance": {
      const scored = scoreRound(roomState);
      const isLast = round >= rounds;
      return { ...scored, phase: isLast ? "final" : "results", gameState: null };
    }

    case "nextRound":
      return initGameState({
        ...roomState,
        round: round + 1,
        players: players.map(p => ({ ...p, ready: false })),
      });

    case "playAgain":
      return initGameState({
        ...roomState,
        round: 1,
        players: players.map(p => ({ ...p, score: 0, delta: 0, ready: false })),
      });

    default:
      return roomState;
  }
}

// ── Supabase helpers ──────────────────────────────────────────────────────

async function txCreateRoom({ code, game, maxPlayers, rounds, playerName }) {
  const me = {
    id: MY_ID, name: playerName || "Host", color: PLAYER_COLORS[0],
    ready: false, host: true, score: 0, delta: 0,
  };
  const state = {
    game, maxPlayers, rounds,
    phase: "lobby", round: 1,
    hostId: MY_ID, players: [me], gameState: null,
  };
  const { error } = await _sb.from("rooms").insert({ code, state });
  if (error) throw new Error(error.message);
  return state;
}

async function txJoinRoom({ code, playerName }) {
  const { data, error } = await _sb.from("rooms").select("state").eq("code", code).single();
  if (error || !data) throw new Error("Room not found");
  const s = data.state;
  // Re-joining
  if (s.players.find(p => p.id === MY_ID)) return s;
  if (s.players.length >= s.maxPlayers) throw new Error("Room is full");
  const color = PLAYER_COLORS[s.players.length % PLAYER_COLORS.length];
  const me = { id: MY_ID, name: playerName || "Player", color, ready: false, host: false, score: 0, delta: 0 };
  const newState = { ...s, players: [...s.players, me] };
  await _sb.from("rooms").update({ state: newState }).eq("code", code);
  return newState;
}

async function txUpdateRoomState(code, state) {
  const { error } = await _sb.from("rooms").update({ state }).eq("code", code);
  if (error) console.error("[Turnix] update:", error.message);
}

function txSubscribeToRoom(code, onUpdate) {
  const ch = _sb.channel(`room-${code}`)
    .on("postgres_changes", {
      event: "UPDATE", schema: "public", table: "rooms", filter: `code=eq.${code}`,
    }, payload => onUpdate(payload.new.state))
    .subscribe();
  return () => _sb.removeChannel(ch);
}

function txBroadcastAction(code, action) {
  _sb.channel(`act-${code}`).send({
    type: "broadcast", event: "action",
    payload: { ...action, playerId: MY_ID },
  });
}

function txSubscribeToActions(code, onAction) {
  const ch = _sb.channel(`act-${code}`)
    .on("broadcast", { event: "action" }, ({ payload }) => onAction(payload))
    .subscribe();
  return () => _sb.removeChannel(ch);
}

async function txLeaveRoom(code) {
  const { data } = await _sb.from("rooms").select("state").eq("code", code).single();
  if (!data) return;
  let s = { ...data.state, players: data.state.players.filter(p => p.id !== MY_ID) };
  if (s.players.length === 0) { await _sb.from("rooms").delete().eq("code", code); return; }
  if (data.state.hostId === MY_ID) {
    s.players[0] = { ...s.players[0], host: true };
    s.hostId = s.players[0].id;
  }
  await _sb.from("rooms").update({ state: s }).eq("code", code);
}

Object.assign(window, {
  MY_ID, PLAYER_COLORS, WRONG_QUESTIONS_DATA,
  processAction, initGameState, scoreRound,
  txCreateRoom, txJoinRoom, txUpdateRoomState,
  txSubscribeToRoom, txBroadcastAction, txSubscribeToActions, txLeaveRoom,
});
