(() => {
  const assetTokenStorageKey = "chesscubing-arena-asset-token";
  let viewportStarted = false;
  let audioContext = null;
  let authModalMessageHandler = null;
  let menuScrollStarted = false;
  let menuLastScrollY = 0;
  let menuAnimationFrame = 0;
  let appliedBodyStateClasses = [];

  function syncViewportHeight() {
    const visibleHeight = window.visualViewport?.height ?? window.innerHeight;
    const viewportTopOffset = window.visualViewport?.offsetTop ?? 0;
    const viewportHeight = Math.max(
      visibleHeight + viewportTopOffset,
      window.innerHeight,
      document.documentElement.clientHeight,
    );

    document.documentElement.style.setProperty("--app-visible-height", `${Math.round(visibleHeight)}px`);
    document.documentElement.style.setProperty("--app-viewport-top", `${Math.round(viewportTopOffset)}px`);
    document.documentElement.style.setProperty("--app-viewport-height", `${Math.round(viewportHeight)}px`);
  }

  function startViewport() {
    if (viewportStarted) {
      return;
    }

    viewportStarted = true;
    syncViewportHeight();
    window.addEventListener("load", syncViewportHeight);
    window.addEventListener("resize", syncViewportHeight);
    window.addEventListener("scroll", syncViewportHeight, { passive: true });
    window.addEventListener("pageshow", syncViewportHeight);
    window.addEventListener("orientationchange", syncViewportHeight);
    window.visualViewport?.addEventListener("resize", syncViewportHeight);
    window.visualViewport?.addEventListener("scroll", syncViewportHeight);
    window.setTimeout(syncViewportHeight, 0);
    window.setTimeout(syncViewportHeight, 150);
    window.setTimeout(syncViewportHeight, 400);
    window.requestAnimationFrame(() => window.requestAnimationFrame(syncViewportHeight));
  }

  function setMenuHidden(hidden) {
    document.body.classList.toggle("site-menu-hidden", hidden);
  }

  function shouldHideMenuForModal(modal) {
    if (!modal) {
      return false;
    }

    return window.matchMedia("(max-width: 900px)").matches && modal.querySelector(".auth-modal-card") !== null;
  }

  function syncMenuVisibility() {
    menuAnimationFrame = 0;

    const menu = document.querySelector(".site-menu-shell");
    if (!menu) {
      setMenuHidden(false);
      menuLastScrollY = window.scrollY || window.pageYOffset || 0;
      return;
    }

    const modalIsOpen = document.querySelector(".modal:not(.hidden)");
    if (modalIsOpen) {
      setMenuHidden(shouldHideMenuForModal(modalIsOpen));
      menuLastScrollY = window.scrollY || window.pageYOffset || 0;
      return;
    }

    const mobileMenuIsOpen =
      window.matchMedia("(max-width: 900px)").matches &&
      document.querySelector(".site-menu-main.is-mobile-open") !== null;

    if (mobileMenuIsOpen) {
      setMenuHidden(false);
      menuLastScrollY = window.scrollY || window.pageYOffset || 0;
      return;
    }

    const currentScrollY = Math.max(window.scrollY || window.pageYOffset || 0, 0);
    const delta = currentScrollY - menuLastScrollY;

    if (currentScrollY <= 24 || delta < -10) {
      setMenuHidden(false);
    } else if (delta > 10 && currentScrollY > 120) {
      setMenuHidden(true);
    }

    menuLastScrollY = currentScrollY;
  }

  function queueMenuVisibilitySync() {
    if (menuAnimationFrame) {
      return;
    }

    menuAnimationFrame = window.requestAnimationFrame(syncMenuVisibility);
  }

  function startMenuScrollTracking() {
    if (menuScrollStarted) {
      queueMenuVisibilitySync();
      return;
    }

    menuScrollStarted = true;
    menuLastScrollY = window.scrollY || window.pageYOffset || 0;
    window.addEventListener("scroll", queueMenuVisibilitySync, { passive: true });
    window.addEventListener("pageshow", queueMenuVisibilitySync);
    window.addEventListener("resize", queueMenuVisibilitySync);
    window.addEventListener("hashchange", queueMenuVisibilitySync);
    window.setTimeout(queueMenuVisibilitySync, 0);
    window.setTimeout(queueMenuVisibilitySync, 150);
  }

  function getAudioContext() {
    const AudioContextClass = window.AudioContext || window.webkitAudioContext;
    if (!AudioContextClass) {
      return null;
    }

    if (!audioContext) {
      try {
        audioContext = new AudioContextClass();
      } catch {
        return null;
      }
    }

    return audioContext;
  }

  function primeAudio() {
    const context = getAudioContext();
    if (!context || context.state === "running") {
      return;
    }

    context.resume().catch(() => undefined);
  }

  function playCubePhaseAlert() {
    primeAudio();
    const context = getAudioContext();
    if (!context || context.state !== "running") {
      return false;
    }

    const pattern = [
      { frequency: 740, offset: 0, duration: 0.12, gain: 0.035 },
      { frequency: 988, offset: 0.18, duration: 0.12, gain: 0.04 },
      { frequency: 1318, offset: 0.36, duration: 0.2, gain: 0.05 },
    ];
    const startAt = context.currentTime + 0.02;

    pattern.forEach(({ frequency, offset, duration, gain }) => {
      const oscillator = context.createOscillator();
      const envelope = context.createGain();
      const toneStartAt = startAt + offset;

      oscillator.type = "triangle";
      oscillator.frequency.setValueAtTime(frequency, toneStartAt);
      envelope.gain.setValueAtTime(0.0001, toneStartAt);
      envelope.gain.exponentialRampToValueAtTime(gain, toneStartAt + 0.02);
      envelope.gain.exponentialRampToValueAtTime(0.0001, toneStartAt + duration);

      oscillator.connect(envelope);
      envelope.connect(context.destination);
      oscillator.start(toneStartAt);
      oscillator.stop(toneStartAt + duration + 0.03);
    });

    return true;
  }

  window.chesscubingPage = {
    setBodyState(page, bodyClass) {
      if (page) {
        document.body.dataset.page = page;
      } else {
        delete document.body.dataset.page;
      }

      for (const className of appliedBodyStateClasses) {
        document.body.classList.remove(className);
      }

      appliedBodyStateClasses = (bodyClass || "")
        .split(/\s+/)
        .map((value) => value.trim())
        .filter((value) => value.length > 0);

      if (appliedBodyStateClasses.length > 0) {
        document.body.classList.add(...appliedBodyStateClasses);
      }

      queueMenuVisibilitySync();
    },
  };

  window.chesscubingViewport = {
    start: startViewport,
  };

  window.chesscubingMenu = {
    start: startMenuScrollTracking,
    sync: queueMenuVisibilitySync,
  };

  window.chesscubingStorage = {
    getMatchState(storageKey, windowNameKey) {
      try {
        const raw = window.localStorage.getItem(storageKey);
        if (raw) {
          return raw;
        }
      } catch {
      }

      try {
        if (!window.name || !window.name.startsWith(windowNameKey)) {
          return null;
        }

        return window.name.slice(windowNameKey.length);
      } catch {
        return null;
      }
    },

    setMatchState(storageKey, windowNameKey, value) {
      try {
        window.name = `${windowNameKey}${value}`;
      } catch {
      }

      try {
        window.localStorage.setItem(storageKey, value);
      } catch {
      }
    },

    clearMatchState(storageKey, windowNameKey) {
      try {
        window.localStorage.removeItem(storageKey);
      } catch {
      }

      try {
        if (window.name && window.name.startsWith(windowNameKey)) {
          window.name = "";
        }
      } catch {
      }
    },
  };

  window.chesscubingAudio = {
    prime: primeAudio,
    playCubePhaseAlert,
  };

  window.chesscubingBrowser = {
    async forceRefresh(path) {
      const refreshToken = `${Date.now()}`;

      try {
        window.sessionStorage.setItem(assetTokenStorageKey, refreshToken);
      } catch {
      }

      if ("caches" in window) {
        try {
          const cacheKeys = await window.caches.keys();
          await Promise.all(cacheKeys.map((cacheKey) => window.caches.delete(cacheKey)));
        } catch {
        }
      }

      if ("serviceWorker" in navigator) {
        try {
          const registrations = await navigator.serviceWorker.getRegistrations();
          await Promise.all(registrations.map((registration) => registration.update().catch(() => undefined)));
          await Promise.all(registrations.map((registration) => registration.unregister().catch(() => undefined)));
        } catch {
        }
      }

      const targetUrl = new URL(path, window.location.href);
      targetUrl.searchParams.set("refresh", refreshToken);
      window.location.replace(targetUrl.toString());
    },
  };

  window.chesscubingAuthModal = {
    registerListener(dotNetReference) {
      if (authModalMessageHandler) {
        window.removeEventListener("message", authModalMessageHandler);
      }

      authModalMessageHandler = (event) => {
        if (event.origin !== window.location.origin) {
          return;
        }

        const data = event.data;
        if (!data || data.source !== "chesscubing-auth-modal" || typeof data.status !== "string") {
          return;
        }

        dotNetReference.invokeMethodAsync("HandleAuthModalMessage", data.status).catch(() => undefined);
      };

      window.addEventListener("message", authModalMessageHandler);
    },

    unregisterListener() {
      if (!authModalMessageHandler) {
        return;
      }

      window.removeEventListener("message", authModalMessageHandler);
      authModalMessageHandler = null;
    },

    notifyParent(status) {
      if (!window.parent || window.parent === window) {
        return;
      }

      window.parent.postMessage(
        {
          source: "chesscubing-auth-modal",
          status,
        },
        window.location.origin,
      );
    },
  };

  startViewport();
  startMenuScrollTracking();
})();
