"use client";

import { useEffect, useRef } from "react";

import {
  createVanishingPoint,
  projectFlightStar,
} from "@/features/flight-starfield/lib/projection";
import {
  createFlightStar,
  createFlightVanishXJitter,
  getFlightStarCount,
  shouldRecycleFlightStar,
} from "@/features/flight-starfield/model/starfieldModel";
import { FlightStar } from "@/features/flight-starfield/model/types";
import { clamp } from "@/shared/lib/math/number";
import { getPrefersReducedMotionMediaQuery } from "@/shared/lib/motion/prefersReducedMotion";

export function FlightStarfieldCanvas({
  vanishYOffset = -68,
  centerProtectRadius = 200,
  isPaused = false,
}: {
  vanishYOffset?: number;
  centerProtectRadius?: number;
  isPaused?: boolean;
}) {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const starsRef = useRef<FlightStar[]>([]);
  const vanishXJitterRef = useRef<number | null>(null);

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

    const context = canvas.getContext("2d");
    if (!context) return;

    let width = window.innerWidth;
    let height = window.innerHeight;
    let animationFrameId = 0;

    const motionQuery = getPrefersReducedMotionMediaQuery();
    let prefersReducedMotion = motionQuery.matches;
    if (vanishXJitterRef.current === null) {
      vanishXJitterRef.current = createFlightVanishXJitter();
    }
    const vanishXJitter = vanishXJitterRef.current ?? 0;

    const setCanvasSize = () => {
      width = window.innerWidth;
      height = window.innerHeight;
      canvas.width = width;
      canvas.height = height;
    };

    const getVanishingPoint = () =>
      createVanishingPoint({
        width,
        height,
        xJitter: vanishXJitter,
        yOffset: vanishYOffset,
      });

    const createStars = () =>
      Array.from({ length: getFlightStarCount(width, height) }, () =>
        createFlightStar({ width, height, isRespawn: false }),
      );

    setCanvasSize();
    if (starsRef.current.length === 0) {
      starsRef.current = createStars();
    }
    let stars = starsRef.current;

    const applyCenterProtection = (x: number, y: number, alpha: number) => {
      const centerX = width / 2;
      const centerY = height / 2;
      const distance = Math.hypot(x - centerX, y - centerY);

      if (distance >= centerProtectRadius) return alpha;

      const ratio = clamp(distance / centerProtectRadius, 0, 1);
      const attenuation = 0.35 + ratio * 0.65;
      return alpha * attenuation;
    };

    const drawStar = ({
      star,
      fromX,
      fromY,
      toX,
      toY,
    }: {
      star: FlightStar;
      fromX: number;
      fromY: number;
      toX: number;
      toY: number;
    }) => {
      const visibleAlpha = applyCenterProtection(toX, toY, star.alpha);
      const deltaX = toX - fromX;
      const deltaY = toY - fromY;
      const movementLength = Math.hypot(deltaX, deltaY);

      if (star.tailLength < 1 || movementLength < 0.001) {
        context.globalAlpha = visibleAlpha;
        context.fillStyle = "#f8fbff";
        context.beginPath();
        context.arc(toX, toY, star.radius, 0, Math.PI * 2);
        context.fill();
        context.globalAlpha = 1;
        return;
      }

      const directionX = deltaX / movementLength;
      const directionY = deltaY / movementLength;
      const tailX = toX - directionX * star.tailLength;
      const tailY = toY - directionY * star.tailLength;

      const gradient = context.createLinearGradient(tailX, tailY, toX, toY);
      gradient.addColorStop(0, "rgba(248, 251, 255, 0)");
      gradient.addColorStop(1, `rgba(248, 251, 255, ${visibleAlpha})`);

      context.strokeStyle = gradient;
      context.lineWidth = clamp(star.radius * 0.9, 0.65, 1.6);
      context.beginPath();
      context.moveTo(tailX, tailY);
      context.lineTo(toX, toY);
      context.stroke();

      context.globalAlpha = Math.min(1, visibleAlpha + 0.08);
      context.fillStyle = "#f8fbff";
      context.beginPath();
      context.arc(
        toX,
        toY,
        clamp(star.radius * 0.72, 0.6, 1.45),
        0,
        Math.PI * 2,
      );
      context.fill();
      context.globalAlpha = 1;
    };

    const drawCenterVeil = () => {
      const vp = getVanishingPoint();
      const veil = context.createRadialGradient(
        vp.x,
        vp.y,
        centerProtectRadius * 0.18,
        vp.x,
        vp.y,
        centerProtectRadius * 1.35,
      );
      veil.addColorStop(0, "rgba(160, 185, 235, 0.08)");
      veil.addColorStop(0.55, "rgba(90, 114, 170, 0.03)");
      veil.addColorStop(1, "rgba(0, 0, 0, 0)");

      context.fillStyle = veil;
      context.fillRect(0, 0, width, height);
    };

    const drawVignette = () => {
      const vignette = context.createRadialGradient(
        width / 2,
        height / 2,
        Math.min(width, height) * 0.25,
        width / 2,
        height / 2,
        Math.max(width, height) * 0.95,
      );
      vignette.addColorStop(0, "rgba(0, 0, 0, 0)");
      vignette.addColorStop(1, "rgba(0, 0, 0, 0.82)");
      context.fillStyle = vignette;
      context.fillRect(0, 0, width, height);
    };

    const drawFrame = (moveStars: boolean) => {
      context.fillStyle = "rgba(2, 5, 10, 0.3)";
      context.fillRect(0, 0, width, height);

      drawCenterVeil();

      stars.forEach((star, index) => {
        const vp = getVanishingPoint();
        const from = projectFlightStar(star, vp, star.z);

        if (moveStars) {
          star.z -= star.speed;
        }

        const to = projectFlightStar(star, vp, star.z);

        if (
          shouldRecycleFlightStar({
            x: to.x,
            y: to.y,
            z: star.z,
            width,
            height,
          })
        ) {
          stars[index] = createFlightStar({ width, height, isRespawn: true });
          starsRef.current = stars;
          return;
        }

        drawStar({
          star,
          fromX: from.x,
          fromY: from.y,
          toX: to.x,
          toY: to.y,
        });
      });

      drawVignette();
    };

    const stopAnimation = () => {
      if (!animationFrameId) return;
      cancelAnimationFrame(animationFrameId);
      animationFrameId = 0;
    };

    const render = () => {
      if (prefersReducedMotion || isPaused) {
        animationFrameId = 0;
        return;
      }

      drawFrame(true);
      animationFrameId = requestAnimationFrame(render);
    };

    const renderStatic = () => {
      context.clearRect(0, 0, width, height);
      drawFrame(false);
    };

    const handleResize = () => {
      setCanvasSize();
      stars = createStars();
      starsRef.current = stars;

      if (prefersReducedMotion || isPaused) {
        renderStatic();
      }
    };

    const handleMotionChange = (event: MediaQueryListEvent) => {
      prefersReducedMotion = event.matches;

      if (prefersReducedMotion) {
        stopAnimation();
        renderStatic();
        return;
      }

      if (!isPaused && !animationFrameId) {
        render();
      }
    };

    window.addEventListener("resize", handleResize);
    motionQuery.addEventListener("change", handleMotionChange);

    if (prefersReducedMotion || isPaused) {
      renderStatic();
    } else {
      render();
    }

    return () => {
      window.removeEventListener("resize", handleResize);
      motionQuery.removeEventListener("change", handleMotionChange);
      stopAnimation();
    };
  }, [vanishYOffset, centerProtectRadius, isPaused]);

  return (
    <canvas
      ref={canvasRef}
      className="fixed inset-0 z-0 bg-black pointer-events-none"
    />
  );
}
