(function () {
  const protocol = "localchat.headsup.v1";
  const eventSessionCreated = "session.created";
  const eventPlayerJoined = "player.joined";
  const eventRoundStarted = "round.started";
  const eventWordMarked = "word.marked";
  const eventRoundFinished = "round.finished";
  const eventScoreReset = "score.reset";
  const storageKey = "heads-up-charades-last";
  const decks = [
    {
      id: "icebreakers",
      label: "Icebreakers",
      words: ["Backpack", "Whiteboard", "Library", "Cafeteria", "Notebook", "Pencil", "Alarm Clock", "Popcorn"]
    },
    {
      id: "actions",
      label: "Actions",
      words: ["Brushing Teeth", "Riding a Bike", "Opening a Locker", "Making Popcorn", "Taking a Selfie", "Swimming", "Skateboarding", "Stretching"]
    },
    {
      id: "food",
      label: "Food",
      words: ["Pizza", "Taco", "Ice Cream", "Apple", "Sandwich", "Noodles", "Pancakes", "Popcorn"]
    },
    {
      id: "animals",
      label: "Animals",
      words: ["Penguin", "Giraffe", "Cat", "Dog", "Dolphin", "Turtle", "Rabbit", "Elephant"]
    },
    {
      id: "sports",
      label: "Sports",
      words: ["Basketball", "Soccer", "Volleyball", "Swimming", "Tennis", "Running", "Skateboarding", "Yoga"]
    }
  ];
  const state = {
    mode: "",
    role: "",
    roomId: "",
    memberId: "",
    memberName: "",
    deckId: "icebreakers",
    duration: 60,
    cursor: "0",
    wordIndex: 0,
    round: null,
    scores: {},
    players: {},
    history: [],
    hiddenWords: {},
    status: "Choose a mode to start.",
    now: Date.now(),
    tickTimer: 0,
    pollTimer: 0
  };

  function api() {
    return window.localchat || {};
  }

  function el(tag, className, text) {
    const node = document.createElement(tag);
    if (className) {
      node.className = className;
    }
    if (text !== undefined && text !== null) {
      node.textContent = text;
    }
    return node;
  }

  function bridge(method, params) {
    const parts = method.split(".");
    let target = api();
    for (let i = 0; i < parts.length; i += 1) {
      target = target && target[parts[i]];
    }
    if (typeof target !== "function") {
      return Promise.resolve({});
    }
    return target(params || {}).catch(function (error) {
      state.status = (error && error.errorMessage) || "Action failed";
      render();
      return {};
    });
  }

  function toast(message) {
    return bridge("ui.showToast", { message: message });
  }

  function storageGet(key) {
    const store = api().storage && api().storage.local;
    if (!store || !store.get) {
      return Promise.resolve("");
    }
    return store.get(key).catch(function () {
      return "";
    });
  }

  function storageSet(key, value) {
    const store = api().storage && api().storage.local;
    if (!store || !store.set) {
      return Promise.resolve();
    }
    return store.set(key, value).catch(function () {});
  }

  function keepScreenOn(enabled) {
    return bridge("ui.setKeepScreenOn", Boolean(enabled));
  }

  function safeId(value, fallback) {
    const raw = String(value || "").trim().replace(/[^a-zA-Z0-9._:-]/g, "-");
    return raw ? raw.slice(0, 80) : fallback;
  }

  function roomCode() {
    return "CHAR-" + Math.floor(1000 + Math.random() * 9000);
  }

  function memberId() {
    return "member-" + Date.now().toString(36) + "-" + Math.random().toString(36).slice(2, 7);
  }

  function deck() {
    return decks.find(function (item) { return item.id === state.deckId; }) || decks[0];
  }

  function nextWord() {
    const words = deck().words.filter(function (word) { return !state.hiddenWords[word]; });
    const source = words.length ? words : deck().words;
    const word = source[state.wordIndex % source.length];
    state.wordIndex = (state.wordIndex + 1) % source.length;
    return word;
  }

  function addPlayer(id, name) {
    const playerId = safeId(id, "");
    if (!playerId) {
      return;
    }
    const playerName = String(name || "Player").trim().slice(0, 32) || "Player";
    state.players[playerId] = { id: playerId, name: playerName };
    if (state.scores[playerName] === undefined) {
      state.scores[playerName] = 0;
    }
  }

  function playerNames() {
    const names = {};
    Object.keys(state.players).forEach(function (id) {
      names[state.players[id].name] = true;
    });
    if (state.memberName) {
      names[state.memberName] = true;
    }
    return Object.keys(names).sort();
  }

  function sendEvent(event, extra) {
    if (!state.roomId) {
      return Promise.resolve({});
    }
    const payload = Object.assign({
      protocol: protocol,
      event: event,
      sessionId: state.roomId,
      memberId: state.memberId,
      memberName: state.memberName,
      createdAt: new Date().toISOString()
    }, extra || {});
    return bridge("room.message", {
      roomId: state.roomId,
      memberId: state.memberId,
      type: "headsup.event",
      payload: payload
    });
  }

  function isPayload(payload) {
    return payload && payload.protocol === protocol && typeof payload.event === "string";
  }

  function applyPayload(payload) {
    if (!isPayload(payload)) {
      return;
    }
    if (payload.event === eventSessionCreated) {
      state.status = "Room " + (payload.sessionId || state.roomId) + " is ready.";
      return;
    }
    if (payload.event === eventPlayerJoined) {
      addPlayer(payload.memberId, payload.memberName);
      state.status = (payload.memberName || "A player") + " joined.";
      return;
    }
    if (payload.event === eventRoundStarted) {
      state.deckId = payload.deckId || state.deckId;
      state.duration = Number(payload.duration || state.duration);
      state.round = {
        id: payload.roundId,
        playerName: payload.playerName || "Player",
        word: payload.word || "Pizza",
        correct: [],
        passed: [],
        startedAt: payload.startedAt || Date.now(),
        endsAt: payload.endsAt || (Date.now() + state.duration * 1000),
        active: true
      };
      state.status = state.round.playerName + " is guessing.";
      keepScreenOn(true);
      return;
    }
    if (payload.event === eventWordMarked && state.round) {
      markWord(payload.result === "correct", false, payload.word);
      return;
    }
    if (payload.event === eventRoundFinished) {
      finishRound(false);
      return;
    }
    if (payload.event === eventScoreReset) {
      state.scores = {};
      state.history = [];
      state.status = "Scoreboard reset.";
    }
  }

  function pollRoom() {
    if (!state.roomId || !api().room || !api().room.poll) {
      return;
    }
    bridge("room.poll", {
      roomId: state.roomId,
      cursor: state.cursor,
      limit: 40
    }).then(function (result) {
      const messages = Array.isArray(result.messages) ? result.messages : [];
      messages.forEach(function (message) {
        if (message && message.memberId !== state.memberId) {
          applyPayload(message.payload);
        }
      });
      if (result.nextCursor !== undefined && result.nextCursor !== null) {
        state.cursor = String(result.nextCursor);
      }
      render();
    });
  }

  function startPolling() {
    stopPolling();
    state.pollTimer = window.setInterval(pollRoom, 1200);
    pollRoom();
  }

  function stopPolling() {
    if (state.pollTimer) {
      window.clearInterval(state.pollTimer);
      state.pollTimer = 0;
    }
  }

  function startTicker() {
    stopTicker();
    state.tickTimer = window.setInterval(function () {
      state.now = Date.now();
      if (state.round && state.round.active && timeLeft() <= 0) {
        finishRound(true);
      } else {
        render();
      }
    }, 300);
  }

  function stopTicker() {
    if (state.tickTimer) {
      window.clearInterval(state.tickTimer);
      state.tickTimer = 0;
    }
  }

  function timeLeft() {
    if (!state.round || !state.round.active) {
      return 0;
    }
    return Math.max(0, Math.ceil((state.round.endsAt - state.now) / 1000));
  }

  function beginRound() {
    const names = playerNames();
    const playerName = names[0] || state.memberName || "Player";
    const word = nextWord();
    const startedAt = Date.now();
    state.round = {
      id: "round-" + startedAt.toString(36),
      playerName: playerName,
      word: word,
      correct: [],
      passed: [],
      startedAt: startedAt,
      endsAt: startedAt + state.duration * 1000,
      active: true
    };
    state.status = playerName + " is guessing.";
    keepScreenOn(true);
    sendEvent(eventRoundStarted, {
      roundId: state.round.id,
      playerName: playerName,
      word: word,
      deckId: state.deckId,
      duration: state.duration,
      startedAt: startedAt,
      endsAt: state.round.endsAt
    });
    render();
  }

  function markWord(correct, broadcast, overrideWord) {
    if (!state.round || !state.round.active) {
      return;
    }
    const word = overrideWord || state.round.word;
    if (correct) {
      state.round.correct.push(word);
      state.scores[state.round.playerName] = (state.scores[state.round.playerName] || 0) + 1;
    } else {
      state.round.passed.push(word);
    }
    state.round.word = nextWord();
    state.status = correct ? "Correct. Next card." : "Passed. Next card.";
    if (broadcast !== false) {
      sendEvent(eventWordMarked, {
        roundId: state.round.id,
        playerName: state.round.playerName,
        word: word,
        result: correct ? "correct" : "pass"
      });
    }
    render();
  }

  function hideCurrentWord() {
    if (!state.round) {
      return;
    }
    state.hiddenWords[state.round.word] = true;
    storageSet(storageKey, JSON.stringify(snapshotSettings()));
    toast("Card hidden on this device");
    markWord(false, true);
  }

  function finishRound(broadcast) {
    if (!state.round) {
      return;
    }
    state.round.active = false;
    state.history.unshift({
      playerName: state.round.playerName,
      score: state.round.correct.length,
      passed: state.round.passed.length,
      at: new Date().toLocaleTimeString()
    });
    state.history = state.history.slice(0, 6);
    state.status = "Round finished.";
    keepScreenOn(false);
    if (broadcast !== false) {
      sendEvent(eventRoundFinished, {
        roundId: state.round.id,
        playerName: state.round.playerName,
        score: state.round.correct.length,
        passed: state.round.passed.length
      });
    }
    render();
  }

  function resetScores() {
    state.scores = {};
    state.history = [];
    state.round = null;
    state.status = "Scoreboard reset.";
    sendEvent(eventScoreReset, {});
    render();
  }

  function snapshotSettings() {
    return {
      roomId: state.roomId,
      name: state.memberName,
      deckId: state.deckId,
      duration: state.duration,
      hiddenWords: state.hiddenWords
    };
  }

  function startSolo() {
    state.mode = "play";
    state.role = "solo";
    state.memberId = memberId();
    state.memberName = "Team 1";
    state.roomId = "";
    state.scores = { "Team 1": 0 };
    addPlayer(state.memberId, state.memberName);
    state.status = "Quick play ready.";
    stopPolling();
    startTicker();
    render();
  }

  function startHost(name) {
    state.mode = "play";
    state.role = "host";
    state.memberId = memberId();
    state.memberName = String(name || "Host").trim().slice(0, 32) || "Host";
    state.roomId = roomCode();
    state.status = "Creating room...";
    addPlayer(state.memberId, state.memberName);
    bridge("room.create", {
      roomId: state.roomId,
      memberId: state.memberId
    }).then(function (result) {
      if (result && result.roomId) {
        state.roomId = result.roomId;
      }
      state.status = "Share room " + state.roomId + " with nearby players.";
      storageSet(storageKey, JSON.stringify(snapshotSettings()));
      sendEvent(eventSessionCreated, {});
      startPolling();
      startTicker();
      render();
    });
    render();
  }

  function joinRoom(roomId, name) {
    const cleanRoom = safeId(roomId, "");
    if (!cleanRoom) {
      toast("Enter a room code first");
      return;
    }
    state.mode = "play";
    state.role = "player";
    state.memberId = memberId();
    state.memberName = String(name || "Player").trim().slice(0, 32) || "Player";
    state.roomId = cleanRoom;
    state.status = "Joining room...";
    addPlayer(state.memberId, state.memberName);
    bridge("room.join", {
      roomId: state.roomId,
      memberId: state.memberId
    }).then(function () {
      state.status = "Joined " + state.roomId + ". Watch the word card.";
      storageSet(storageKey, JSON.stringify(snapshotSettings()));
      sendEvent(eventPlayerJoined, {});
      startPolling();
      startTicker();
      render();
    });
    render();
  }

  function renderShell() {
    document.body.innerHTML = "";
    if (!document.querySelector("link[data-heads-up-style]")) {
      const link = document.createElement("link");
      link.rel = "stylesheet";
      link.href = "assets/style.css";
      link.setAttribute("data-heads-up-style", "true");
      document.head.appendChild(link);
    }
    const app = el("main", "headsUpApp");
    const topbar = el("section", "topbar");
    const brand = el("div", "brand");
    const mark = el("img");
    mark.alt = "";
    mark.src = "assets/heads-up-charades-icon.png";
    const titleWrap = el("div");
    titleWrap.appendChild(el("h1", "", "Heads Up Charades"));
    titleWrap.appendChild(el("div", "helper", "Act it out. Guess fast. Keep it clean."));
    brand.appendChild(mark);
    brand.appendChild(titleWrap);
    topbar.appendChild(brand);
    topbar.appendChild(el("div", "roomBadge", state.roomId || "Local"));
    app.appendChild(topbar);
    const hero = el("section", "hero");
    const heroImage = el("img", "heroImage");
    heroImage.alt = "";
    heroImage.src = "assets/heads-up-charades-header.webp";
    hero.appendChild(heroImage);
    app.appendChild(hero);
    document.body.appendChild(app);
    return app;
  }

  function renderHome(app) {
    const surface = el("section", "surface");
    const modes = el("div", "modeGrid");
    const quick = el("button", "modeButton");
    quick.innerHTML = "<strong>Quick Play</strong><span>One device, pass-and-play rounds.</span>";
    quick.addEventListener("click", startSolo);
    const host = el("button", "modeButton");
    host.innerHTML = "<strong>Host Game</strong><span>Create a room and keep the score in sync.</span>";
    const join = el("button", "modeButton");
    join.innerHTML = "<strong>Join Game</strong><span>Enter a nearby room code and follow along.</span>";
    modes.appendChild(quick);
    modes.appendChild(host);
    modes.appendChild(join);

    const setup = el("div", "joinGrid");
    const hostName = field("Host name", "host-name", state.memberName || "Host");
    const roomInput = field("Room code", "room-code", state.roomId || "");
    const playerName = field("Player name", "player-name", state.memberName || "Player");
    const hostButton = el("button", "primaryButton", "Create Game");
    hostButton.addEventListener("click", function () { startHost(hostName.input.value); });
    const joinButton = el("button", "secondaryButton", "Join Game");
    joinButton.addEventListener("click", function () { joinRoom(roomInput.input.value, playerName.input.value); });
    const hostBox = el("div");
    hostBox.appendChild(hostName.wrap);
    hostBox.appendChild(hostButton);
    const joinBox = el("div");
    joinBox.appendChild(roomInput.wrap);
    joinBox.appendChild(playerName.wrap);
    joinBox.appendChild(joinButton);
    setup.appendChild(hostBox);
    setup.appendChild(joinBox);
    host.addEventListener("click", function () { hostName.input.focus(); });
    join.addEventListener("click", function () { roomInput.input.focus(); });

    surface.appendChild(modes);
    surface.appendChild(setup);
    surface.appendChild(el("p", "status", state.status));
    app.appendChild(surface);
  }

  function field(label, id, value) {
    const wrap = el("label", "field");
    const text = el("span", "", label);
    const input = document.createElement("input");
    input.id = id;
    input.value = value || "";
    wrap.appendChild(text);
    wrap.appendChild(input);
    return { wrap: wrap, input: input };
  }

  function renderGame(app) {
    const surface = el("section", "surface playGrid");
    const card = el("section", "wordCard");
    const active = Boolean(state.round && state.round.active);
    card.appendChild(el("div", "eyebrow", active ? state.round.playerName + " guesses" : "Ready"));
    card.appendChild(el("div", "word", state.round ? state.round.word : "Pick a deck and start a round."));
    card.appendChild(el("div", "timer" + (active ? "" : " idle"), active ? String(timeLeft()) : String(state.duration)));
    card.appendChild(el("div", "helper", state.round && !state.round.active ? "Round over. Review the score below." : "No recording. No speech recognition. Just clean clues."));

    const controls = el("section", "sidePanel");
    if (state.role !== "player") {
      controls.appendChild(renderDeckControls());
      const start = el("button", "primaryButton", active ? "Restart Round" : "Start Round");
      start.addEventListener("click", beginRound);
      controls.appendChild(start);
      const row = el("div", "buttonRow");
      const correct = el("button", "judgeGood", "Correct");
      const pass = el("button", "judgeMiss", "Pass");
      correct.addEventListener("click", function () { markWord(true, true); });
      pass.addEventListener("click", function () { markWord(false, true); });
      row.appendChild(correct);
      row.appendChild(pass);
      controls.appendChild(row);
      const end = el("button", "secondaryButton", "End Round");
      end.addEventListener("click", function () { finishRound(true); });
      controls.appendChild(end);
      const hide = el("button", "secondaryButton", "Hide Card");
      hide.addEventListener("click", hideCurrentWord);
      controls.appendChild(hide);
      const reset = el("button", "secondaryButton", "Reset Scores");
      reset.addEventListener("click", resetScores);
      controls.appendChild(reset);
    } else {
      controls.appendChild(el("p", "status", "Player view is synced from the host. Keep the phone visible to the clue-givers."));
    }
    const home = el("button", "secondaryButton", "Back to Modes");
    home.addEventListener("click", function () {
      state.mode = "";
      state.role = "";
      state.round = null;
      stopPolling();
      stopTicker();
      keepScreenOn(false);
      render();
    });
    controls.appendChild(home);
    controls.appendChild(el("p", "status", state.status));
    surface.appendChild(card);
    surface.appendChild(controls);
    app.appendChild(surface);
    app.appendChild(renderScores());
    app.appendChild(renderHistory());
  }

  function renderDeckControls() {
    const wrap = el("div", "controlStack");
    const deckLabel = el("label", "field");
    deckLabel.appendChild(el("span", "", "Deck"));
    const deckSelect = document.createElement("select");
    decks.forEach(function (item) {
      const option = document.createElement("option");
      option.value = item.id;
      option.textContent = item.label;
      option.selected = item.id === state.deckId;
      deckSelect.appendChild(option);
    });
    deckSelect.addEventListener("change", function () {
      state.deckId = deckSelect.value;
      storageSet(storageKey, JSON.stringify(snapshotSettings()));
      render();
    });
    deckLabel.appendChild(deckSelect);
    const durationLabel = el("label", "field");
    durationLabel.appendChild(el("span", "", "Round length"));
    const durationSelect = document.createElement("select");
    [30, 60, 90].forEach(function (value) {
      const option = document.createElement("option");
      option.value = String(value);
      option.textContent = value + " seconds";
      option.selected = value === state.duration;
      durationSelect.appendChild(option);
    });
    durationSelect.addEventListener("change", function () {
      state.duration = Number(durationSelect.value || 60);
      storageSet(storageKey, JSON.stringify(snapshotSettings()));
      render();
    });
    durationLabel.appendChild(durationSelect);
    wrap.appendChild(deckLabel);
    wrap.appendChild(durationLabel);
    return wrap;
  }

  function renderScores() {
    const panel = el("section", "scoreboard");
    panel.appendChild(el("h2", "", "Scoreboard"));
    const names = Object.keys(state.scores).sort(function (a, b) {
      return state.scores[b] - state.scores[a] || a.localeCompare(b);
    });
    if (!names.length) {
      panel.appendChild(el("p", "helper", "No scores yet."));
      return panel;
    }
    names.forEach(function (name) {
      const row = el("div", "scoreRow");
      row.appendChild(el("div", "scoreName", name));
      row.appendChild(el("div", "scoreValue", String(state.scores[name] || 0)));
      panel.appendChild(row);
    });
    return panel;
  }

  function renderHistory() {
    const panel = el("section", "history");
    panel.appendChild(el("h2", "", "Recent Rounds"));
    if (!state.history.length) {
      panel.appendChild(el("p", "helper", "No rounds yet."));
      return panel;
    }
    state.history.forEach(function (item) {
      const row = el("div", "historyRow");
      row.appendChild(el("div", "historyPrompt", item.playerName + " scored " + item.score));
      row.appendChild(el("div", "historyMeta", item.passed + " passed - " + item.at));
      panel.appendChild(row);
    });
    return panel;
  }

  function render() {
    state.now = Date.now();
    const app = renderShell();
    if (state.mode === "play") {
      renderGame(app);
    } else {
      renderHome(app);
    }
  }

  function boot() {
    storageGet(storageKey).then(function (value) {
      if (value) {
        try {
          const saved = JSON.parse(value);
          state.roomId = saved.roomId || "";
          state.memberName = saved.name || "";
          state.deckId = saved.deckId || state.deckId;
          state.duration = Number(saved.duration || state.duration);
          state.hiddenWords = saved.hiddenWords || {};
        } catch (error) {
          state.roomId = "";
        }
      }
      render();
    });
  }

  window.addEventListener("beforeunload", function () {
    stopPolling();
    stopTicker();
    keepScreenOn(false);
  });

  boot();
})();
