import { useRef, useEffect } from 'react';
import { useStore } from '@nanostores/react';
import { $appSettings } from '../stores/appSettings';
import { $shader } from '../stores/shader';
import { uiState, showPerformanceWarning } from '../stores/ui';
import {
  $input,
  updateMousePosition,
  updateTouchPosition,
  updateDeviceMotion,
} from '../stores/input';
import { FakeShader } from '../FakeShader';
import { UI_HEIGHTS } from '../utils/constants';

export function ShaderCanvas() {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const shaderRef = useRef<FakeShader | null>(null);
  const settings = useStore($appSettings);
  const shader = useStore($shader);
  const ui = useStore(uiState);
  const input = useStore($input);

  // Mouse tracking state
  const mouseState = useRef({
    lastX: 0,
    lastY: 0,
    startTime: Date.now(),
  });

  // Touch gesture state
  const touchState = useRef({
    initialPinchDistance: 0,
    initialPinchAngle: 0,
  });

  useEffect(() => {
    if (!canvasRef.current) return;

    // Initialize shader
    shaderRef.current = new FakeShader(canvasRef.current, shader.code);

    // Set up canvas size
    setupCanvas();

    // Clean up on unmount
    return () => {
      if (shaderRef.current) {
        shaderRef.current.destroy();
      }
    };
  }, []);

  // Update shader when code changes
  useEffect(() => {
    if (shaderRef.current) {
      shaderRef.current.setCode(shader.code);
    }
  }, [shader.code]);

  // Update shader settings
  useEffect(() => {
    if (shaderRef.current) {
      shaderRef.current.setRenderMode(settings.renderMode);
      shaderRef.current.setValueMode(settings.valueMode ?? 'integer');
      shaderRef.current.setHueShift(settings.hueShift ?? 0);
      shaderRef.current.setTimeSpeed(settings.timeSpeed ?? 1.0);
      shaderRef.current.setBPM(settings.currentBPM ?? 120);
      shaderRef.current.setTargetFPS(settings.fps);
    }
  }, [settings.renderMode, settings.valueMode, settings.hueShift, settings.timeSpeed, settings.currentBPM, settings.fps]);

  // Handle canvas resize when resolution or UI visibility changes
  useEffect(() => {
    setupCanvas();
  }, [settings.resolution, ui.uiVisible]);

  // Handle animation
  useEffect(() => {
    if (shaderRef.current) {
      shaderRef.current.startAnimation();
    }
  }, [shader.code]);

  // Update input data to shader
  useEffect(() => {
    if (shaderRef.current) {
      shaderRef.current.setMousePosition(
        input.mouseX,
        input.mouseY,
        input.mousePressed,
        input.mouseVX,
        input.mouseVY,
        input.mouseClickTime
      );
      shaderRef.current.setTouchPosition(
        input.touchCount,
        input.touch0X,
        input.touch0Y,
        input.touch1X,
        input.touch1Y,
        input.pinchScale,
        input.pinchRotation
      );
      shaderRef.current.setDeviceMotion(
        input.accelX,
        input.accelY,
        input.accelZ,
        input.gyroX,
        input.gyroY,
        input.gyroZ
      );
      shaderRef.current.setAudioData(
        input.audioLevel,
        input.bassLevel,
        input.midLevel,
        input.trebleLevel
      );
    }
  }, [input]);

  const setupCanvas = () => {
    if (!canvasRef.current) return;

    const width = window.innerWidth;
    const height = ui.uiVisible
      ? window.innerHeight - UI_HEIGHTS.TOTAL_UI_HEIGHT
      : window.innerHeight;

    const scale = settings.resolution;

    // Set canvas internal size with resolution scaling
    canvasRef.current.width = Math.floor(width / scale);
    canvasRef.current.height = Math.floor(height / scale);

    console.log(
      `Canvas setup: ${canvasRef.current.width}x${canvasRef.current.height} (scale: ${scale}x), UI visible: ${ui.uiVisible}`
    );
  };

  const handleMouseMove = (e: React.MouseEvent) => {
    const lastX = mouseState.current.lastX;
    const lastY = mouseState.current.lastY;
    const x = e.clientX / window.innerWidth;
    const y = 1.0 - e.clientY / window.innerHeight; // Invert Y to match shader coordinates
    const vx = x - lastX;
    const vy = y - lastY;

    mouseState.current.lastX = x;
    mouseState.current.lastY = y;

    updateMousePosition(x, y, input.mousePressed, vx, vy, input.mouseClickTime);
  };

  const handleMouseDown = () => {
    const clickTime = Date.now();
    updateMousePosition(
      input.mouseX,
      input.mouseY,
      true,
      input.mouseVX,
      input.mouseVY,
      clickTime
    );
  };

  const handleMouseUp = () => {
    updateMousePosition(
      input.mouseX,
      input.mouseY,
      false,
      input.mouseVX,
      input.mouseVY,
      input.mouseClickTime
    );
  };

  const handleTouchStart = (e: React.TouchEvent) => {
    // Only prevent default on canvas area for shader interaction
    e.preventDefault();

    updateTouchPositions(e.touches);
    initializePinchGesture(e.touches);
  };

  const handleTouchMove = (e: React.TouchEvent) => {
    e.preventDefault();

    updateTouchPositions(e.touches);
    updatePinchGesture(e.touches);
  };

  const handleTouchEnd = (e: React.TouchEvent) => {
    e.preventDefault();

    const touchCount = e.touches.length;
    if (touchCount === 0) {
      updateTouchPosition(0, 0, 0, 0, 0, 1, 0);
    } else {
      updateTouchPositions(e.touches);
    }
  };

  const updateTouchPositions = (touches: React.TouchList | TouchList) => {
    let touch0X = 0,
      touch0Y = 0,
      touch1X = 0,
      touch1Y = 0;

    if (touches.length > 0) {
      touch0X = touches[0].clientX / window.innerWidth;
      touch0Y = 1.0 - touches[0].clientY / window.innerHeight;
    }
    if (touches.length > 1) {
      touch1X = touches[1].clientX / window.innerWidth;
      touch1Y = 1.0 - touches[1].clientY / window.innerHeight;
    }

    updateTouchPosition(
      touches.length,
      touch0X,
      touch0Y,
      touch1X,
      touch1Y,
      input.pinchScale,
      input.pinchRotation
    );
  };

  const initializePinchGesture = (touches: React.TouchList | TouchList) => {
    if (touches.length === 2) {
      const dx = touches[1].clientX - touches[0].clientX;
      const dy = touches[1].clientY - touches[0].clientY;
      touchState.current.initialPinchDistance = Math.sqrt(dx * dx + dy * dy);
      touchState.current.initialPinchAngle = Math.atan2(dy, dx);
    }
  };

  const updatePinchGesture = (touches: React.TouchList | TouchList) => {
    if (touches.length === 2 && touchState.current.initialPinchDistance > 0) {
      const dx = touches[1].clientX - touches[0].clientX;
      const dy = touches[1].clientY - touches[0].clientY;
      const distance = Math.sqrt(dx * dx + dy * dy);
      const angle = Math.atan2(dy, dx);

      const pinchScale = distance / touchState.current.initialPinchDistance;
      const pinchRotation = angle - touchState.current.initialPinchAngle;

      updateTouchPosition(
        touches.length,
        input.touch0X,
        input.touch0Y,
        input.touch1X,
        input.touch1Y,
        pinchScale,
        pinchRotation
      );
    }
  };

  // Set up device motion listener
  useEffect(() => {
    const handleDeviceMotion = (e: DeviceMotionEvent) => {
      if (e.acceleration && e.rotationRate) {
        updateDeviceMotion(
          e.acceleration.x || 0,
          e.acceleration.y || 0,
          e.acceleration.z || 0,
          e.rotationRate.alpha || 0,
          e.rotationRate.beta || 0,
          e.rotationRate.gamma || 0
        );
      }
    };

    if (window.DeviceMotionEvent) {
      window.addEventListener('devicemotion', handleDeviceMotion);
      return () =>
        window.removeEventListener('devicemotion', handleDeviceMotion);
    }
  }, []);

  // Set up performance warning listener
  useEffect(() => {
    const handlePerformanceWarning = (e: MessageEvent) => {
      if (e.data === 'performance-warning') {
        showPerformanceWarning();
      }
    };

    window.addEventListener('message', handlePerformanceWarning);
    return () =>
      window.removeEventListener('message', handlePerformanceWarning);
  }, []);

  // Set up window resize listener
  useEffect(() => {
    const handleResize = () => setupCanvas();
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, [settings.resolution, ui.uiVisible]);

  return (
    <canvas
      ref={canvasRef}
      id="canvas"
      style={{
        position: 'fixed',
        top: 0,
        left: 0,
        width: '100vw',
        height: '100vh',
        imageRendering: 'pixelated',
        touchAction: 'none',
        pointerEvents: 'auto',
      }}
      onMouseMove={handleMouseMove}
      onMouseDown={handleMouseDown}
      onMouseUp={handleMouseUp}
      onTouchStart={handleTouchStart}
      onTouchMove={handleTouchMove}
      onTouchEnd={handleTouchEnd}
    />
  );
}
