(function () {
  const protocol = "localchat.activity.v1";
  const eventSessionCreated = "session.created";
  const eventPollOpened = "poll.opened";
  const eventVoteCast = "poll.vote";
  const eventPollClosed = "poll.closed";
  const eventParticipantJoined = "participant.joined";
  const eventLotteryDrawn = "lottery.draw";
  const lotteryAlgorithm = "fnv1a32-lcg-fisher-yates-v1";
  const toolDefinitions = [
    { id: "poll", label: "Live Vote" },
    { id: "lottery", label: "Lucky Draw" }
  ];
  const defaultOptions = [
    { id: "bingo", label: "Bingo" },
    { id: "lottery", label: "Lucky Draw" },
    { id: "guess", label: "Guessing Game" }
  ];
  const state = {
    role: "",
    roomId: "",
    memberId: "",
    memberName: "",
    cursor: 0,
    poll: null,
    participants: {},
    votes: {},
    lottery: null,
    activeTool: "poll",
    selectedOptionId: "",
    closedSummary: null,
    pollTimer: 0,
    status: ""
  };

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

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

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

  function currentOptions() {
    return state.poll && state.poll.options ? state.poll.options : defaultOptions;
  }

  function parseOptions(source) {
    const labels = String(source || "")
      .split("\n")
      .map(function (item) { return item.trim(); })
      .filter(Boolean)
      .slice(0, 6);
    return (labels.length ? labels : defaultOptions.map(function (option) { return option.label; }))
      .map(function (label, index) {
        return { id: "option-" + (index + 1), label: label.slice(0, 48) };
      });
  }

  function upsertParticipant(memberId, memberName) {
    const id = safeId(memberId, "");
    if (!id) {
      return;
    }
    state.participants[id] = {
      id: id,
      name: String(memberName || "Guest").trim().slice(0, 40) || "Guest"
    };
  }

  function participantList() {
    return Object.keys(state.participants)
      .sort()
      .map(function (id) {
        return state.participants[id];
      });
  }

  function parseManualCandidates(source) {
    const seen = {};
    return String(source || "")
      .split("\n")
      .map(function (item) { return item.trim(); })
      .filter(Boolean)
      .slice(0, 40)
      .map(function (name, index) {
        const base = safeId("manual-" + hash32(name + ":" + index).toString(36), "manual-" + index);
        return { id: base, name: name.slice(0, 40) };
      })
      .filter(function (candidate) {
        if (seen[candidate.id]) {
          return false;
        }
        seen[candidate.id] = true;
        return true;
      });
  }

  function hash32(value) {
    let hash = 2166136261;
    const text = String(value || "");
    for (let i = 0; i < text.length; i += 1) {
      hash ^= text.charCodeAt(i);
      hash = Math.imul(hash, 16777619);
    }
    return hash >>> 0;
  }

  function seededRandom(seed) {
    let stateValue = hash32(seed) || 1;
    return function () {
      stateValue = Math.imul(1664525, stateValue) + 1013904223;
      return (stateValue >>> 0) / 4294967296;
    };
  }

  function seededShuffle(items, seed) {
    const random = seededRandom(seed);
    const copy = items.slice();
    for (let index = copy.length - 1; index > 0; index -= 1) {
      const swap = Math.floor(random() * (index + 1));
      const temp = copy[index];
      copy[index] = copy[swap];
      copy[swap] = temp;
    }
    return copy;
  }

  function sendEvent(event, extra) {
    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: "activity.event",
      payload: payload
    });
  }

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

  function isEvent(payload, event, aliases) {
    if (payload.event === event) {
      return true;
    }
    return Array.isArray(aliases) && aliases.indexOf(payload.event) !== -1;
  }

  function applyPayload(payload) {
    if (!isActivityPayload(payload)) {
      return;
    }
    if (isEvent(payload, eventSessionCreated)) {
      state.sessionId = payload.sessionId || state.roomId;
      return;
    }
    if (isEvent(payload, eventParticipantJoined, ["participant.entered"])) {
      upsertParticipant(payload.memberId, payload.memberName);
      return;
    }
    if (isEvent(payload, eventPollOpened)) {
      state.poll = {
        pollId: payload.pollId,
        question: payload.question || "Live vote",
        options: Array.isArray(payload.options) ? payload.options : defaultOptions,
        closed: false
      };
      state.votes = {};
      state.closedSummary = null;
      state.selectedOptionId = "";
      return;
    }
    if (isEvent(payload, eventVoteCast, ["poll.vote.cast"]) && state.role === "host") {
      if (state.poll && payload.pollId === state.poll.pollId && payload.optionId) {
        state.votes[payload.memberId || payload.memberName || "guest"] = {
          optionId: payload.optionId,
          memberName: payload.memberName || "Guest"
        };
      }
      return;
    }
    if (isEvent(payload, eventPollClosed)) {
      state.closedSummary = payload.summary || null;
      if (state.poll) {
        state.poll.closed = true;
      }
      return;
    }
    if (isEvent(payload, eventLotteryDrawn, ["lottery.drawn"])) {
      state.lottery = {
        lotteryId: payload.lotteryId,
        title: payload.title || "Lucky Draw",
        seed: payload.seed || "",
        algorithm: payload.algorithm || lotteryAlgorithm,
        candidateCount: Number(payload.candidateCount || 0),
        winners: Array.isArray(payload.winners) ? payload.winners : []
      };
    }
  }

  function pollRoom() {
    if (!state.roomId) {
      return;
    }
    bridge("room.poll", {
      roomId: state.roomId,
      memberId: state.memberId,
      since: state.cursor,
      limit: 50
    }).then(function (result) {
      const messages = Array.isArray(result.messages) ? result.messages : [];
      messages.forEach(function (message) {
        applyPayload(message.payload);
      });
      state.cursor = Number(result.nextSince || result.nextCursor || state.cursor || 0);
      render();
    });
  }

  function startPolling() {
    if (state.pollTimer) {
      clearInterval(state.pollTimer);
    }
    state.pollTimer = setInterval(pollRoom, 1200);
    pollRoom();
  }

  function beginHost() {
    state.role = "host";
    state.roomId = newRoomId();
    state.memberName = "Host";
    bridge("ui.setKeepScreenOn", true);
    bridge("room.create", {
      roomId: state.roomId,
      memberId: state.memberId
    }).then(function () {
      state.status = "Room is live";
      startPolling();
      return sendEvent(eventSessionCreated, {
        tools: toolDefinitions.map(function (tool) { return tool.id; })
      });
    }).then(function () {
      render();
    });
  }

  function beginParticipant(roomId, name) {
    state.role = "participant";
    state.roomId = safeId(roomId, "ACT-0000").slice(0, 40);
    state.memberName = String(name || "Guest").trim().slice(0, 40) || "Guest";
    storageSet("activity-suite-name", state.memberName);
    bridge("room.join", {
      roomId: state.roomId,
      memberId: state.memberId
    }).then(function () {
      state.status = "Joined " + state.roomId;
      startPolling();
      return sendEvent(eventParticipantJoined, {});
    }).then(function () {
      render();
    });
  }

  function openPoll(form) {
    const questionInput = form.elements.namedItem("question");
    const optionsInput = form.elements.namedItem("options");
    const question = String(questionInput.value || "").trim().slice(0, 120) || "Choose tonight's first activity";
    const options = parseOptions(optionsInput.value);
    const pollId = "poll-" + Date.now();
    state.activeTool = "poll";
    state.poll = { pollId: pollId, question: question, options: options, closed: false };
    state.votes = {};
    state.closedSummary = null;
    state.selectedOptionId = "";
    sendEvent(eventPollOpened, {
      pollId: pollId,
      question: question,
      options: options
    }).then(function () {
      state.status = "Poll opened";
      render();
    });
  }

  function castVote(optionId) {
    if (!state.poll || state.poll.closed) {
      return;
    }
    state.selectedOptionId = optionId;
    sendEvent(eventVoteCast, {
      pollId: state.poll.pollId,
      optionId: optionId
    }).then(function () {
      toast("Vote sent");
      render();
    });
  }

  function summarizeVotes() {
    const options = currentOptions();
    const counts = {};
    options.forEach(function (option) {
      counts[option.id] = 0;
    });
    Object.keys(state.votes).forEach(function (memberId) {
      const vote = state.votes[memberId];
      if (counts[vote.optionId] !== undefined) {
        counts[vote.optionId] += 1;
      }
    });
    return {
      totalVotes: Object.keys(state.votes).length,
      counts: counts
    };
  }

  function closePoll() {
    if (!state.poll) {
      return;
    }
    const summary = summarizeVotes();
    state.poll.closed = true;
    state.closedSummary = summary;
    sendEvent(eventPollClosed, {
      pollId: state.poll.pollId,
      summary: summary
    }).then(function () {
      state.status = "Poll closed";
      render();
    });
  }

  function drawLottery(form) {
    const titleInput = form.elements.namedItem("lotteryTitle");
    const countInput = form.elements.namedItem("winnerCount");
    const seedInput = form.elements.namedItem("lotterySeed");
    const manualInput = form.elements.namedItem("manualCandidates");
    const title = String(titleInput.value || "").trim().slice(0, 80) || "Lucky Draw";
    const manualCandidates = parseManualCandidates(manualInput.value);
    const candidatesById = {};
    participantList().forEach(function (candidate) {
      candidatesById[candidate.id] = candidate;
    });
    manualCandidates.forEach(function (candidate) {
      candidatesById[candidate.id] = candidate;
    });
    const candidates = Object.keys(candidatesById).sort().map(function (id) {
      return candidatesById[id];
    });
    if (!candidates.length) {
      state.status = "No candidates";
      render();
      return;
    }
    const requestedWinnerCount = Number(countInput.value || 1);
    const safeWinnerCount = Number.isFinite(requestedWinnerCount) ? requestedWinnerCount : 1;
    const winnerCount = Math.max(1, Math.min(10, safeWinnerCount, candidates.length));
    const lotteryId = "lottery-" + Date.now();
    const seed = String(seedInput.value || "").trim().slice(0, 80) || lotteryId;
    const winners = seededShuffle(candidates, seed).slice(0, winnerCount);
    state.activeTool = "lottery";
    state.lottery = {
      lotteryId: lotteryId,
      title: title,
      seed: seed,
      algorithm: lotteryAlgorithm,
      candidateCount: candidates.length,
      winners: winners
    };
    sendEvent(eventLotteryDrawn, {
      lotteryId: lotteryId,
      title: title,
      seed: seed,
      algorithm: lotteryAlgorithm,
      candidateCount: candidates.length,
      winners: winners
    }).then(function () {
      state.status = "Winners drawn";
      render();
    });
  }

  function renderShell(content) {
    document.body.innerHTML = "";
    if (!document.querySelector("link[data-activity-suite]")) {
      const link = document.createElement("link");
      link.rel = "stylesheet";
      link.href = "assets/style.css";
      link.dataset.activitySuite = "true";
      document.head.appendChild(link);
    }
    const app = el("main", "activityApp");
    const topbar = el("header", "topbar");
    const brand = el("div", "brand");
    const mark = el("img");
    mark.src = "assets/activity-suite-icon.png";
    mark.alt = "";
    brand.appendChild(mark);
    brand.appendChild(el("h1", "", "Activity Suite"));
    topbar.appendChild(brand);
    if (state.roomId) {
      topbar.appendChild(el("div", "roomCode", state.roomId));
    }
    app.appendChild(topbar);
    const hero = el("section", "hero");
    const heroImage = el("img", "heroImage");
    heroImage.src = "assets/activity-suite-header.webp";
    heroImage.alt = "";
    hero.appendChild(heroImage);
    app.appendChild(hero);
    app.appendChild(content);
    document.body.appendChild(app);
  }

  function renderStart() {
    const box = el("section", "surface");
    const grid = el("div", "modeGrid");
    const host = el("button", "modeButton");
    host.innerHTML = "<strong>Host</strong><span>Run a live vote or draw winners from one room.</span>";
    host.addEventListener("click", beginHost);
    const join = el("form", "joinRow");
    join.innerHTML = [
      "<label class=\"field\"><span>Room code</span><input name=\"room\" maxlength=\"40\" autocomplete=\"off\"></label>",
      "<label class=\"field\"><span>Name</span><input name=\"name\" maxlength=\"40\" autocomplete=\"name\"></label>",
      "<button class=\"modeButton\" type=\"submit\"><strong>Join</strong><span>Enter the room and take part from your device.</span></button>"
    ].join("");
    join.addEventListener("submit", function (event) {
      event.preventDefault();
      beginParticipant(
        join.elements.namedItem("room").value,
        join.elements.namedItem("name").value
      );
    });
    grid.appendChild(host);
    grid.appendChild(join);
    box.appendChild(grid);
    renderShell(box);
  }

  function renderHost() {
    const box = el("section", "surface");
    const grid = el("div", "hostGrid");
    const tools = el("div", "toolStack");
    tools.appendChild(renderToolTabs());
    const setup = el("form", "toolPanel");
    setup.innerHTML = [
      "<h2>Live Vote</h2>",
      "<label class=\"field\"><span>Question</span><textarea name=\"question\">Choose tonight's first activity</textarea></label>",
      "<label class=\"field\"><span>Options</span><textarea name=\"options\">Bingo\nLucky Draw\nGuessing Game</textarea></label>",
      "<button class=\"primaryButton\" type=\"submit\">Open Poll</button>"
    ].join("");
    setup.addEventListener("submit", function (event) {
      event.preventDefault();
      openPoll(setup);
    });
    const lottery = el("form", "toolPanel");
    lottery.innerHTML = [
      "<h2>Lucky Draw</h2>",
      "<label class=\"field\"><span>Prize</span><input name=\"lotteryTitle\" maxlength=\"80\" value=\"Lucky Draw\"></label>",
      "<label class=\"field\"><span>Winners</span><input name=\"winnerCount\" type=\"number\" min=\"1\" max=\"10\" value=\"1\"></label>",
      "<label class=\"field\"><span>Seed</span><input name=\"lotterySeed\" maxlength=\"80\" placeholder=\"optional\"></label>",
      "<label class=\"field\"><span>Manual candidates</span><textarea name=\"manualCandidates\" placeholder=\"one name per line\"></textarea></label>",
      "<button class=\"primaryButton\" type=\"submit\">Draw Winners</button>"
    ].join("");
    lottery.addEventListener("submit", function (event) {
      event.preventDefault();
      drawLottery(lottery);
    });
    tools.appendChild(state.activeTool === "lottery" ? lottery : setup);
    const live = el("div", "liveStack");
    if (state.poll) {
      live.appendChild(renderPollView(false));
      const row = el("div", "buttonRow");
      const close = el("button", "primaryButton dangerButton", "Close Poll");
      close.type = "button";
      close.addEventListener("click", closePoll);
      row.appendChild(close);
      live.appendChild(row);
    } else {
      live.appendChild(el("div", "status", "No active poll"));
    }
    live.appendChild(renderCandidatePanel());
    live.appendChild(renderLotteryResult());
    grid.appendChild(tools);
    grid.appendChild(live);
    box.appendChild(grid);
    if (state.status) {
      box.appendChild(el("div", "status", state.status));
    }
    renderShell(box);
  }

  function renderToolTabs() {
    const tabs = el("div", "toolTabs");
    toolDefinitions.forEach(function (tool) {
      const button = el("button", "toolTab", tool.label);
      button.type = "button";
      if (state.activeTool === tool.id) {
        button.classList.add("active");
      }
      button.addEventListener("click", function () {
        state.activeTool = tool.id;
        render();
      });
      tabs.appendChild(button);
    });
    return tabs;
  }

  function renderParticipant() {
    const box = el("section", "surface");
    if (state.poll) {
      box.appendChild(renderPollView(true));
    } else {
      box.appendChild(el("div", "status", "Waiting for host"));
    }
    box.appendChild(renderLotteryResult());
    if (state.status) {
      box.appendChild(el("div", "status", state.status));
    }
    renderShell(box);
  }

  function renderPollView(canVote) {
    const wrap = el("div");
    wrap.appendChild(el("h2", "pollTitle", state.poll.question));
    const options = el("div", "options");
    currentOptions().forEach(function (option) {
      const button = el("button", "optionButton", option.label);
      button.type = "button";
      if (state.selectedOptionId === option.id) {
        button.classList.add("selected");
      }
      button.disabled = !canVote || state.poll.closed;
      button.addEventListener("click", function () {
        castVote(option.id);
      });
      options.appendChild(button);
    });
    wrap.appendChild(options);
    wrap.appendChild(renderResults());
    return wrap;
  }

  function renderResults() {
    const summary = state.closedSummary || (state.role === "host" ? summarizeVotes() : null);
    const box = el("div", "resultList");
    if (!summary) {
      return box;
    }
    const total = Math.max(1, Number(summary.totalVotes || 0));
    currentOptions().forEach(function (option) {
      const count = Number(summary.counts && summary.counts[option.id] || 0);
      const percent = Math.round((count / total) * 100);
      const row = el("div", "resultRow");
      const meta = el("div", "resultMeta");
      meta.appendChild(el("span", "", option.label));
      meta.appendChild(el("strong", "", count + " / " + percent + "%"));
      const meter = el("div", "meter");
      const fill = el("span");
      fill.style.width = percent + "%";
      meter.appendChild(fill);
      row.appendChild(meta);
      row.appendChild(meter);
      box.appendChild(row);
    });
    return box;
  }

  function renderCandidatePanel() {
    const panel = el("div", "candidatePanel");
    const candidates = participantList();
    panel.appendChild(el("h2", "", "Candidates"));
    panel.appendChild(el("p", "finePrint", String(candidates.length) + " joined"));
    const list = el("div", "chipList");
    candidates.slice(0, 16).forEach(function (candidate) {
      list.appendChild(el("span", "chip", candidate.name));
    });
    if (!candidates.length) {
      list.appendChild(el("span", "chip mutedChip", "Waiting"));
    }
    panel.appendChild(list);
    return panel;
  }

  function renderLotteryResult() {
    const panel = el("div", "lotteryResult");
    if (!state.lottery) {
      if (state.role === "host") {
        panel.appendChild(el("div", "status", "No lottery result"));
      }
      return panel;
    }
    panel.appendChild(el("h2", "", state.lottery.title));
    panel.appendChild(el("p", "finePrint", String(state.lottery.candidateCount) + " candidates"));
    const list = el("ol", "winnerList");
    state.lottery.winners.forEach(function (winner) {
      list.appendChild(el("li", "", winner.name || "Winner"));
    });
    panel.appendChild(list);
    return panel;
  }

  function render() {
    if (!state.role) {
      renderStart();
    } else if (state.role === "host") {
      renderHost();
    } else {
      renderParticipant();
    }
  }

  storageGet("activity-suite-member").then(function (memberId) {
    state.memberId = safeId(memberId, "");
    if (!state.memberId) {
      state.memberId = newMemberId();
      storageSet("activity-suite-member", state.memberId);
    }
    return storageGet("activity-suite-name");
  }).then(function (name) {
    state.memberName = String(name || "").slice(0, 40);
    render();
  });
})();
