const params = new URLSearchParams(window.location.search);
const token = params.get("token") || "";
const root = document.documentElement;
const field = document.querySelector("#lightField");
const startPanel = document.querySelector("#startPanel");
const startButton = document.querySelector("#startButton");
const introText = document.querySelector("#introText");
const safetyText = document.querySelector("#safetyText");
const sessionTitle = document.querySelector("#sessionTitle");
const phaseName = document.querySelector("#phaseName");
const frequencyValue = document.querySelector("#frequencyValue");
const statusPanel = document.querySelector("#statusPanel");
const preparationPanel = document.querySelector("#preparationPanel");
const preparationLabel = document.querySelector("#preparationLabel");
const preparationSeconds = document.querySelector("#preparationSeconds");
const preparationHint = document.querySelector("#preparationHint");
const fullscreenRestoreButton = document.querySelector("#fullscreenRestoreButton");
const IS_IOS = /iPad|iPhone|iPod/.test(navigator.userAgent)
  || (navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1);
const SCREEN_LIGHT_STATE_POLL_MS = 33;

let lightState = {
  active: false,
  frequencyHz: 10,
  pulseShape: "sine",
  pulseMode: "sync",
  pulseEpochMs: Date.now(),
  lightCodeActive: false,
  lightCodeType: "",
  lightCodeDirect: false,
  lightCodeFloorAlpha: 0,
  color: "#40c9ff",
  intensity: 0,
  language: "nl",
  sessionTitle: "NeuroSync Pro",
  phase: "Stand-by"
};
let wakeLock = null;
let wakeLockWanted = false;
let noSleep = null;
let statusHideTimer = null;
let keepAwakeVideo = null;
let keepAwakeCanvas = null;
let keepAwakeTimer = null;
let keepAwakeRetryTimer = null;
let fullscreenRetryTimer = null;
let clientActiveTimer = null;
let fullscreenWanted = false;
let lastTouchRestoreAt = 0;
let oscillator = {
  phase: 0,
  lastFrameMs: performance.now(),
  lastActive: false,
  lastFrequencyHz: 10,
  lastPulseEpochMs: 0,
  lastPulseMode: "sync"
};

function endpoint(path) {
  const url = new URL(path, window.location.origin);
  if (token) url.searchParams.set("token", token);
  return url;
}

function clamp(value, min, max) {
  return Math.min(max, Math.max(min, Number(value) || 0));
}

function pulseMultiplier(position, shape) {
  if (shape === "triangle") return 1 - Math.abs(position * 2 - 1);
  if (shape === "square") return position < 0.5 ? 1 : 0;
  return 0.5 - 0.5 * Math.cos(position * Math.PI * 2);
}

function syncOscillatorToState(frequency) {
  const pulseEpochMs = Number(lightState.pulseEpochMs);
  if (Number.isFinite(pulseEpochMs) && pulseEpochMs > 0) {
    const elapsed = (Date.now() - pulseEpochMs) / 1000;
    oscillator.phase = ((elapsed * frequency) % 1 + 1) % 1;
    oscillator.lastPulseEpochMs = pulseEpochMs;
  } else {
    oscillator.phase = 0;
    oscillator.lastPulseEpochMs = 0;
  }
  oscillator.lastFrequencyHz = frequency;
  oscillator.lastPulseMode = lightState.pulseMode;
}

function applyLanguage(language) {
  const nl = language === "nl";
  document.documentElement.lang = nl ? "nl" : "en";
  introText.textContent = nl
    ? "Gebruik het volledige scherm als persoonlijke lichtbron voor entrainment."
    : "Use the full screen as a personal light source for entrainment.";
  startButton.textContent = IS_IOS
    ? (nl ? "Start stabiele lichtmodus" : "Start stable light mode")
    : (nl ? "Start volledig scherm" : "Start full screen");
  safetyText.textContent = nl
    ? "Niet gebruiken bij lichtgevoelige epilepsie of ongemak."
    : "Do not use with photosensitive epilepsy or discomfort.";
  preparationLabel.textContent = nl ? "Sessie start over" : "Session starts in";
  preparationHint.textContent = nl
    ? "Plaats de smartphone in de VR-adapter."
    : "Place the smartphone in the VR adapter.";
  fullscreenRestoreButton.textContent = nl
    ? "Tik om volledig scherm te herstellen"
    : "Tap to restore full screen";
}

async function requestWakeLock() {
  wakeLockWanted = true;
  try {
    wakeLock = await navigator.wakeLock?.request("screen");
    wakeLock?.addEventListener?.("release", () => {
      wakeLock = null;
      if (wakeLockWanted && document.visibilityState === "visible") {
        window.setTimeout(() => void requestWakeLock(), 500);
      }
    }, { once: true });
  } catch {
    wakeLock = null;
  }
}

function fullscreenElement() {
  return document.fullscreenElement
    || document.webkitFullscreenElement
    || document.mozFullScreenElement
    || document.msFullscreenElement
    || null;
}

function updateViewportHeight() {
  const width = window.visualViewport?.width || window.innerWidth || document.documentElement.clientWidth || 0;
  const height = window.visualViewport?.height || window.innerHeight || document.documentElement.clientHeight || 0;
  if (width > 0) root.style.setProperty("--viewport-width", `${width}px`);
  if (height > 0) root.style.setProperty("--viewport-height", `${height}px`);
}

function collapseMobileBrowserChrome() {
  updateViewportHeight();
  window.scrollTo(0, 120);
  window.setTimeout(() => window.scrollTo(0, 120), 80);
  window.setTimeout(() => {
    window.scrollTo(0, 120);
    updateViewportHeight();
  }, 220);
}

async function requestFullscreenMode() {
  if (fullscreenElement()) return true;
  if (IS_IOS) {
    collapseMobileBrowserChrome();
    updateFullscreenRestorePrompt();
    return true;
  }
  const targets = [document.documentElement, document.body, document.querySelector(".screen-light-shell")].filter(Boolean);
  for (const target of targets) {
    const request = target.requestFullscreen
      || target.webkitRequestFullscreen
      || target.mozRequestFullScreen
      || target.msRequestFullscreen;
    if (typeof request !== "function") continue;
    try {
      await request.call(target);
      const enteredFullscreen = Boolean(fullscreenElement());
      updateFullscreenRestorePrompt();
      collapseMobileBrowserChrome();
      return enteredFullscreen;
    } catch {
      // Try the next supported target; iOS can reject one element and accept another.
    }
  }
  collapseMobileBrowserChrome();
  updateFullscreenRestorePrompt();
  return false;
}

function sessionWantsFullscreen() {
  return fullscreenWanted || Boolean(lightState.active || lightState.preparation?.active);
}

function updateFullscreenRestorePrompt() {
  fullscreenRestoreButton.hidden = IS_IOS
    || Boolean(fullscreenElement())
    || !sessionWantsFullscreen()
    || !startPanel.classList.contains("hidden");
}

function scheduleFullscreenRetry(delay = 350) {
  if (IS_IOS || fullscreenRetryTimer || fullscreenElement() || !sessionWantsFullscreen()) return;
  fullscreenRetryTimer = window.setTimeout(() => {
    fullscreenRetryTimer = null;
    if (document.visibilityState === "visible" && sessionWantsFullscreen() && !fullscreenElement()) {
      void requestFullscreenMode().then(() => updateFullscreenRestorePrompt());
      scheduleFullscreenRetry(1500);
    }
  }, delay);
}

function restoreFullscreenAfterViewportChange() {
  if (!sessionWantsFullscreen() || !startPanel.classList.contains("hidden")) return;
  updateViewportHeight();
  collapseMobileBrowserChrome();
  updateFullscreenRestorePrompt();
  if (IS_IOS) return;
  window.setTimeout(() => {
    void requestFullscreenMode().then(() => updateFullscreenRestorePrompt());
  }, 120);
  scheduleFullscreenRetry(700);
}

async function requestScreenOrientationLock() {
  if (!screen.orientation?.lock) return false;
  const width = window.visualViewport?.width || window.innerWidth || 0;
  const height = window.visualViewport?.height || window.innerHeight || 0;
  const currentOrientation = width > height ? "landscape" : "portrait";
  try {
    await screen.orientation.lock(currentOrientation);
    return true;
  } catch {
    return false;
  }
}

async function startKeepAwakeFallback() {
  if (!noSleep && typeof NoSleep === "function") {
    try {
      noSleep = new NoSleep();
    } catch {
      noSleep = null;
    }
  }
  if (noSleep && !noSleep.isEnabled) {
    await noSleep.enable().catch(() => {});
  }
  if (keepAwakeVideo) {
    await keepAwakeVideo.play().catch(() => {});
    return;
  }
  try {
    keepAwakeCanvas = document.createElement("canvas");
    keepAwakeCanvas.width = 2;
    keepAwakeCanvas.height = 2;
    const context = keepAwakeCanvas.getContext("2d");
    let frame = 0;
    const draw = () => {
      if (!context) return;
      context.fillStyle = frame % 2 ? "#000" : "#010101";
      context.fillRect(0, 0, 2, 2);
      frame += 1;
    };
    draw();
    keepAwakeTimer = setInterval(draw, 15000);

    const stream = keepAwakeCanvas.captureStream?.(0.1);
    if (!stream) return;
    keepAwakeVideo = document.createElement("video");
    keepAwakeVideo.muted = true;
    keepAwakeVideo.loop = true;
    keepAwakeVideo.playsInline = true;
    keepAwakeVideo.setAttribute("playsinline", "");
    keepAwakeVideo.setAttribute("aria-hidden", "true");
    keepAwakeVideo.style.position = "fixed";
    keepAwakeVideo.style.width = "1px";
    keepAwakeVideo.style.height = "1px";
    keepAwakeVideo.style.opacity = "0";
    keepAwakeVideo.style.pointerEvents = "none";
    keepAwakeVideo.style.left = "-10px";
    keepAwakeVideo.style.top = "-10px";
    keepAwakeVideo.srcObject = stream;
    document.body.appendChild(keepAwakeVideo);
    await keepAwakeVideo.play().catch(() => {});
  } catch {
    if (keepAwakeTimer) clearInterval(keepAwakeTimer);
    keepAwakeTimer = null;
    keepAwakeVideo = null;
    keepAwakeCanvas = null;
  }
  if (!keepAwakeRetryTimer) {
    keepAwakeRetryTimer = setInterval(() => {
      if (wakeLockWanted && document.visibilityState === "visible") {
        void requestWakeLock();
        void startKeepAwakeFallback();
      }
    }, 30000);
  }
}

async function publishClientActive() {
  if (startPanel.classList.contains("hidden") && document.visibilityState === "visible") {
    await fetch(endpoint("/screen-light/client-active"), { method: "POST", cache: "no-store" }).catch(() => {});
  }
}

function startClientActiveHeartbeat() {
  void publishClientActive();
  if (clientActiveTimer) return;
  clientActiveTimer = setInterval(() => {
    void publishClientActive();
  }, 1000);
}

async function startFullScreen() {
  fullscreenWanted = true;
  collapseMobileBrowserChrome();
  startPanel.classList.add("hidden");
  startClientActiveHeartbeat();
  showStatusBriefly(1800);
  await requestScreenOrientationLock();
  await requestFullscreenMode();
  await requestWakeLock();
  await startKeepAwakeFallback();
  updateFullscreenRestorePrompt();
}

function restoreFullscreenFromUserGesture(event) {
  event.preventDefault();
  event.stopPropagation();
  fullscreenWanted = true;
  lastTouchRestoreAt = Date.now();
  collapseMobileBrowserChrome();
  void requestFullscreenMode().then(() => {
    updateFullscreenRestorePrompt();
    void requestWakeLock();
    void startKeepAwakeFallback();
  });
}

function showStatusBriefly(duration = 2200) {
  statusPanel.classList.add("visible");
  clearTimeout(statusHideTimer);
  statusHideTimer = setTimeout(() => {
    if (startPanel.classList.contains("hidden") && !lightState.preparation?.active) {
      statusPanel.classList.remove("visible");
    }
  }, duration);
}

async function fetchState() {
  try {
    const response = await fetch(endpoint("/screen-light/state"), { cache: "no-store" });
    if (!response.ok) return;
    const next = await response.json();
    lightState = { ...lightState, ...next };
    applyLanguage(lightState.language);
    const preparing = Boolean(lightState.preparation?.active);
    const remainingSeconds = Math.max(0, Math.ceil(Number(lightState.preparation?.remainingSeconds) || 0));
    sessionTitle.textContent = lightState.sessionTitle || lightState.product || "NeuroSync Pro";
    phaseName.textContent = lightState.phase || (lightState.active ? "Live" : "Stand-by");
    frequencyValue.textContent = preparing
      ? `${remainingSeconds}s`
      : lightState.lightCodeActive
        ? `${lightState.lightCodeType === "spectrastrobe" ? "SpectraStrobe" : "AudioStrobe"} · ${Number(lightState.frequencyHz || 0).toFixed(1)} Hz`
        : `${Number(lightState.frequencyHz || 0).toFixed(1)} Hz`;
    preparationPanel.hidden = !preparing;
    if (preparing) {
      preparationSeconds.textContent = String(remainingSeconds);
      statusPanel.classList.add("visible");
    }
    if (sessionWantsFullscreen() && startPanel.classList.contains("hidden") && !fullscreenElement()) {
      updateFullscreenRestorePrompt();
      if (!IS_IOS) {
        void requestFullscreenMode().then(() => updateFullscreenRestorePrompt());
        scheduleFullscreenRetry();
      }
    }
    root.style.setProperty("--light-color", /^#[0-9a-f]{6}$/i.test(lightState.color) ? lightState.color : "#40c9ff");
  } catch {
    // Keep the last known state while the desktop app reconnects.
  }
}

function render() {
  const frequency = clamp(lightState.frequencyHz, 0.1, 40);
  const preparing = Boolean(lightState.preparation?.active);
  const now = performance.now();
  const frameSeconds = clamp((now - oscillator.lastFrameMs) / 1000, 0, 0.12);
  oscillator.lastFrameMs = now;
  const intensity = lightState.active && !lightState.blackout && !lightState.paused && !preparing
    ? clamp(lightState.intensity, 0, 1000) / 100
    : 0;
  const pulseEpochMs = Number(lightState.pulseEpochMs) || 0;
  const epochChanged = Math.abs(pulseEpochMs - oscillator.lastPulseEpochMs) > 750;
  const pulseModeChanged = lightState.pulseMode !== oscillator.lastPulseMode;
  if (intensity <= 0) {
    oscillator.lastActive = false;
    oscillator.lastFrequencyHz = frequency;
    oscillator.lastPulseMode = lightState.pulseMode;
    oscillator.lastPulseEpochMs = pulseEpochMs;
  } else if (!oscillator.lastActive || epochChanged || pulseModeChanged) {
    syncOscillatorToState(frequency);
    oscillator.lastActive = true;
  } else {
    oscillator.phase = (oscillator.phase + frequency * frameSeconds) % 1;
    oscillator.lastFrequencyHz = frequency;
    oscillator.lastPulseMode = lightState.pulseMode;
    oscillator.lastPulseEpochMs = pulseEpochMs;
  }
  const position = oscillator.phase;
  const multiplier = lightState.pulseMode === "breathe"
    ? 0.05 + ((Math.sin(position * Math.PI * 2 - Math.PI / 2) + 1) / 2) * 0.95
    : pulseMultiplier(position, lightState.pulseShape);
  const floorAlpha = lightState.active && !lightState.blackout && !lightState.paused && !preparing
    ? clamp(lightState.lightCodeFloorAlpha, 0, 0.3)
    : 0;
  const pulsedAlpha = lightState.lightCodeDirect
    ? intensity
    : intensity * (lightState.ganzfeld ? 0.88 : multiplier);
  const alpha = Math.max(floorAlpha, pulsedAlpha);
  root.style.setProperty("--light-alpha", String(clamp(alpha, 0, 1)));
  requestAnimationFrame(render);
}

startButton.addEventListener("click", () => void startFullScreen());
fullscreenRestoreButton.addEventListener("pointerdown", restoreFullscreenFromUserGesture);
fullscreenRestoreButton.addEventListener("touchend", (event) => {
  if (Date.now() - lastTouchRestoreAt < 600) return;
  restoreFullscreenFromUserGesture(event);
});
fullscreenRestoreButton.addEventListener("click", (event) => {
  if (Date.now() - lastTouchRestoreAt < 600) {
    event.preventDefault();
    return;
  }
  restoreFullscreenFromUserGesture(event);
});
document.addEventListener("pointerdown", (event) => {
  if (!startPanel.classList.contains("hidden")) return;
  if (event.target === fullscreenRestoreButton) return;
  if (sessionWantsFullscreen() && !fullscreenElement()) {
    collapseMobileBrowserChrome();
    if (!IS_IOS) {
      void requestFullscreenMode().then(() => updateFullscreenRestorePrompt());
    }
  }
  showStatusBriefly();
});
document.addEventListener("visibilitychange", () => {
  if (document.visibilityState === "visible" && wakeLockWanted) {
    if (!wakeLock) void requestWakeLock();
    void startKeepAwakeFallback();
  }
  if (document.visibilityState === "visible" && sessionWantsFullscreen() && !fullscreenElement()) {
    scheduleFullscreenRetry();
  }
  updateFullscreenRestorePrompt();
});
document.addEventListener("fullscreenchange", () => {
  if (fullscreenElement() && wakeLockWanted) {
    if (!wakeLock) void requestWakeLock();
    void startKeepAwakeFallback();
  }
  updateFullscreenRestorePrompt();
});
document.addEventListener("webkitfullscreenchange", () => {
  if (fullscreenElement() && wakeLockWanted) {
    if (!wakeLock) void requestWakeLock();
    void startKeepAwakeFallback();
  }
  updateFullscreenRestorePrompt();
});
window.addEventListener("orientationchange", restoreFullscreenAfterViewportChange);
window.addEventListener("resize", restoreFullscreenAfterViewportChange);
window.visualViewport?.addEventListener?.("resize", () => {
  updateViewportHeight();
  restoreFullscreenAfterViewportChange();
});
screen.orientation?.addEventListener?.("change", restoreFullscreenAfterViewportChange);

applyLanguage(lightState.language);
updateViewportHeight();
void fetchState();
setInterval(fetchState, SCREEN_LIGHT_STATE_POLL_MS);
requestAnimationFrame(render);
