(function () {
  const storageKey = "signal-grid-best";
  const roles = [
    "teamWave", "teamWave", "teamWave", "teamWave", "teamWave",
    "teamWave", "teamWave", "teamWave", "teamWave",
    "teamEmber", "teamEmber", "teamEmber", "teamEmber",
    "teamEmber", "teamEmber", "teamEmber", "teamEmber",
    "neutral", "neutral", "neutral", "neutral", "neutral", "neutral", "neutral",
    "hazard"
  ];
  const words = [
    "Anchor", "Arcade", "Beacon", "Bridge", "Campus", "Canyon", "Cipher",
    "Comet", "Copper", "Courier", "Delta", "Drift", "Echo", "Engine",
    "Falcon", "Festival", "Harbor", "Helix", "Horizon", "Jacket", "Jungle",
    "Lantern", "Library", "Magnet", "Market", "Meteor", "Mirror", "Museum",
    "Nebula", "Noodle", "Orbit", "Paper", "Parade", "Pepper", "Pixel",
    "Planet", "Pocket", "Quartz", "Radar", "Rocket", "Signal", "Silver",
    "Sketch", "Station", "Summit", "Temple", "Ticket", "Tunnel", "Velvet",
    "Voyage", "Window"
  ];

  const state = {
    seed: "",
    keyVisible: false,
    finished: false,
    status: "Wave starts. Reveal the key only for the clue giver.",
    best: "",
    startedAt: 0,
    tiles: []
  };

  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 ensureStyle() {
    if (document.querySelector('link[data-signal-grid="style"]')) {
      return;
    }
    const link = document.createElement("link");
    link.rel = "stylesheet";
    link.href = "assets/style.css";
    link.dataset.signalGrid = "style";
    document.head.appendChild(link);
  }

  function toast(message) {
    const bridge = api();
    if (bridge.ui && bridge.ui.showToast) {
      return bridge.ui.showToast({ message: message }).catch(function () {});
    }
    return Promise.resolve();
  }

  function loadBest() {
    const store = api().storage && api().storage.local;
    if (!store || !store.get) {
      return Promise.resolve();
    }
    return store.get(storageKey).then(function (value) {
      state.best = String(value || "");
    }).catch(function () {});
  }

  function saveBest(message) {
    const store = api().storage && api().storage.local;
    state.best = message;
    if (!store || !store.set) {
      return Promise.resolve();
    }
    return store.set(storageKey, message).catch(function () {});
  }

  function seedValue() {
    return Math.floor(100000 + Math.random() * 900000).toString();
  }

  function randomFor(seed) {
    let value = 2166136261;
    for (let i = 0; i < seed.length; i += 1) {
      value ^= seed.charCodeAt(i);
      value = Math.imul(value, 16777619);
    }
    return function () {
      value += 0x6D2B79F5;
      let next = value;
      next = Math.imul(next ^ (next >>> 15), next | 1);
      next ^= next + Math.imul(next ^ (next >>> 7), next | 61);
      return ((next ^ (next >>> 14)) >>> 0) / 4294967296;
    };
  }

  function shuffled(source, rand) {
    const list = source.slice();
    for (let index = list.length - 1; index > 0; index -= 1) {
      const swap = Math.floor(rand() * (index + 1));
      const temp = list[index];
      list[index] = list[swap];
      list[swap] = temp;
    }
    return list;
  }

  function newRound(seed) {
    const nextSeed = String(seed || seedValue());
    const rand = randomFor(nextSeed);
    const selectedWords = shuffled(words, rand).slice(0, 25);
    const selectedRoles = shuffled(roles, rand);
    state.seed = nextSeed;
    state.keyVisible = false;
    state.finished = false;
    state.startedAt = Date.now();
    state.status = "Wave starts. Reveal the key only for the clue giver.";
    state.tiles = selectedWords.map(function (word, index) {
      return {
        word: word,
        role: selectedRoles[index],
        revealed: false
      };
    });
  }

  function remaining(role) {
    return state.tiles.filter(function (tile) {
      return tile.role === role && !tile.revealed;
    }).length;
  }

  function revealedCount() {
    return state.tiles.filter(function (tile) {
      return tile.revealed;
    }).length;
  }

  function roleLabel(role) {
    if (role === "teamWave") {
      return "Wave";
    }
    if (role === "teamEmber") {
      return "Ember";
    }
    if (role === "hazard") {
      return "Hazard";
    }
    return "Neutral";
  }

  function finish(message) {
    const seconds = Math.max(1, Math.round((Date.now() - state.startedAt) / 1000));
    const summary = message + " in " + seconds + "s";
    state.finished = true;
    state.status = summary;
    saveBest(summary);
    toast(summary);
  }

  function reveal(index) {
    if (state.finished || state.keyVisible) {
      return;
    }
    const tile = state.tiles[index];
    if (!tile || tile.revealed) {
      return;
    }
    tile.revealed = true;
    if (tile.role === "hazard") {
      finish("Hazard found");
    } else if (remaining("teamWave") === 0) {
      finish("Wave cleared the grid");
    } else if (remaining("teamEmber") === 0) {
      finish("Ember cleared the grid");
    } else {
      state.status = roleLabel(tile.role) + " tile revealed: " + tile.word + ".";
      toast(state.status);
    }
    render();
  }

  function scoreBox(label, value, note) {
    const box = el("div", "score");
    box.appendChild(el("strong", "", value));
    box.appendChild(el("span", "", label));
    if (note) {
      box.appendChild(el("span", "", note));
    }
    return box;
  }

  function render() {
    ensureStyle();
    document.body.innerHTML = "";

    const app = el("main", "signalGrid");
    const top = el("div", "topBar");
    const titleWrap = el("div");
    titleWrap.appendChild(el("h1", "", "Signal Grid"));
    titleWrap.appendChild(el("p", "seed", "Seed " + state.seed));
    top.appendChild(titleWrap);

    const keyButton = el(
      "button",
      "actionButton primary",
      state.keyVisible ? "Hide Key" : "Show Key"
    );
    keyButton.addEventListener("click", function () {
      state.keyVisible = !state.keyVisible;
      state.status = state.keyVisible ? "Key is visible." : "Key hidden.";
      render();
    });
    top.appendChild(keyButton);

    const scores = el("section", "scoreboard");
    scores.appendChild(scoreBox("Wave left", String(remaining("teamWave")), "starts"));
    scores.appendChild(scoreBox("Ember left", String(remaining("teamEmber")), ""));
    scores.appendChild(scoreBox("Revealed", String(revealedCount()) + "/25", state.best || "no saved round"));

    const board = el("section", "board");
    state.tiles.forEach(function (tile, index) {
      const classes = ["tile", tile.role];
      if (tile.revealed) {
        classes.push("revealed");
      }
      if (state.keyVisible) {
        classes.push("isKey");
      }
      const button = el("button", classes.join(" "), tile.word);
      button.disabled = tile.revealed || state.keyVisible || state.finished;
      button.addEventListener("click", function () {
        reveal(index);
      });
      board.appendChild(button);
    });

    const actions = el("section", "actions");
    const next = el("button", "actionButton secondary", "New Grid");
    next.addEventListener("click", function () {
      newRound();
      render();
      toast("New Signal Grid ready");
    });
    const same = el("button", "actionButton secondary", "Replay Seed");
    same.addEventListener("click", function () {
      newRound(state.seed);
      render();
      toast("Seed replayed");
    });
    const status = el("p", "status wide", state.status);
    actions.appendChild(next);
    actions.appendChild(same);
    actions.appendChild(status);

    app.appendChild(top);
    app.appendChild(scores);
    app.appendChild(board);
    app.appendChild(actions);
    document.body.appendChild(app);
  }

  loadBest().then(function () {
    newRound();
    render();
  });
})();
