const { useCallback, useEffect, useRef, useState } = React;

const KIOSK_VIDEO_PATH = "assets/media/cosmic-call-attract-loop-v06.mp4";
const KIOSK_POSTER_PATH = "assets/media/cosmic-call-attract-poster-v06.jpg";
const KIOSK_POSITION_KEY = "cosmic-call-kiosk-video-position-v1";
const KIOSK_INPUT_GUARD_MS = 500;

function kioskDurationMs(parameter, fallbackSeconds, minimumSeconds) {
  const rawValue = new URLSearchParams(window.location.search).get(parameter);
  const value = rawValue === null || rawValue.trim() === "" ? NaN : Number(rawValue);
  const seconds = Number.isFinite(value)
    ? Math.min(3600, Math.max(minimumSeconds, value))
    : fallbackSeconds;
  return seconds * 1000;
}

const KIOSK_IDLE_RETURN_MS = kioskDurationMs("idle", 60, 3);
const KIOSK_RESET_GRACE_MS = kioskDurationMs("resetGrace", 3, 1);

function kioskModeEnabled() {
  return new URLSearchParams(window.location.search).get("kiosk") !== "0";
}

function isSafeIdleSession(state) {
  return Boolean(
    state
      && state.phase === "prep"
      && state.runState === "idle"
      && !state.runId
      && !state.safety?.pause?.latched
      && !state.safety?.estop?.latched
  );
}

function readSavedVideoPosition() {
  try {
    const value = Number(window.localStorage.getItem(KIOSK_POSITION_KEY));
    return Number.isFinite(value) && value >= 0 ? value : 0;
  } catch (_error) {
    return 0;
  }
}

function saveVideoPosition(value) {
  try {
    if (Number.isFinite(value) && value >= 0) {
      window.localStorage.setItem(KIOSK_POSITION_KEY, String(value));
    }
  } catch (_error) {
    // The kiosk still works when storage is unavailable.
  }
}

function KioskIdleExperience({ state, children }) {
  const enabled = useRef(kioskModeEnabled()).current;
  const [idleVisible, setIdleVisible] = useState(() => enabled && isSafeIdleSession(state));
  const [isExiting, setIsExiting] = useState(false);
  const [inputGuardActive, setInputGuardActive] = useState(false);
  const [videoState, setVideoState] = useState("loading");
  const videoRef = useRef(null);
  const latestStateRef = useRef(state);
  const idleVisibleRef = useRef(idleVisible);
  const exitingRef = useRef(false);
  const activePointersRef = useRef(new Set());
  const idleTimerRef = useRef(null);
  const wakeFallbackRef = useRef(null);
  const guardTimerRef = useRef(null);
  const stalledTimerRef = useRef(null);
  const lastPersistAtRef = useRef(0);
  const previousSessionRef = useRef({
    sessionId: state.sessionId,
    eligible: isSafeIdleSession(state),
  });

  latestStateRef.current = state;
  idleVisibleRef.current = idleVisible;

  const clearIdleTimer = useCallback(() => {
    if (idleTimerRef.current) window.clearTimeout(idleTimerRef.current);
    idleTimerRef.current = null;
  }, []);

  const scheduleIdle = useCallback((delay = KIOSK_IDLE_RETURN_MS) => {
    clearIdleTimer();
    if (!enabled || idleVisibleRef.current || !isSafeIdleSession(latestStateRef.current)) return;
    idleTimerRef.current = window.setTimeout(() => {
      idleTimerRef.current = null;
      if (!isSafeIdleSession(latestStateRef.current)) return;
      exitingRef.current = false;
      setIsExiting(false);
      setVideoState("loading");
      setIdleVisible(true);
    }, delay);
  }, [clearIdleTimer, enabled]);

  const clearWakeFallback = useCallback(() => {
    if (wakeFallbackRef.current) window.clearTimeout(wakeFallbackRef.current);
    wakeFallbackRef.current = null;
  }, []);

  const requestScreenWakeLock = useCallback(async () => {
    if (!navigator.wakeLock || document.visibilityState !== "visible") return;
    try {
      await navigator.wakeLock.request("screen");
    } catch (_error) {
      // Outdoor kiosk power policy remains the authoritative fallback.
    }
  }, []);

  const finishWake = useCallback(() => {
    if (!idleVisibleRef.current && !exitingRef.current) return;
    clearWakeFallback();
    activePointersRef.current.clear();
    exitingRef.current = false;
    setIsExiting(false);
    setIdleVisible(false);
    setInputGuardActive(true);
    if (guardTimerRef.current) window.clearTimeout(guardTimerRef.current);
    guardTimerRef.current = window.setTimeout(() => {
      guardTimerRef.current = null;
      setInputGuardActive(false);
    }, KIOSK_INPUT_GUARD_MS);
    requestScreenWakeLock();
    scheduleIdle(KIOSK_IDLE_RETURN_MS);
  }, [clearWakeFallback, requestScreenWakeLock, scheduleIdle]);

  const beginWake = useCallback(() => {
    if (!idleVisibleRef.current || exitingRef.current) return;
    exitingRef.current = true;
    setIsExiting(true);
    if (videoRef.current) {
      saveVideoPosition(videoRef.current.currentTime);
      videoRef.current.pause();
    }
    clearWakeFallback();
    wakeFallbackRef.current = window.setTimeout(finishWake, 900);
  }, [clearWakeFallback, finishWake]);

  const showIdleNow = useCallback(() => {
    if (!enabled || !isSafeIdleSession(latestStateRef.current)) return false;
    clearIdleTimer();
    clearWakeFallback();
    activePointersRef.current.clear();
    exitingRef.current = false;
    setIsExiting(false);
    setInputGuardActive(false);
    setVideoState("loading");
    setIdleVisible(true);
    return true;
  }, [clearIdleTimer, clearWakeFallback, enabled]);

  const handlePointerDown = useCallback((event) => {
    event.preventDefault();
    event.stopPropagation();
    activePointersRef.current.add(event.pointerId);
    if (event.currentTarget.setPointerCapture) {
      try {
        event.currentTarget.setPointerCapture(event.pointerId);
      } catch (_error) {
        // Pointer capture is an optimization, not a requirement.
      }
    }
    beginWake();
  }, [beginWake]);

  const handlePointerEnd = useCallback((event) => {
    event.preventDefault();
    event.stopPropagation();
    activePointersRef.current.delete(event.pointerId);
    if (activePointersRef.current.size === 0 && exitingRef.current) finishWake();
  }, [finishWake]);

  const handleOverlayClick = useCallback((event) => {
    event.preventDefault();
    event.stopPropagation();
  }, []);

  const handleOverlayKeyDown = useCallback((event) => {
    if (event.repeat || ["Shift", "Control", "Alt", "Meta", "CapsLock", "Tab"].includes(event.key)) return;
    event.preventDefault();
    event.stopPropagation();
    beginWake();
    window.setTimeout(finishWake, 120);
  }, [beginWake, finishWake]);

  const handleAppActivity = useCallback(() => {
    if (idleVisibleRef.current || !isSafeIdleSession(latestStateRef.current)) return;
    scheduleIdle(KIOSK_IDLE_RETURN_MS);
  }, [scheduleIdle]);

  const tryPlayVideo = useCallback(() => {
    const video = videoRef.current;
    if (!video || !idleVisibleRef.current || document.visibilityState !== "visible") return;
    video.muted = true;
    const playResult = video.play();
    if (playResult && typeof playResult.catch === "function") {
      playResult.catch(() => setVideoState("fallback"));
    }
  }, []);

  const restoreVideoPosition = useCallback(() => {
    const video = videoRef.current;
    if (!video || !Number.isFinite(video.duration) || video.duration <= 0) return;
    const saved = readSavedVideoPosition();
    video.currentTime = saved < video.duration - 1 ? saved : 0;
    tryPlayVideo();
  }, [tryPlayVideo]);

  const handleTimeUpdate = useCallback(() => {
    const video = videoRef.current;
    const now = Date.now();
    if (!video || now - lastPersistAtRef.current < 2000) return;
    lastPersistAtRef.current = now;
    saveVideoPosition(video.currentTime);
  }, []);

  const handleVideoStalled = useCallback(() => {
    if (stalledTimerRef.current) window.clearTimeout(stalledTimerRef.current);
    stalledTimerRef.current = window.setTimeout(() => {
      stalledTimerRef.current = null;
      if (idleVisibleRef.current) setVideoState("fallback");
    }, 5000);
  }, []);

  const handleVideoPlaying = useCallback(() => {
    if (stalledTimerRef.current) window.clearTimeout(stalledTimerRef.current);
    stalledTimerRef.current = null;
    setVideoState("ready");
  }, []);

  useEffect(() => {
    const eligible = isSafeIdleSession(state);
    const previous = previousSessionRef.current;

    if (!enabled) {
      clearIdleTimer();
      setIdleVisible(false);
    } else if (!eligible) {
      clearIdleTimer();
      clearWakeFallback();
      activePointersRef.current.clear();
      exitingRef.current = false;
      setIsExiting(false);
      setIdleVisible(false);
    } else if (!previous.eligible || previous.sessionId !== state.sessionId) {
      setIdleVisible(false);
      setIsExiting(false);
      exitingRef.current = false;
      scheduleIdle(KIOSK_RESET_GRACE_MS);
    } else if (!idleVisible && !isExiting) {
      scheduleIdle(KIOSK_IDLE_RETURN_MS);
    }

    previousSessionRef.current = { sessionId: state.sessionId, eligible };
  }, [
    clearIdleTimer,
    clearWakeFallback,
    enabled,
    idleVisible,
    isExiting,
    scheduleIdle,
    state.phase,
    state.runId,
    state.runState,
    state.sessionId,
    state.safety?.estop?.latched,
    state.safety?.pause?.latched,
  ]);

  useEffect(() => {
    if (!idleVisible) {
      if (videoRef.current) videoRef.current.pause();
      document.body.classList.remove("kiosk-idle-active");
      return undefined;
    }
    document.body.classList.add("kiosk-idle-active");
    const frame = window.requestAnimationFrame(tryPlayVideo);
    return () => {
      window.cancelAnimationFrame(frame);
      document.body.classList.remove("kiosk-idle-active");
    };
  }, [idleVisible, tryPlayVideo]);

  useEffect(() => {
    const handleVisibility = () => {
      if (document.visibilityState === "hidden") {
        if (videoRef.current) {
          saveVideoPosition(videoRef.current.currentTime);
          videoRef.current.pause();
        }
        return;
      }
      if (!isSafeIdleSession(latestStateRef.current)) {
        setIdleVisible(false);
        return;
      }
      if (idleVisibleRef.current) tryPlayVideo();
    };
    const handleExternalWake = () => {
      beginWake();
      window.setTimeout(finishWake, 120);
    };
    document.addEventListener("visibilitychange", handleVisibility);
    window.addEventListener("cosmic-call:kiosk-wake", handleExternalWake);
    return () => {
      document.removeEventListener("visibilitychange", handleVisibility);
      window.removeEventListener("cosmic-call:kiosk-wake", handleExternalWake);
    };
  }, [beginWake, finishWake, tryPlayVideo]);

  useEffect(() => {
    const kioskApi = {
      wake: () => {
        beginWake();
        window.setTimeout(finishWake, 120);
      },
      sleep: showIdleNow,
      activity: handleAppActivity,
      getState: () => ({
        enabled,
        idleVisible: idleVisibleRef.current,
        sessionEligible: isSafeIdleSession(latestStateRef.current),
      }),
    };
    window.CosmicCallKiosk = kioskApi;
    return () => {
      if (window.CosmicCallKiosk === kioskApi) delete window.CosmicCallKiosk;
    };
  }, [beginWake, enabled, finishWake, handleAppActivity, showIdleNow]);

  useEffect(() => () => {
    clearIdleTimer();
    clearWakeFallback();
    if (guardTimerRef.current) window.clearTimeout(guardTimerRef.current);
    if (stalledTimerRef.current) window.clearTimeout(stalledTimerRef.current);
  }, [clearIdleTimer, clearWakeFallback]);

  const covered = idleVisible && !isExiting;

  return (
    <React.Fragment>
      <div
        className={`kiosk-app-layer ${covered ? "is-covered" : ""}`}
        aria-hidden={covered ? "true" : undefined}
        onPointerDownCapture={handleAppActivity}
        onKeyDownCapture={handleAppActivity}
      >
        {children}
      </div>

      {idleVisible ? (
        <div
          className={`kiosk-idle-screen ${isExiting ? "is-exiting" : ""}`}
          data-screen-label="儿童屏·宣传视频待机"
          data-video-state={videoState}
          data-idle-return-ms={KIOSK_IDLE_RETURN_MS}
          data-reset-grace-ms={KIOSK_RESET_GRACE_MS}
          role="button"
          tabIndex="0"
          aria-label="宣传视频正在播放。轻触屏幕或按任意键进入互动观测。"
          onPointerDown={handlePointerDown}
          onPointerUp={handlePointerEnd}
          onPointerCancel={handlePointerEnd}
          onClick={handleOverlayClick}
          onKeyDown={handleOverlayKeyDown}
        >
          <img className="kiosk-idle-poster" src={KIOSK_POSTER_PATH} alt="" aria-hidden="true" />
          <video
            ref={videoRef}
            className="kiosk-idle-video"
            src={KIOSK_VIDEO_PATH}
            poster={KIOSK_POSTER_PATH}
            autoPlay
            loop
            muted
            playsInline
            preload="auto"
            aria-hidden="true"
            onLoadedMetadata={restoreVideoPosition}
            onCanPlay={tryPlayVideo}
            onPlaying={handleVideoPlaying}
            onTimeUpdate={handleTimeUpdate}
            onStalled={handleVideoStalled}
            onError={() => setVideoState("fallback")}
          ></video>
          <div className="kiosk-idle-shade" aria-hidden="true"></div>
          <div className="kiosk-wake-cue" aria-hidden="true">
            <span className="kiosk-touch-orbit"><i></i></span>
            <span className="kiosk-wake-copy">
              <strong>轻触屏幕</strong>
              <small>进入互动观测</small>
            </span>
          </div>
          {videoState === "fallback" ? (
            <div className="kiosk-video-fallback-note" aria-hidden="true">宣传片暂未载入 · 轻触仍可开始</div>
          ) : null}
        </div>
      ) : null}

      {inputGuardActive ? (
        <div
          className="kiosk-input-guard"
          aria-hidden="true"
          onPointerDown={handleOverlayClick}
          onPointerUp={handleOverlayClick}
          onClick={handleOverlayClick}
        ></div>
      ) : null}
    </React.Fragment>
  );
}

Object.assign(window, { KioskIdleExperience });
