"use client";

import React, { useRef, useEffect, useState, useCallback } from "react";

// Версия движка, подставляется query-параметром при загрузке воркера и WASM
// модуля (?v=...) — простой cache-busting при выкладке новой сборки
// matrix-engine, чтобы браузер/CDN не отдавали закэшированный старый файл.
const ENGINE_VERSION = "3.1.5";

export interface NetrunnerMatrixProps {
  isSecure?: boolean;
  assetsPath?: string;
  isDarkMode?: boolean;
  showEyes?: boolean;
}

/**
 * React-обёртка над Matrix-движком. Сама ничего не рисует и не считает —
 * только создаёт <canvas>, один раз передаёт владение им в Web Worker через
 * transferControlToOffscreen() и дальше общается с воркером message-протоколом
 * (см. типы сообщений ниже). Вся физика (WASM) и рендер (WebGL2) живут в
 * воркере, чтобы тяжёлый цикл tick()+draw() не блокировал main thread и не
 * подвешивал остальной интерфейс страницы.
 *
 * Message-протокол Main thread -> Worker:
 *  - INIT         — единственный раз при монтировании: передаёт OffscreenCanvas
 *                    и стартовые параметры (размеры, DPR, тема, VPN-статус, mobile).
 *  - PAUSE/RESUME — по visibilitychange, чтобы не рендерить и не тратить батарею
 *                    во вкладке в фоне.
 *  - RESIZE       — по ResizeObserver контейнера, пересчитывает канвас и пересоздаёт капли.
 *  - THEME        — смена VPN-статуса и/или тёмной темы (перекрашивает атлас символов).
 *  - TILT         — deviceorientation.gamma (наклон телефона) для лёгкого 3D-параллакса.
 *  - SHOCKWAVE/DRAW_START/DRAW_END/KINETIC_SWIPE/MOUSE_MOVE — интеракции указателя/тача.
 *
 * Worker -> Main thread:
 *  - READY — WASM-модуль загружен и первый кадр посчитан, можно проявлять канвас (fade-in).
 */

export const NetrunnerMatrix: React.FC<NetrunnerMatrixProps> = ({
  isSecure = false,
  assetsPath = "/wasm-matrix",
  isDarkMode = true,
  showEyes = true,
}) => {
  const containerRef = useRef<HTMLDivElement>(null);
  const workerRef = useRef<Worker | null>(null);
  // canvas.transferControlToOffscreen() можно вызвать РОВНО ОДИН РАЗ за всё
  // время жизни конкретного <canvas> DOM-узла — повторный вызов бросает
  // исключение. Этот флаг защищает эффект инициализации от повторного запуска
  // (например, из-за React StrictMode двойного вызова эффектов в dev-режиме
  // или гонки при быстром ре-монтировании).
  const hasTransferred = useRef(false);
  const [mounted, setMounted] = useState(false);

  useEffect(() => {
    setMounted(true);
    return () => setMounted(false);
  }, []);

  const checkIsMobile = useCallback(() => {
    if (typeof window === "undefined") return false;
    // Убираем проверку на тач, оставляем только ширину
    return window.innerWidth <= 768;
  }, []);

  const getEyeY = useCallback(() => {
    const anchor = document.getElementById("eye-anchor");
    const container = containerRef.current;
    if (anchor && container) {
      const ar = anchor.getBoundingClientRect();
      const cr = container.getBoundingClientRect();
      return ar.top - cr.top + ar.height / 2;
    }
    return typeof window !== "undefined" ? window.innerHeight * 0.4 : 300;
  }, []);

  // === 1. ИНИЦИАЛИЗАЦИЯ И УПРАВЛЕНИЕ ВОРКЕРОМ ===
  // Создаёт canvas, передаёт его в Worker сообщением INIT, подписывается на
  // visibilitychange (PAUSE/RESUME) и ResizeObserver (RESIZE). Выполняется
  // один раз при монтировании (условия внутри — раннние return, а не смена
  // зависимостей эффекта на каждый рендер).
  useEffect(() => {
    if (
      !mounted ||
      typeof window === "undefined" ||
      !("OffscreenCanvas" in window)
    )
      return;

    const container = containerRef.current;
    if (!container || hasTransferred.current) return;

    const canvas = document.createElement("canvas");
    Object.assign(canvas.style, {
      position: "absolute",
      top: "0",
      left: "0",
      width: "100%",
      height: "100%",
      display: "block",
      zIndex: "0",
      pointerEvents: "none",
      transition: "opacity 1.5s ease-in-out",
      opacity: "0",
    });

    canvas.id = "matrix-canvas";
    container.insertBefore(canvas, container.firstChild);

    try {
      const isMobile = checkIsMobile();
      const isLowQuality =
        isMobile || (navigator.hardwareConcurrency || 4) <= 4;
      const currentDPR = window.devicePixelRatio || 1;
      const optimalScale = isMobile ? Math.min(currentDPR, 1.25) : currentDPR;
      const logicalWidth = container.clientWidth;
      const logicalHeight = container.clientHeight;

      const workerUrl = new URL(
        `${assetsPath}/matrix_worker.js?v=${ENGINE_VERSION}`,
        window.location.origin,
      ).href;

      const worker = new Worker(workerUrl, { type: "module" });
      workerRef.current = worker;

      const offscreen = canvas.transferControlToOffscreen();
      hasTransferred.current = true;

      worker.postMessage(
        {
          type: "INIT",
          payload: {
            canvas: offscreen,
            assetsPath,
            logicalWidth,
            logicalHeight,
            pixelScale: optimalScale,
            eyeY: getEyeY(),
            isMobile,
            isVpnOn: isSecure,
            isDarkMode,
            fontSize: isMobile ? 12 : 16,
            isLowQuality,
            showEyes,
          },
        },
        [offscreen],
      );

      worker.onmessage = (e) => {
        if (e.data.type === "READY") {
          requestAnimationFrame(() => {
            canvas.style.opacity = "1";
          });
        }
      };

      const onVisibilityChange = () => {
        if (!workerRef.current) return;
        if (document.hidden) {
          workerRef.current.postMessage({ type: "PAUSE" });
        } else {
          workerRef.current.postMessage({ type: "RESUME" });
        }
      };
      document.addEventListener("visibilitychange", onVisibilityChange);

      const resizeObserver = new ResizeObserver((entries) => {
        for (let entry of entries) {
          const { width: newWidth, height: newHeight } = entry.contentRect;
          if (newWidth === 0 || newHeight === 0) continue;

          // ИСПРАВЛЕНО: Вычисляем DPR и масштаб ВНУТРИ ResizeObserver
          if (typeof window === "undefined") continue;
          const currentDPR = window.devicePixelRatio || 1;
          const isMobileNow = newWidth <= 768;
          const optimalScale = isMobileNow
            ? Math.min(currentDPR, 1.25)
            : currentDPR;

          workerRef.current?.postMessage({
            type: "RESIZE",
            payload: {
              logicalWidth: newWidth,
              logicalHeight: newHeight,
              pixelScale: optimalScale, // Передаем СВЕЖЕЕ значение!
              assetsPath,
              eyeY: getEyeY(),
              isMobile: isMobileNow,
              fontSize: isMobileNow ? 12 : 16,
              isLowQuality: isMobileNow || isLowQuality,
            },
          });
        }
      });
      resizeObserver.observe(container);

      return () => {
        resizeObserver.disconnect();
        document.removeEventListener("visibilitychange", onVisibilityChange);
        if (workerRef.current) {
          workerRef.current.terminate();
          workerRef.current = null;
        }
        if (container.contains(canvas)) container.removeChild(canvas);
        hasTransferred.current = false;
      };
    } catch (e) {
      console.error("Matrix WebGL Bridge Failed:", e);
    }
  }, [mounted, checkIsMobile, getEyeY, assetsPath, showEyes, isDarkMode]);

  // === 2. СИНХРОНИЗАЦИЯ ТЕМЫ И VPN ===
  // Отдельный эффект от инициализации: isSecure/isDarkMode меняются по ходу
  // жизни страницы (тумблер VPN, переключение темы), и каждый раз нужно
  // только отправить THEME в уже живой воркер, не пересоздавая canvas/worker.
  useEffect(() => {
    if (workerRef.current && mounted) {
      workerRef.current.postMessage({
        type: "THEME",
        payload: {
          isVpnOn: isSecure,
          isDarkMode: isDarkMode,
        },
      });
    }
  }, [isSecure, isDarkMode, mounted]);

  // === 3. ИНТЕРАКТИВНОСТЬ ===
  // Глобальные слушатели указателя/тача/гироскопа на window (не на canvas —
  // у канваса pointerEvents: none, чтобы не перехватывать клики по UI поверх
  // фона), с фильтром по closest("button, a, ...") чтобы не триггерить
  // шоквейвы при обычных кликах по интерфейсу.
  useEffect(() => {
    if (!mounted || !workerRef.current || !containerRef.current) return;

    const handleOrientation = (e: DeviceOrientationEvent) => {
      let gamma = e.gamma || 0;
      if (gamma > 45) gamma = 45;
      if (gamma < -45) gamma = -45;
      workerRef.current?.postMessage({
        type: "TILT",
        payload: { x: gamma / 45.0, y: 0 },
      });
    };

    window.addEventListener("deviceorientation", handleOrientation);

    const handleEvents = (e: any) => {
      if (
        e.target instanceof Element &&
        e.target.closest("button, a, input, textarea, label")
      )
        return;

      const isDown = e.type === "pointerdown" || e.type === "touchstart";
      const isMove = e.type === "pointermove" || e.type === "touchmove";

      const clientX = e.touches ? e.touches[0]?.clientX : e.clientX;
      const clientY = e.touches ? e.touches[0]?.clientY : e.clientY;

      if (clientX === undefined || clientY === undefined) return;

      const rect = containerRef.current!.getBoundingClientRect();
      const px = clientX - rect.left;
      const py = clientY - rect.top;

      if (isDown) {
        workerRef.current?.postMessage({
          type: "SHOCKWAVE",
          payload: { x: px, y: py },
        });
        workerRef.current?.postMessage({
          type: "DRAW_START",
          payload: { x: px, y: py },
        });
      }
      if (isMove) {
        workerRef.current?.postMessage({
          type: "KINETIC_SWIPE",
          payload: { x: px, y: py },
        });
        workerRef.current?.postMessage({
          type: "MOUSE_MOVE",
          payload: { x: px, y: py },
        });
      }
    };

    const handleStop = () =>
      workerRef.current?.postMessage({ type: "DRAW_END", payload: {} });

    const options = { passive: true };
    window.addEventListener("pointerdown", handleEvents, options);
    window.addEventListener("pointermove", handleEvents, options);
    window.addEventListener("pointerup", handleStop, options);
    window.addEventListener("touchstart", handleEvents, options);
    window.addEventListener("touchmove", handleEvents, options);
    window.addEventListener("touchend", handleStop, options);

    return () => {
      window.removeEventListener("deviceorientation", handleOrientation);
      window.removeEventListener("pointerdown", handleEvents);
      window.removeEventListener("pointermove", handleEvents);
      window.removeEventListener("pointerup", handleStop);
      window.removeEventListener("touchstart", handleEvents);
      window.removeEventListener("touchmove", handleEvents);
      window.removeEventListener("touchend", handleStop);
    };
  }, [mounted]);

  return (
    <div
      ref={containerRef}
      className="absolute inset-0 overflow-hidden bg-transparent pointer-events-none"
    ></div>
  );
};
