"use strict";

// Each game has its OWN settings: alerts, alert filter, pricing. Poll interval is global.
const GAME_DEFAULTS = () => ({
  notifyDesktop: true, notifySound: true, notifyBadge: true, soundVolume: 0.7,
  minPrice: 0, filterTypes: [], filterRegions: [],
  pricing: {}, // marginPct, minOffer, roundTo, placementPrice, regionMultipliers, divisionPrices, netWinPrices, enabledTiers
});
const DEFAULT_SETTINGS = {
  pollInterval: 8000,
  // Phase 2 automation is GLOBAL (not per-game).
  master: false,
  offer: { autoEnabled: false, deliveryTime: "Day28", maxPerHour: 30, minGapSec: 6, priceCap: 200, categories: ["5", "6"], regions: [] },
  message: { autoStartConversation: false, sendOpener: false, template: "" },
  telegram: { enabled: false, token: "", chatId: "" },
  license: { key: "", status: "none", expiresAt: 0, lastCheck: 0, deviceId: "" },
  games: { lol: GAME_DEFAULTS(), valorant: GAME_DEFAULTS() },
};
const OFFERABLE = ["5", "6"]; // categories with a confirmed offer body (Rank, Placements)

// Per-game region options for the alert filter (LoL uses full names, Valorant short codes).
const GAME_REGIONS = {
  lol: [["Europe West", "EUW"], ["Europe Nordic", "EUNE"], ["North America", "NA"], ["Turkey", "TR"], ["Korea", "KR"], ["Brazil", "BR"], ["Latin America North", "LAN"], ["Latin America South", "LAS"], ["Oceania", "OCE"], ["Russia", "RU"], ["Japan", "JP"]],
  valorant: [["EU", "EU"], ["NA", "NA"], ["AP", "AP"], ["LATAM", "LATAM"], ["Brazil", "BR"], ["Korea", "KR"]],
};

// Migrate any saved settings into the per-game shape.
function normalizeSettings(s) {
  s = s || {};
  const glob = (x) => ({
    pollInterval: x.pollInterval || 8000,
    master: !!x.master,
    offer: Object.assign({}, DEFAULT_SETTINGS.offer, x.offer || {}),
    message: Object.assign({}, DEFAULT_SETTINGS.message, x.message || {}),
    telegram: Object.assign({}, DEFAULT_SETTINGS.telegram, x.telegram || {}),
    license: Object.assign({}, DEFAULT_SETTINGS.license, x.license || {}),
  });
  if (s.games && s.games.lol) {
    return Object.assign(glob(s), {
      games: {
        lol: Object.assign(GAME_DEFAULTS(), s.games.lol),
        valorant: Object.assign(GAME_DEFAULTS(), s.games.valorant),
      },
    });
  }
  const oldP = s.pricing || {};
  const mk = (gk) => ({
    notifyDesktop: s.notifyDesktop !== false,
    notifySound: s.notifySound !== false,
    notifyBadge: s.notifyBadge !== false,
    soundVolume: s.soundVolume != null ? s.soundVolume : 0.7,
    minPrice: s.minPrice || 0,
    filterTypes: s.filterTypes || [],
    filterRegions: s.filterRegions || [],
    pricing: Object.assign(
      { marginPct: oldP.marginPct, minOffer: oldP.minOffer, roundTo: oldP.roundTo, placementPrice: oldP.placementPrice, regionMultipliers: oldP.regionMultipliers },
      (oldP.byGame && oldP.byGame[gk]) || (gk === "lol" ? { divisionPrices: oldP.divisionPrices, netWinPrices: oldP.netWinPrices, enabledTiers: oldP.enabledTiers } : {})
    ),
  });
  return Object.assign(glob(s), { games: { lol: mk("lol"), valorant: mk("valorant") } });
}

// The active pricing block for a request's game.
function gamePricing(game) {
  const g = (settings.games || {})[game] || (settings.games || {}).lol || {};
  return g.pricing || {};
}

// "Key=number" lines <-> object (for step prices / region multipliers)
function parseKVNum(s) {
  const o = {};
  String(s).split("\n").forEach((line) => {
    const [k, v] = line.split("=").map((x) => x.trim());
    if (k && v !== undefined && !isNaN(parseFloat(v))) o[k] = parseFloat(v);
  });
  return o;
}
function kvNumToText(o) {
  return Object.entries(o || {}).map(([k, v]) => `${k}=${v}`).join("\n");
}

// ---- price matrix UI (a separate matrix per game, each in its own settings group) ----
function emblemPath(game, tier) {
  const dir = game === "valorant" ? "assets/ranks/valorant/" : "assets/ranks/";
  return dir + tier.toLowerCase() + ".png";
}

function divInput(tier, div, label, val) {
  return el("label", { class: "dcell" }, [
    el("span", { class: "dlabel", text: label }),
    el("input", { type: "number", min: "0", step: "0.05", class: "p-div", value: val, "data-tier": tier, "data-div": div }),
  ]);
}
function netWinInput(tier, val) {
  return el("label", { class: "dcell" }, [
    el("span", { class: "dlabel", text: "$/win" }),
    el("input", { type: "number", min: "0", step: "0.5", class: "p-netwin", value: val, "data-tier": tier }),
  ]);
}

// Merge a game's pricing block over its defaults (for building the matrix UI).
function mergedMatrix(gameKey, pricing) {
  const P = EldoradoPricing;
  pricing = pricing || {};
  const dp = P.defaultDivisionPrices(gameKey);
  for (const t of Object.keys(dp)) dp[t] = Object.assign({}, dp[t], (pricing.divisionPrices || {})[t] || {});
  return {
    divisionPrices: dp,
    netWinPrices: Object.assign({}, P.defaultNetWin(gameKey), pricing.netWinPrices || {}),
    enabledTiers: Object.assign({}, pricing.enabledTiers || {}),
  };
}

// Build the single #price-matrix for the given game from its pricing block.
function buildMatrix(gameKey, pricing) {
  const box = document.getElementById("price-matrix");
  if (!box || typeof EldoradoPricing === "undefined") return;
  const g = EldoradoPricing.GAMES[gameKey];
  const st = mergedMatrix(gameKey, pricing);
  box.style.setProperty("--divs", String(g.divs.length));
  box.replaceChildren();
  [...g.tiers, ...g.apex].forEach((tier) => {
    const isApex = g.apex.includes(tier);
    const card = el("div", { class: "tcard" });

    const cb = el("input", { type: "checkbox", class: "t-en", "data-tier": tier });
    cb.checked = st.enabledTiers[tier] !== false;
    cb.addEventListener("click", (e) => e.stopPropagation());

    const img = el("img", { class: "ticon", src: emblemPath(gameKey, tier), alt: tier });
    img.addEventListener("error", () => { img.style.visibility = "hidden"; });

    const head = el("div", { class: "tcard-head" }, [
      cb, img,
      el("span", { class: "tname", text: tier }),
      el("span", { class: "tedit", text: "Click to edit" }),
    ]);
    head.addEventListener("click", () => card.classList.toggle("open"));

    const body = el("div", { class: "tcard-body" });
    body.appendChild(el("div", { class: "mlabel", text: isApex ? "Rank boost · flat" : "Rank boost · per division" }));
    const rb = el("div", { class: "mrow" });
    const dp = st.divisionPrices[tier] || {};
    if (isApex) rb.appendChild(divInput(tier, "flat", "Price", dp.flat));
    else g.divs.forEach((d) => rb.appendChild(divInput(tier, d, d, dp[d])));
    body.appendChild(rb);
    body.appendChild(el("div", { class: "mlabel", text: "Net Wins · per win" }));
    body.appendChild(el("div", { class: "mrow one" }, [netWinInput(tier, st.netWinPrices[tier])]));

    card.appendChild(head);
    card.appendChild(body);
    box.appendChild(card);
  });
}

// Read the visible #price-matrix into a pricing block
function readMatrix() {
  const root = document.getElementById("price-matrix");
  const divisionPrices = {}, netWinPrices = {}, enabledTiers = {};
  if (!root) return { divisionPrices, netWinPrices, enabledTiers };
  root.querySelectorAll(".p-div").forEach((inp) => {
    const t = inp.getAttribute("data-tier"), d = inp.getAttribute("data-div");
    (divisionPrices[t] = divisionPrices[t] || {})[d] = parseFloat(inp.value) || 0;
  });
  root.querySelectorAll(".t-en").forEach((cb) => { enabledTiers[cb.getAttribute("data-tier")] = cb.checked; });
  root.querySelectorAll(".p-netwin").forEach((inp) => { netWinPrices[inp.getAttribute("data-tier")] = parseFloat(inp.value) || 0; });
  return { divisionPrices, netWinPrices, enabledTiers };
}

const $ = (id) => document.getElementById(id);
// Null-safe addEventListener: skips (and warns) if the element isn't in the DOM,
// so one missing/stale element can't break the whole popup.
function $on(id, ev, fn) {
  const e = $(id);
  if (e) e.addEventListener(ev, fn);
  else console.warn("[BoostWatcher] popup: missing #" + id);
}
let allRequests = [];
let offeredMap = {}; // detailsId -> { offerId, price, ... }
let settings = Object.assign({}, DEFAULT_SETTINGS);

// ---- helpers ----
function sendSW(message) {
  return new Promise((res) => {
    try {
      chrome.runtime.sendMessage(message, (r) => {
        void chrome.runtime.lastError;
        res(r);
      });
    } catch (e) {
      res(null);
    }
  });
}
function getLocal(keys) {
  return new Promise((r) => chrome.storage.local.get(keys, r));
}
function setLocal(obj) {
  return new Promise((r) => chrome.storage.local.set(obj, r));
}

function relTime(iso) {
  if (!iso) return "";
  const t = new Date(iso).getTime();
  if (isNaN(t)) return "";
  const s = Math.max(0, (Date.now() - t) / 1000);
  if (s < 60) return `${Math.floor(s)}s`;
  if (s < 3600) return `${Math.floor(s / 60)}m`;
  if (s < 86400) return `${Math.floor(s / 3600)}h`;
  return `${Math.floor(s / 86400)}d`;
}

// Full region name -> short code (for the header line, e.g. "Custom Request - TR")
const REGION_CODES = [
  ["europe nordic", "EUNE"], ["europe west", "EUW"], ["north america", "NA"],
  ["latin america north", "LAN"], ["latin america south", "LAS"], ["brazil", "BR"],
  ["turkey", "TR"], ["korea", "KR"], ["japan", "JP"], ["oceania", "OCE"],
  ["russia", "RU"], ["middle east", "ME"], ["singapore", "SG"],
  ["vietnam", "VN"], ["taiwan", "TW"], ["philippines", "PH"], ["thailand", "TH"],
];
function regionCode(region) {
  if (!region) return "";
  const r = String(region).toLowerCase();
  for (const [k, v] of REGION_CODES) if (r.includes(k)) return v;
  return region; // unknown: show the full text as-is
}

// Small DOM helper: el("div", {class:"x"}, [children/text])
function el(tag, attrs, children) {
  const node = document.createElement(tag);
  if (attrs) {
    for (const k in attrs) {
      if (k === "class") node.className = attrs[k];
      else if (k === "text") node.textContent = attrs[k];
      else if (k.startsWith("data-")) node.setAttribute(k, attrs[k]);
      else node[k] = attrs[k];
    }
  }
  if (children != null) {
    (Array.isArray(children) ? children : [children]).forEach((c) => {
      if (c == null) return;
      node.appendChild(typeof c === "string" ? document.createTextNode(c) : c);
    });
  }
  return node;
}

// ---- render ----
function applyViewFilters(list) {
  const game = $("f-game").value;
  const cat = $("f-category").value;
  const region = $("f-region").value.trim().toLowerCase();
  const onlyAlertable = $("f-alertable").checked;
  return list.filter((r) => {
    if (game && r.game !== game) return false;
    if (cat && r.type !== cat) return false;
    if (region && !String(r.region || "").toLowerCase().includes(region)) return false;
    if (onlyAlertable && !r.alertable) return false;
    return true;
  });
}

// Rank name → emblem image, game-aware. Target rank preferred, else current.
function rankEmblem(game, rank) {
  if (!rank || typeof EldoradoPricing === "undefined") return null;
  const g = EldoradoPricing.GAMES[game] || EldoradoPricing.GAMES.lol;
  const i = EldoradoPricing.parseRank(rank, g);
  if (i < 0) return null;
  return emblemPath(g.key, g.ladder[i].tier);
}

const STATUS_LABEL = { taken: "Taken", cancelled: "Cancelled", expired: "Expired", inactive: "Inactive" };

// Icon-only action button (open = arrow, dismiss = X).
function iconBtn(act, label, svg) {
  const b = el("button", { class: "rowbtn " + act, "data-act": act, title: label });
  b.setAttribute("aria-label", label);
  b.innerHTML = svg;
  return b;
}
const ICON_OPEN = '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M7 17 17 7M9 7h8v8"/></svg>';
const ICON_X = '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M6 6 18 18M18 6 6 18"/></svg>';

function rowNode(r, hot) {
  const inactive = r.status && r.status !== "active";
  const item = el("div", {
    class: "item" + (r.unread ? " unread" : "") + (inactive ? " inactive" : "") + (hot ? " hot" : ""),
    "data-id": r.detailsId || "",
    "data-reqid": r.id || "",
  });

  const embSrc = rankEmblem(r.game, r.targetRank) || rankEmblem(r.game, r.currentRank);
  let emb;
  if (embSrc) {
    emb = el("img", { class: "emb", src: embSrc, alt: "" });
    emb.addEventListener("error", () => { emb.style.visibility = "hidden"; });
  } else {
    emb = el("span", { class: "emb emb-none", text: (r.category || "?").charAt(0) });
  }

  const gameTag = r.game === "valorant" ? "VAL" : r.game === "lol" ? "LoL" : (r.game || "");
  const chips = el("div", { class: "chips" }, [
    hot ? el("span", { class: "chip hot", text: "★ Top" }) : null,
    inactive ? el("span", { class: "chip s-" + r.status, text: STATUS_LABEL[r.status] || "Inactive" }) : null,
    gameTag ? el("span", { class: "chip g-" + r.game, text: gameTag }) : null,
    el("span", { class: "chip t-" + (r.type || "other"), text: r.category || "Boost" }),
    r.region ? el("span", { class: "chip", text: regionCode(r.region) }) : null,
  ]);

  let flow;
  if (r.currentRank && r.targetRank) {
    flow = el("div", { class: "flow" }, [r.currentRank, el("span", { class: "arrow", text: "→" }), r.targetRank]);
  } else {
    flow = el("div", { class: "flow", text: r.currentRank || r.extra || (r.category || "—") });
  }

  const sub = el("div", { class: "sub", text: [r.buyer, r.region].filter(Boolean).join(" · ") });
  const center = el("div", { class: "center" }, [chips, flow, sub]);

  const pr = typeof EldoradoPricing !== "undefined" ? EldoradoPricing.compute(r, gamePricing(r.game)) : null;
  const priced = pr && pr.final != null;
  const right = el("div", { class: "right" }, [
    el("div", { class: "price" + (priced ? "" : " none"), text: priced ? "$" + pr.final + (pr.perWin ? "/win" : "") : "—" }),
    el("div", { class: "time", text: relTime(r.date || (r.firstSeenTs && new Date(r.firstSeenTs).toISOString())) }),
  ]);

  // Offer button / offered state (Phase 2)
  const detId = r.detailsId;
  const off = detId ? offeredMap[detId] : null;
  if (off && off.offerId) {
    right.appendChild(el("div", { class: "offered", text: "✓ $" + off.price }));
  } else if (priced && detId && OFFERABLE.includes(String(r.categoryId))) {
    const btn = el("button", { class: "offerbtn", text: "Offer $" + pr.final });
    const licOk = settings.license && settings.license.status === "active" && settings.license.expiresAt > Date.now();
    if (!settings.master || !licOk) {
      btn.disabled = true;
      btn.title = licOk ? "Enable the master switch in Settings → Automation" : "Activate your license in Settings → License";
    }
    btn.addEventListener("click", (ev) => { ev.stopPropagation(); doOffer(r, pr.final, btn); });
    right.appendChild(btn);
  }

  const actions = el("div", { class: "row-actions" }, [
    iconBtn("open", "Aç", ICON_OPEN),
    iconBtn("dismiss", "Yoksay", ICON_X),
  ]);

  item.appendChild(emb);
  item.appendChild(center);
  item.appendChild(right);
  item.appendChild(actions);
  return item;
}

// ---- offers (Phase 2) ----
async function doOffer(req, price, btn) {
  if (btn) { btn.disabled = true; btn.textContent = "…"; }
  const r = await sendSW({ type: "makeOffer", req, price });
  if (r && r.ok) {
    offeredMap[req.detailsId] = { offerId: r.offerId, price: r.price };
    render();
  } else {
    const why = (r && r.error) || "failed";
    if (btn) {
      btn.disabled = false;
      btn.title = "Failed: " + why;
      btn.textContent = why === "master-off" ? "master off" : "✗ retry";
    }
  }
}

// Suggested price for a request (its game's matrix), or null if unpriceable.
function priceOf(r) {
  if (typeof EldoradoPricing === "undefined") return null;
  try {
    const pr = EldoradoPricing.compute(r, gamePricing(r.game));
    return pr && pr.final != null ? pr.final : null;
  } catch (e) { return null; }
}
function isActive(r) { return !(r.status && r.status !== "active"); }

function render() {
  let view = applyViewFilters(allRequests);
  const mode = ($("f-sort") && $("f-sort").value) || "newest";
  // Inactive (taken/cancelled) always sink to the bottom; then by chosen mode.
  if (mode === "value") {
    view = view.slice().sort((a, b) =>
      (isActive(b) - isActive(a)) ||
      ((priceOf(b) == null ? -1 : priceOf(b)) - (priceOf(a) == null ? -1 : priceOf(a))) ||
      ((b.firstSeenTs || 0) - (a.firstSeenTs || 0))
    );
  } else {
    view = view.slice().sort((a, b) => (isActive(b) - isActive(a)) || ((b.firstSeenTs || 0) - (a.firstSeenTs || 0)));
  }
  // Highlight the top value tier (active, priced, >= 0.8 × highest in view).
  const prices = view.filter((r) => isActive(r) && priceOf(r) != null).map((r) => priceOf(r));
  const maxP = prices.length ? Math.max.apply(null, prices) : 0;
  const hotMin = prices.length > 1 && maxP > 0 ? maxP * 0.8 : Infinity;

  const listEl = $("list");
  listEl.replaceChildren();
  if (!view.length) {
    $("empty").classList.remove("hidden");
  } else {
    $("empty").classList.add("hidden");
    const frag = document.createDocumentFragment();
    view.forEach((r) => frag.appendChild(rowNode(r, isActive(r) && priceOf(r) != null && priceOf(r) >= hotMin)));
    listEl.appendChild(frag);
  }
  renderStats(view);
}

function isToday(ts) {
  if (!ts) return false;
  return new Date(ts).toDateString() === new Date().toDateString();
}

function renderStats(list) {
  const box = $("stats");
  if (!box) return;
  list = list || allRequests; // stats follow the current category/view filter
  const total = list.length;
  const today = list.filter((r) => isToday(r.firstSeenTs || (r.date ? Date.parse(r.date) : 0))).length;
  const hasP = typeof EldoradoPricing !== "undefined";
  const sym = "$";
  let sum = 0, n = 0;
  if (hasP) list.forEach((r) => {
    const pr = EldoradoPricing.compute(r, gamePricing(r.game));
    if (pr && pr.final != null) { sum += pr.final; n++; }
  });
  const avg = n ? Math.round(sum / n) : null;
  const stat = (v, l) => el("div", { class: "stat" }, [el("div", { class: "v", text: String(v) }), el("div", { class: "l", text: l })]);
  box.replaceChildren(stat(today, "Today"), stat(total, "Total"), stat(avg != null ? sym + avg : "—", "Avg price"));
}

function renderStatus(status, lastPoll) {
  const elx = $("status");
  elx.className = "status " + (status || "");
  const ago = lastPoll ? ` · ${relTime(new Date(lastPoll).toISOString())} ago` : "";
  if (status === "ok") elx.textContent = "connected" + ago;
  else if (status === "auth") elx.textContent = "login required — sign in to eldorado";
  else if (status === "error") elx.textContent = "connection error" + ago;
  else elx.textContent = "waiting for eldorado tab";
}

// ---- settings panel (per game) ----
let gtab = "lol";
let gameForm = { lol: GAME_DEFAULTS(), valorant: GAME_DEFAULTS() };

function buildRegionFilter(gameKey, selected) {
  const box = $("regionfilter");
  if (!box) return;
  box.replaceChildren();
  (GAME_REGIONS[gameKey] || []).forEach(([value, label]) => {
    const inp = el("input", { type: "checkbox", class: "s-freg", value });
    inp.checked = (selected || []).includes(value);
    box.appendChild(el("label", {}, [inp, " " + label]));
  });
}

// Put a game's saved settings into the form fields.
function populateGameForm(gameKey) {
  const gf = gameForm[gameKey];
  const pd = EldoradoPricing.DEFAULTS;
  $("ga-label").textContent = gameKey === "valorant" ? "Valorant" : "League of Legends";
  $("s-volume").value = gf.soundVolume != null ? gf.soundVolume : 0.7;
  $("s-desktop").checked = !!gf.notifyDesktop;
  $("s-sound").checked = !!gf.notifySound;
  $("s-badge").checked = !!gf.notifyBadge;
  $("s-minprice").value = gf.minPrice ?? 0;
  document.querySelectorAll(".s-fcat").forEach((c) => { c.checked = (gf.filterTypes || []).includes(c.value); });
  buildRegionFilter(gameKey, gf.filterRegions);
  const pp = gf.pricing || {};
  $("p-margin").value = pp.marginPct ?? pd.marginPct;
  $("p-min").value = pp.minOffer ?? pd.minOffer;
  $("p-round").value = pp.roundTo ?? pd.roundTo;
  $("p-place").value = pp.placementPrice ?? pd.placementPrice;
  $("p-regions").value = kvNumToText(pp.regionMultipliers || pd.regionMultipliers);
  buildMatrix(gameKey, pp);
}

// Read the form fields back into a game's working settings.
function readGameForm(gameKey) {
  const types = []; document.querySelectorAll(".s-fcat").forEach((c) => { if (c.checked) types.push(c.value); });
  const regions = []; document.querySelectorAll(".s-freg").forEach((c) => { if (c.checked) regions.push(c.value); });
  gameForm[gameKey] = {
    notifyDesktop: $("s-desktop").checked,
    notifySound: $("s-sound").checked,
    notifyBadge: $("s-badge").checked,
    soundVolume: Number($("s-volume").value),
    minPrice: parseFloat($("s-minprice").value) || 0,
    filterTypes: types,
    filterRegions: regions,
    pricing: Object.assign({
      marginPct: parseFloat($("p-margin").value) || 0,
      minOffer: parseFloat($("p-min").value) || 0,
      roundTo: parseFloat($("p-round").value) || 0.01,
      placementPrice: parseFloat($("p-place").value) || 0,
      regionMultipliers: parseKVNum($("p-regions").value),
    }, readMatrix()),
  };
}

function switchGtab(gameKey) {
  readGameForm(gtab); // keep current game's edits
  gtab = gameKey;
  document.querySelectorAll(".gtab").forEach((b) => b.classList.toggle("on", b.getAttribute("data-game") === gameKey));
  populateGameForm(gameKey);
}

function fillSettingsForm() {
  $("s-interval").value = Math.round((settings.pollInterval || 8000) / 1000);
  gameForm = {
    lol: Object.assign(GAME_DEFAULTS(), (settings.games || {}).lol),
    valorant: Object.assign(GAME_DEFAULTS(), (settings.games || {}).valorant),
  };
  gtab = "lol";
  document.querySelectorAll(".gtab").forEach((b) => b.classList.toggle("on", b.getAttribute("data-game") === "lol"));
  populateGameForm("lol");
  // automation (global, Phase 2)
  const o = settings.offer || {}, m = settings.message || {};
  $("a-master").checked = !!settings.master;
  $("a-auto").checked = !!o.autoEnabled;
  $("a-delivery").value = o.deliveryTime || "Day28";
  $("a-maxhour").value = o.maxPerHour ?? 30;
  $("a-gap").value = o.minGapSec ?? 6;
  $("a-cap").value = o.priceCap ?? 200;
  $("a-regions").value = (o.regions || []).join(", ");
  document.querySelectorAll(".a-cat").forEach((c) => { c.checked = (o.categories || ["5", "6"]).includes(c.value); });
  $("a-conv").checked = !!m.autoStartConversation;
  $("a-opener").checked = !!m.sendOpener;
  $("a-template").value = m.template || "";
  // telegram (global phone alerts)
  const tg = settings.telegram || {};
  if ($("tg-enabled")) {
    $("tg-enabled").checked = !!tg.enabled;
    $("tg-token").value = tg.token || "";
    $("tg-chat").value = tg.chatId || "";
  }
  // license
  if ($("lic-key")) $("lic-key").value = (settings.license && settings.license.key) || "";
  renderLicenseStatus(settings.license);
}

function renderLicenseStatus(lic) {
  const el = $("lic-status");
  if (!el) return;
  lic = lic || {};
  if (!lic.key) { el.textContent = "Not activated — paste your key below."; el.style.color = "var(--muted)"; return; }
  const days = lic.expiresAt ? Math.ceil((lic.expiresAt - Date.now()) / 86400000) : 0;
  if (lic.status === "active" && days > 0) {
    el.style.color = "var(--green)";
    el.textContent = "Active · " + days + " day" + (days === 1 ? "" : "s") + " left";
  } else if (lic.status === "revoked") {
    el.style.color = "var(--red)"; el.textContent = "Revoked.";
  } else {
    el.style.color = "var(--red)";
    el.textContent = days <= 0 ? "Expired — renew to keep offering." : "Inactive — re-activate your key.";
  }
}

async function saveSettings() {
  readGameForm(gtab); // capture the currently-shown game's edits
  settings = {
    pollInterval: Math.max(5, Number($("s-interval").value) || 8) * 1000,
    master: $("a-master").checked,
    offer: {
      autoEnabled: $("a-auto").checked,
      deliveryTime: $("a-delivery").value || "Day28",
      maxPerHour: Math.max(1, Number($("a-maxhour").value) || 30),
      minGapSec: Math.max(0, Number($("a-gap").value) || 0),
      priceCap: Math.max(0, Number($("a-cap").value) || 0),
      categories: Array.from(document.querySelectorAll(".a-cat")).filter((c) => c.checked).map((c) => c.value),
      regions: $("a-regions").value.split(",").map((x) => x.trim()).filter(Boolean),
    },
    message: {
      autoStartConversation: $("a-conv").checked,
      sendOpener: $("a-opener").checked,
      template: $("a-template").value,
    },
    telegram: $("tg-enabled") ? {
      enabled: $("tg-enabled").checked,
      token: $("tg-token").value.trim(),
      chatId: $("tg-chat").value.trim(),
    } : (settings.telegram || { enabled: false, token: "", chatId: "" }),
    license: settings.license || { key: "", status: "none", expiresAt: 0, lastCheck: 0, deviceId: "" },
    games: { lol: gameForm.lol, valorant: gameForm.valorant },
  };
  await setLocal({ settings });
  render(); // refresh prices in the list immediately
  $("btn-save").textContent = "Saved ✓";
  setTimeout(() => ($("btn-save").textContent = "Save"), 1200);
}

// ---- events ----
function wireEvents() {
  ["f-game", "f-category", "f-region", "f-alertable", "f-sort"].forEach((id) => $on(id, "input", render));
  $on("btn-markread", "click", () => sendSW({ type: "markAllRead" }));
  $on("btn-clear", "click", () => sendSW({ type: "clear" }));
  $on("btn-save", "click", saveSettings);
  $on("btn-testsound", "click", () => sendSW({ type: "testSound" }));
  $on("btn-testtelegram", "click", async () => {
    const out = $("tg-result");
    out.classList.remove("hidden");
    out.style.color = "var(--muted)";
    out.textContent = "Sending to Telegram…";
    const r = await sendSW({ type: "testTelegram", token: $("tg-token").value.trim(), chatId: $("tg-chat").value.trim() });
    out.style.color = r && r.ok ? "var(--green)" : "var(--red)";
    out.textContent = r && r.ok ? "Sent ✓ — check your phone, then Save." : "Failed: " + ((r && r.error) || "no response");
  });
  $on("btn-activate", "click", async () => {
    const out = $("lic-result");
    out.classList.remove("hidden");
    out.style.color = "var(--muted)";
    out.textContent = "Activating…";
    const key = ($("lic-key").value || "").trim();
    const r = await sendSW({ type: "activateLicense", key });
    if (r && r.valid) {
      out.style.color = "var(--green)";
      out.textContent = "Activated ✓";
    } else {
      const why = (r && r.status) || "failed";
      out.style.color = "var(--red)";
      out.textContent =
        why === "device-mismatch" ? "This key is already in use on another device." :
        why === "expired" ? "This key has expired — renew first." :
        why === "offline" ? "Couldn't reach the server — check your connection and try again." :
        why === "unknown" ? "Invalid key — check it and try again." :
        "Couldn't activate.";
    }
    const d = await getLocal("settings");
    settings = normalizeSettings(d.settings);
    if ($("lic-key")) $("lic-key").value = (settings.license && settings.license.key) || key;
    renderLicenseStatus(settings.license);
    render();
  });
  $on("btn-testnotif", "click", async () => {
    const out = $("notif-result");
    out.classList.remove("hidden");
    out.style.color = "var(--muted)";
    out.textContent = "Sending test notification…";
    const r = await sendSW({ type: "testNotif" });
    if (r && r.ok) {
      out.style.color = "var(--green)";
      out.textContent = "Sent ✓ — if no popup appeared, Windows/Chrome is blocking it (see below).";
    } else {
      out.style.color = "var(--red)";
      out.textContent = "Failed: " + ((r && r.error) || "no response from extension");
    }
  });
  $on("tab-live", "click", () => switchTab("live"));
  $on("tab-settings", "click", () => switchTab("settings"));
  document.querySelectorAll(".group-h").forEach((h) =>
    h.addEventListener("click", () => h.parentElement.classList.toggle("open"))
  );
  document.querySelectorAll(".gtab").forEach((b) =>
    b.addEventListener("click", () => switchGtab(b.getAttribute("data-game")))
  );

  $on("list", "click", (e) => {
    const btn = e.target.closest(".rowbtn");
    if (!btn) return; // only the Aç / Yoksay buttons are clickable
    const card = e.target.closest(".item");
    if (!card) return;
    if (btn.getAttribute("data-act") === "dismiss") {
      const reqId = card.getAttribute("data-reqid");
      if (reqId) sendSW({ type: "dismissOne", id: reqId });
    } else {
      const id = card.getAttribute("data-id");
      chrome.tabs.create({
        url: id
          ? `https://www.eldorado.gg/boosting-request/${id}`
          : "https://www.eldorado.gg/dashboard/notifications",
      });
    }
  });
}

function switchTab(which) {
  const live = which === "live";
  $("tab-live").classList.toggle("on", live);
  $("tab-settings").classList.toggle("on", !live);
  $("live").classList.toggle("hidden", !live);
  $("settings").classList.toggle("hidden", live);
}

// ---- init ----
async function init() {
  wireEvents();
  const data = await getLocal(["settings", "requests", "status", "lastPoll", "offered"]);
  settings = normalizeSettings(data.settings);
  allRequests = Array.isArray(data.requests) ? data.requests : [];
  offeredMap = data.offered && typeof data.offered === "object" ? data.offered : {};
  try { fillSettingsForm(); } catch (e) { console.warn("[BoostWatcher] fillSettingsForm:", e); }
  render();
  renderStatus(data.status, data.lastPoll);

  // Mark read when popup opens (clears the badge)
  sendSW({ type: "markAllRead" });

  // Live-update while open
  chrome.storage.onChanged.addListener((changes, area) => {
    if (area !== "local") return;
    if (changes.requests) {
      allRequests = changes.requests.newValue || [];
      render();
    }
    if (changes.settings) {
      settings = normalizeSettings(changes.settings.newValue);
      render(); // pricing/region changes affect displayed prices
    }
    if (changes.offered) {
      offeredMap = changes.offered.newValue || {};
      render();
    }
    if (changes.status || changes.lastPoll) {
      getLocal(["status", "lastPoll"]).then((d) => renderStatus(d.status, d.lastPoll));
    }
  });
}

init();
