(() => {
  "use strict";

  const ACCOUNT_KEY = "dailyDice.playerAccount.v2";
  const PENDING_BONUS_KEY = "dailyDice.pendingInviteBonus.v1";
  const REFERRAL_KEYS = ["ref", "invite", "inviteCode", "referral"];

  function readJson(key, fallback = null) {
    try {
      const value = localStorage.getItem(key);
      return value ? JSON.parse(value) : fallback;
    } catch {
      return fallback;
    }
  }

  function writeJson(key, value) {
    try {
      localStorage.setItem(key, JSON.stringify(value));
    } catch {
      // The game still works when storage is blocked.
    }
  }

  function normalizeCode(value) {
    return String(value || "").trim().toUpperCase();
  }

  function normalizePhone(value) {
    const digits = String(value || "").replace(/\D/g, "");
    return digits.startsWith("60") ? `0${digits.slice(2)}` : digits;
  }

  function accountIdentity(name, phone, inviteCode) {
    return [String(name || "").trim().toLowerCase(), normalizePhone(phone), normalizeCode(inviteCode)].join("|");
  }

  function getIncomingReferral() {
    const params = new URLSearchParams(location.search);
    for (const key of REFERRAL_KEYS) {
      const value = normalizeCode(params.get(key));
      if (value) return { key, value };
    }
    return null;
  }

  function removeOwnReferralFromUrl(ownCode) {
    const incoming = getIncomingReferral();
    if (!incoming || incoming.value !== normalizeCode(ownCode)) return;

    const url = new URL(location.href);
    REFERRAL_KEYS.forEach((key) => url.searchParams.delete(key));
    history.replaceState(history.state, "", `${url.pathname}${url.search}${url.hash}`);
  }

  const savedAccount = readJson(ACCOUNT_KEY, {});
  if (savedAccount?.inviteCode) removeOwnReferralFromUrl(savedAccount.inviteCode);

  document.addEventListener("DOMContentLoaded", () => {
    const nameInput = document.getElementById("playerName");
    const phoneInput = document.getElementById("phoneNumber");
    const rememberInput = document.getElementById("rememberPlayer");
    const inviteCodeNode = document.getElementById("inviteCode");
    const popupInviteCodeNode = document.getElementById("resultPopupInviteCode");

    if (savedAccount?.name && nameInput && !nameInput.value) nameInput.value = savedAccount.name;
    if (savedAccount?.phone && phoneInput && !phoneInput.value) phoneInput.value = savedAccount.phone;
    if (rememberInput && (savedAccount?.name || savedAccount?.phone)) rememberInput.checked = true;

    const saveAccount = () => {
      const current = readJson(ACCOUNT_KEY, {});
      const visibleCode = normalizeCode(inviteCodeNode?.textContent || popupInviteCodeNode?.textContent);
      const incomingCode = getIncomingReferral()?.value || "";
      const inviteCode = visibleCode && visibleCode !== incomingCode
        ? visibleCode
        : normalizeCode(current.inviteCode);

      const next = {
        name: String(nameInput?.value || current.name || "").trim(),
        phone: String(phoneInput?.value || current.phone || "").trim(),
        inviteCode,
        updatedAt: new Date().toISOString(),
      };

      if (next.name || next.phone || next.inviteCode) writeJson(ACCOUNT_KEY, next);
      if (next.inviteCode) removeOwnReferralFromUrl(next.inviteCode);
    };

    nameInput?.addEventListener("change", saveAccount);
    phoneInput?.addEventListener("change", saveAccount);
    rememberInput?.addEventListener("change", saveAccount);

    const observer = new MutationObserver(saveAccount);
    if (inviteCodeNode) observer.observe(inviteCodeNode, { childList: true, subtree: true, characterData: true });
    if (popupInviteCodeNode) observer.observe(popupInviteCodeNode, { childList: true, subtree: true, characterData: true });

    setTimeout(() => {
      saveAccount();
      nameInput?.dispatchEvent(new Event("input", { bubbles: true }));
      phoneInput?.dispatchEvent(new Event("input", { bubbles: true }));
      nameInput?.dispatchEvent(new Event("change", { bubbles: true }));
      phoneInput?.dispatchEvent(new Event("change", { bubbles: true }));
    }, 0);
  });

  const originalFetch = window.fetch.bind(window);
  window.fetch = async (input, init = {}) => {
    let payload = null;
    try {
      if (typeof init.body === "string") payload = JSON.parse(init.body);
    } catch {
      payload = null;
    }

    const response = await originalFetch(input, init);
    if (!payload || typeof payload !== "object") return response;

    if (payload.event === "claim_invite_bonus" && response.ok) {
      try {
        const data = await response.clone().json();
        const identity = accountIdentity(payload.name, payload.phone, payload.inviteCode);
        const pending = readJson(PENDING_BONUS_KEY, {});
        const serverBonus = Math.max(0, Number(data.bonusRolls) || 0);

        if (serverBonus > 0) {
          writeJson(PENDING_BONUS_KEY, {
            identity,
            bonusRolls: serverBonus,
            savedAt: new Date().toISOString(),
          });
          return response;
        }

        if (pending?.identity === identity && Number(pending.bonusRolls) > 0) {
          return new Response(JSON.stringify({ ...data, ok: true, bonusRolls: Number(pending.bonusRolls) }), {
            status: response.status,
            statusText: response.statusText,
            headers: { "Content-Type": "application/json; charset=utf-8", "Cache-Control": "no-store" },
          });
        }
      } catch {
        return response;
      }
    }

    if (payload.event === "daily_results" && response.ok && Array.isArray(payload.results) && payload.results.length >= 2) {
      try {
        localStorage.removeItem(PENDING_BONUS_KEY);
      } catch {
        // Ignore storage failures.
      }
    }

    return response;
  };
})();
