"use client";

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

// Colors inspired by the original fireworks
const COLORS = {
  Red: "#ff0043",
  Green: "#14fc56",
  Blue: "#1e7fff",
  Purple: "#e60aff",
  Gold: "#ffbf36",
  White: "#ffffff",
};

const COLOR_NAMES = Object.keys(COLORS);
const COLOR_CODES = COLOR_NAMES.map(
  (colorName) => COLORS[colorName as keyof typeof COLORS]
);

// Special invisible color for transitions
const INVISIBLE = "_INVISIBLE_";
const COLOR_CODES_W_INVIS = [...COLOR_CODES, INVISIBLE];

// Constants for physics
const GRAVITY = 0.9;
const PI_2 = Math.PI * 2;
const PI_HALF = Math.PI * 0.5;

// Utility functions
const random = (min: number, max: number) => Math.random() * (max - min) + min;
const randomInt = (min: number, max: number) => Math.floor(random(min, max));
const randomColor = () => COLOR_CODES[randomInt(0, COLOR_CODES.length)];

// Create particle arc helper
const createParticleArc = (
  start: number,
  arcLength: number,
  count: number,
  randomness: number,
  particleFactory: (angle: number) => void
) => {
  const angleDelta = arcLength / count;
  const end = start + arcLength - angleDelta * 0.5;

  if (end > start) {
    for (let angle = start; angle < end; angle += angleDelta) {
      particleFactory(angle + Math.random() * angleDelta * randomness);
    }
  }
};

// Create spherical burst helper
const createBurst = (
  count: number,
  particleFactory: (angle: number, speedMult: number) => void,
  startAngle = 0,
  arcLength = PI_2
) => {
  const R = 0.5 * Math.sqrt(count / Math.PI);
  const C = 2 * R * Math.PI;
  const C_HALF = C / 2;

  for (let i = 0; i <= C_HALF; i++) {
    const ringAngle = (i / C_HALF) * PI_HALF;
    const ringSize = Math.cos(ringAngle);
    const partsPerFullRing = C * ringSize;
    const partsPerArc = partsPerFullRing * (arcLength / PI_2);

    const angleInc = PI_2 / partsPerFullRing;
    const angleOffset = Math.random() * angleInc + startAngle;
    const maxRandomAngleOffset = angleInc * 0.33;

    for (let j = 0; j < partsPerArc; j++) {
      const randomAngleOffset = Math.random() * maxRandomAngleOffset;
      const angle = angleInc * j + angleOffset + randomAngleOffset;
      particleFactory(angle, ringSize);
    }
  }
};

// Object pools for performance
const createParticleCollection = () => {
  const collection: { [key: string]: any[] } = {};
  COLOR_CODES_W_INVIS.forEach((color) => {
    collection[color] = [];
  });
  return collection;
};

// Particle classes
class Star {
  x: number;
  y: number;
  prevX: number;
  prevY: number;
  speedX: number;
  speedY: number;
  life: number;
  fullLife: number;
  color: string;
  sparkFreq: number;
  sparkTimer: number;
  sparkColor: string;
  sparkSpeed: number;
  sparkLife: number;
  sparkLifeVariation: number;
  visible: boolean;
  heavy: boolean;
  strobe: boolean;
  strobeFreq: number;
  transitionTime: number;
  secondColor?: string;
  colorChanged: boolean;
  spinAngle: number;
  spinSpeed: number;
  spinRadius: number;
  updateFrame: number;
  onDeath?: (star: Star) => void;

  static drawWidth = 2;
  static airDrag = 0.98;
  static airDragHeavy = 0.992;
  static active = createParticleCollection();
  static _pool: Star[] = [];

  constructor(
    x: number,
    y: number,
    color: string,
    angle: number,
    speed: number,
    life: number,
    speedOffX = 0,
    speedOffY = 0
  ) {
    this.x = x;
    this.y = y;
    this.prevX = x;
    this.prevY = y;
    this.speedX = Math.sin(angle) * speed + speedOffX;
    this.speedY = Math.cos(angle) * speed + speedOffY;
    this.life = life;
    this.fullLife = life;
    this.color = color;
    this.sparkFreq = 0;
    this.sparkTimer = 0;
    this.sparkColor = color;
    this.sparkSpeed = 1;
    this.sparkLife = 750;
    this.sparkLifeVariation = 0.25;
    this.visible = true;
    this.heavy = false;
    this.strobe = false;
    this.strobeFreq = 60;
    this.transitionTime = 0;
    this.colorChanged = false;
    this.spinAngle = Math.random() * PI_2;
    this.spinSpeed = 0.8;
    this.spinRadius = 0;
    this.updateFrame = 0;
  }

  static add(
    x: number,
    y: number,
    color: string,
    angle: number,
    speed: number,
    life: number,
    speedOffX = 0,
    speedOffY = 0
  ) {
    const instance =
      this._pool.pop() ||
      new Star(x, y, color, angle, speed, life, speedOffX, speedOffY);

    // Reset instance
    instance.x = x;
    instance.y = y;
    instance.prevX = x;
    instance.prevY = y;
    instance.color = color;
    instance.speedX = Math.sin(angle) * speed + speedOffX;
    instance.speedY = Math.cos(angle) * speed + speedOffY;
    instance.life = life;
    instance.fullLife = life;
    instance.visible = true;
    instance.heavy = false;
    instance.sparkFreq = 0;
    instance.sparkTimer = 0;
    instance.sparkColor = color;
    instance.sparkSpeed = 1;
    instance.sparkLife = 750;
    instance.sparkLifeVariation = 0.25;
    instance.strobe = false;
    instance.transitionTime = 0;
    instance.secondColor = undefined;
    instance.colorChanged = false;
    instance.spinAngle = Math.random() * PI_2;
    instance.spinRadius = 0;
    instance.onDeath = undefined;

    this.active[color].push(instance);
    return instance;
  }

  static returnInstance(instance: Star) {
    instance.onDeath?.(instance);
    instance.onDeath = undefined;
    instance.secondColor = undefined;
    this._pool.push(instance);
  }

  update(timeStep: number, sparks: Spark[]) {
    this.life -= timeStep;
    if (this.life <= 0) return false;

    const burnRate = (this.life / this.fullLife) ** 0.5;
    const burnRateInverse = 1 - burnRate;

    this.prevX = this.x;
    this.prevY = this.y;
    this.x += this.speedX * (timeStep / 16);
    this.y += this.speedY * (timeStep / 16);

    // Air drag
    const drag = this.heavy ? 0.992 : 0.98;
    this.speedX *= drag ** (timeStep / 16);
    this.speedY *= drag ** (timeStep / 16);
    this.speedY += (GRAVITY * timeStep) / 1000;

    // Spin effects for comets
    if (this.spinRadius > 0) {
      this.spinAngle += (this.spinSpeed * timeStep) / 16;
      this.x += (Math.sin(this.spinAngle) * this.spinRadius * timeStep) / 16;
      this.y += (Math.cos(this.spinAngle) * this.spinRadius * timeStep) / 16;
    }

    // Sparks
    if (this.sparkFreq > 0) {
      this.sparkTimer -= timeStep;
      while (this.sparkTimer < 0) {
        this.sparkTimer +=
          this.sparkFreq * 0.75 + this.sparkFreq * burnRateInverse * 4;
        sparks.push(
          new Spark(
            this.x,
            this.y,
            this.sparkColor,
            Math.random() * PI_2,
            Math.random() * this.sparkSpeed * burnRate,
            this.sparkLife * 0.8 +
              Math.random() * this.sparkLife * this.sparkLifeVariation
          )
        );
      }
    }

    // Handle transitions
    if (this.life < this.transitionTime) {
      if (this.secondColor && !this.colorChanged) {
        this.colorChanged = true;
        // Move star to new color array
        const currentArray = Star.active[this.color];
        const index = currentArray.indexOf(this);
        if (index > -1) {
          currentArray.splice(index, 1);
          this.color = this.secondColor;
          Star.active[this.secondColor].push(this);
          if (this.secondColor === INVISIBLE) {
            this.sparkFreq = 0;
          }
        }
      }

      if (this.strobe) {
        this.visible = Math.floor(this.life / this.strobeFreq) % 3 === 0;
      }
    }

    return true;
  }

  draw(ctx: CanvasRenderingContext2D) {
    if (!this.visible) return;

    ctx.strokeStyle = this.color;
    ctx.lineWidth = Star.drawWidth;
    ctx.lineCap = "round";
    ctx.beginPath();
    ctx.moveTo(this.x, this.y);
    ctx.lineTo(this.prevX, this.prevY);
    ctx.stroke();
  }
}

class Spark {
  x: number;
  y: number;
  prevX: number;
  prevY: number;
  speedX: number;
  speedY: number;
  life: number;
  fullLife: number;
  color: string;

  static drawWidth = 1;
  static airDrag = 0.9;
  static active = createParticleCollection();
  static _pool: Spark[] = [];

  constructor(
    x: number,
    y: number,
    color: string,
    angle: number,
    speed: number,
    life: number
  ) {
    this.x = x;
    this.y = y;
    this.prevX = x;
    this.prevY = y;
    this.speedX = Math.sin(angle) * speed;
    this.speedY = Math.cos(angle) * speed;
    this.life = life;
    this.fullLife = life;
    this.color = color;
  }

  static add(
    x: number,
    y: number,
    color: string,
    angle: number,
    speed: number,
    life: number
  ) {
    const instance =
      this._pool.pop() || new Spark(x, y, color, angle, speed, life);

    instance.x = x;
    instance.y = y;
    instance.prevX = x;
    instance.prevY = y;
    instance.speedX = Math.sin(angle) * speed;
    instance.speedY = Math.cos(angle) * speed;
    instance.life = life;
    instance.fullLife = life;
    instance.color = color;

    this.active[color].push(instance);
    return instance;
  }

  static returnInstance(instance: Spark) {
    this._pool.push(instance);
  }

  update(timeStep: number) {
    this.life -= timeStep;
    if (this.life <= 0) return false;

    this.prevX = this.x;
    this.prevY = this.y;
    this.x += this.speedX * (timeStep / 16);
    this.y += this.speedY * (timeStep / 16);

    this.speedX *= 0.9 ** (timeStep / 16);
    this.speedY *= 0.9 ** (timeStep / 16);
    this.speedY += (GRAVITY * timeStep) / 1000;

    return true;
  }

  draw(ctx: CanvasRenderingContext2D) {
    const alpha = this.life / this.fullLife;
    ctx.globalAlpha = alpha;
    ctx.strokeStyle = this.color;
    ctx.lineWidth = Spark.drawWidth;
    ctx.lineCap = "round";
    ctx.beginPath();
    ctx.moveTo(this.x, this.y);
    ctx.lineTo(this.prevX, this.prevY);
    ctx.stroke();
    ctx.globalAlpha = 1;
  }
}

class BurstFlash {
  x: number;
  y: number;
  radius: number;

  static active: BurstFlash[] = [];
  static _pool: BurstFlash[] = [];

  constructor(x: number, y: number, radius: number) {
    this.x = x;
    this.y = y;
    this.radius = radius;
  }

  static add(x: number, y: number, radius: number) {
    const instance = this._pool.pop() || new BurstFlash(x, y, radius);
    instance.x = x;
    instance.y = y;
    instance.radius = radius;
    this.active.push(instance);
    return instance;
  }

  static returnInstance(instance: BurstFlash) {
    this._pool.push(instance);
  }
}

class Shell {
  x: number;
  y: number;
  targetX: number;
  targetY: number;
  speedX: number;
  speedY: number;
  life: number;
  color: string;
  size: number;
  type: string;
  exploded: boolean;
  comet?: Star;

  constructor(x: number, targetX: number, targetY: number) {
    this.x = x;
    this.y = window.innerHeight;
    this.targetX = targetX;
    this.targetY = targetY;

    // Ajusta a velocidade inicial para ser mais controlada
    const distance = Math.sqrt((targetX - x) ** 2 + (targetY - this.y) ** 2);
    const launchVelocity = (distance * 0.022) ** 0.64; // Reduzido de 0.026 para 0.022

    this.speedX = ((targetX - x) / distance) * launchVelocity * 0.3;
    this.speedY = -launchVelocity;
    this.life = launchVelocity * 300;
    this.color = randomColor();
    this.size = 1.1;
    this.type = this.getRandomType();
    this.exploded = false;
  }

  getRandomType(): string {
    // Agora pode ser 'chrysanthemum' ou 'palm'
    const types = ["chrysanthemum", "palm"];
    return types[randomInt(0, types.length)];
  }

  update(timeStep: number): boolean {
    if (this.exploded) return false;

    this.life -= timeStep;
    this.x += this.speedX * (timeStep / 16);
    this.y += this.speedY * (timeStep / 16);
    this.speedY += (GRAVITY * timeStep) / 1000;

    const distanceToTarget = Math.sqrt(
      (this.targetX - this.x) ** 2 + (this.targetY - this.y) ** 2
    );

    if (distanceToTarget < 20 || this.life <= 0 || this.speedY > 0) {
      return false;
    }

    return true;
  }

  launch() {
    // Cobrinha maior
    this.comet = Star.add(
      this.x,
      this.y,
      this.color,
      Math.PI,
      Math.abs(this.speedY) * 1.25, // um pouco maior
      this.life
    );
    this.comet.heavy = true;
    this.comet.spinRadius = random(1.0, 1.6); // aumenta o raio da cobrinha
    this.comet.sparkFreq = 32;
    this.comet.sparkLife = 320;
    this.comet.sparkLifeVariation = 3;
    this.comet.sparkSpeed = 0.5;
    this.comet.sparkColor = this.color;
  }

  draw(ctx: CanvasRenderingContext2D) {
    if (this.exploded) return;

    // Draw comet trail
    ctx.strokeStyle = this.color;
    ctx.lineWidth = 4;
    ctx.lineCap = "round";
    ctx.beginPath();
    ctx.moveTo(this.x, this.y);
    ctx.lineTo(this.x - this.speedX * 3, this.y - this.speedY * 3);
    ctx.stroke();

    // Draw comet head
    ctx.fillStyle = this.color;
    ctx.beginPath();
    ctx.arc(this.x, this.y, 3, 0, PI_2);
    ctx.fill();
  }

  explode(stars: Star[], sparks: Spark[]) {
    this.exploded = true;

    // Volta ao padrão de partículas e velocidade
    const starCount = 60 + randomInt(0, 40);
    const speed = 3 + this.size;

    switch (this.type) {
      case "chrysanthemum":
        this.createChrysanthemum(stars, sparks, starCount, speed);
        break;
      case "palm":
        this.createPalm(stars, sparks, starCount, speed);
        break;
    }

    // Criar efeito de flash
    this.createBurstFlash(sparks);
  }

  createChrysanthemum(
    stars: Star[],
    sparks: Spark[],
    count: number,
    speed: number
  ) {
    createBurst(count, (angle, speedMult) => {
      const star = Star.add(
        this.x,
        this.y,
        this.color,
        angle,
        speed * 0.8, // Velocidade mais consistente
        900 + random(-200, 200)
      );

      if (Math.random() < 0.3) {
        star.sparkFreq = 200;
        star.sparkColor = COLORS.Gold;
        star.sparkSpeed = 0.5;
        star.sparkLife = 400;
      }

      stars.push(star);
    });
  }

  createPalm(stars: Star[], sparks: Spark[], count: number, speed: number) {
    createBurst(Math.floor(count * 0.4), (angle, speedMult) => {
      const star = Star.add(
        this.x,
        this.y,
        this.color,
        angle,
        speedMult * speed,
        1800 + random(-300, 300)
      );

      star.sparkFreq = 80;
      star.sparkColor = COLORS.Gold;
      star.sparkSpeed = 0.8;
      star.sparkLife = 1400;
      star.sparkLifeVariation = 2;

      stars.push(star);
    });
  }

  createBurstFlash(sparks: Spark[]) {
    // Create bright white flash
    BurstFlash.add(this.x, this.y, 40 + this.size * 10);

    for (let i = 0; i < 30; i++) {
      sparks.push(
        new Spark(
          this.x,
          this.y,
          COLORS.White,
          Math.random() * PI_2,
          random(0.5, 3),
          150 + random(-30, 30)
        )
      );
    }
  }
}

const Fireworks2: React.FC = () => {
  const trailsCanvasRef = useRef<HTMLCanvasElement>(null);
  const mainCanvasRef = useRef<HTMLCanvasElement>(null);
  const animationFrameRef = useRef<number>();
  const lastTimeRef = useRef<number>(0);
  const shellsRef = useRef<Shell[]>([]);
  const starsRef = useRef<Star[]>([]);
  const sparksRef = useRef<Spark[]>([]);
  const lastLaunchTimeRef = useRef<number>(0);

  const resizeCanvas = useCallback(() => {
    const trailsCanvas = trailsCanvasRef.current;
    const mainCanvas = mainCanvasRef.current;
    if (!trailsCanvas || !mainCanvas) return;

    const width = window.innerWidth;
    const height = window.innerHeight;

    trailsCanvas.width = width;
    trailsCanvas.height = height;
    mainCanvas.width = width;
    mainCanvas.height = height;
  }, []);

  const launchShell = useCallback(() => {
    const width = window.innerWidth;
    const height = window.innerHeight;
    const margin = 120;
    const x = random(margin, width - margin);
    const targetX = random(margin, width - margin);
    // Ajusta a altura máxima para ser mais baixa
    const targetY = random(height * 0.35, height * 0.45); // Antes era 0.25 até 0.55

    const shell = new Shell(x, targetX, targetY);
    shell.launch();
    shellsRef.current.push(shell);
  }, []);

  const update = useCallback(
    (currentTime: number) => {
      const trailsCanvas = trailsCanvasRef.current;
      const mainCanvas = mainCanvasRef.current;
      if (!trailsCanvas || !mainCanvas) return;

      const trailsCtx = trailsCanvas.getContext("2d");
      const mainCtx = mainCanvas.getContext("2d");
      if (!trailsCtx || !mainCtx) return;

      const deltaTime = currentTime - lastTimeRef.current || 16;
      lastTimeRef.current = currentTime;

      // Auto-launch shells
      if (currentTime - lastLaunchTimeRef.current > 1500) {
        launchShell();
        lastLaunchTimeRef.current = currentTime;
      }

      // Clear main canvas
      mainCtx.clearRect(0, 0, mainCanvas.width, mainCanvas.height);

      // Add fade effect to trails canvas
      trailsCtx.globalCompositeOperation = "source-over";
      trailsCtx.fillStyle = "rgba(0, 0, 0, 0.15)";
      trailsCtx.fillRect(0, 0, trailsCanvas.width, trailsCanvas.height);

      // Draw burst flashes first
      while (BurstFlash.active.length > 0) {
        const flash = BurstFlash.active.pop()!;
        const gradient = trailsCtx.createRadialGradient(
          flash.x,
          flash.y,
          0,
          flash.x,
          flash.y,
          flash.radius
        );
        gradient.addColorStop(0.024, "rgba(255, 255, 255, 1)");
        gradient.addColorStop(0.125, "rgba(255, 160, 20, 0.2)");
        gradient.addColorStop(0.32, "rgba(255, 140, 20, 0.11)");
        gradient.addColorStop(1, "rgba(255, 120, 20, 0)");
        trailsCtx.fillStyle = gradient;
        trailsCtx.fillRect(
          flash.x - flash.radius,
          flash.y - flash.radius,
          flash.radius * 2,
          flash.radius * 2
        );
        BurstFlash.returnInstance(flash);
      }

      trailsCtx.globalCompositeOperation = "lighten";

      // Update and draw shells
      for (let i = shellsRef.current.length - 1; i >= 0; i--) {
        const shell = shellsRef.current[i];
        shell.draw(mainCtx);

        if (!shell.update(deltaTime)) {
          shell.explode(starsRef.current, sparksRef.current);
          shellsRef.current.splice(i, 1);
        }
      }

      // Update and draw stars by color
      COLOR_CODES_W_INVIS.forEach((color) => {
        const stars = Star.active[color];
        trailsCtx.strokeStyle = color;
        trailsCtx.lineWidth = Star.drawWidth;
        trailsCtx.lineCap = "round";

        for (let i = stars.length - 1; i >= 0; i--) {
          const star = stars[i];
          if (color !== INVISIBLE) {
            star.draw(trailsCtx);
          }

          if (!star.update(deltaTime, sparksRef.current)) {
            stars.splice(i, 1);
            Star.returnInstance(star);
          }
        }
      });

      // Update and draw sparks by color
      COLOR_CODES.forEach((color) => {
        const sparks = Spark.active[color];
        trailsCtx.strokeStyle = color;
        trailsCtx.lineWidth = Spark.drawWidth;
        trailsCtx.lineCap = "round";

        for (let i = sparks.length - 1; i >= 0; i--) {
          const spark = sparks[i];
          spark.draw(trailsCtx);

          if (!spark.update(deltaTime)) {
            sparks.splice(i, 1);
            Spark.returnInstance(spark);
          }
        }
      });

      animationFrameRef.current = requestAnimationFrame(update);
    },
    [launchShell]
  );

  useEffect(() => {
    resizeCanvas();
    window.addEventListener("resize", resizeCanvas);

    // Start animation
    animationFrameRef.current = requestAnimationFrame(update);

    return () => {
      window.removeEventListener("resize", resizeCanvas);
      if (animationFrameRef.current) {
        cancelAnimationFrame(animationFrameRef.current);
      }
    };
  }, [resizeCanvas, update]);

  return (
    <div
      className="fixed inset-0 pointer-events-none"
      style={{ width: "100vw", height: "100vh" }}
    >
      <canvas
        ref={trailsCanvasRef}
        className="absolute inset-0"
        style={{ mixBlendMode: "lighten", width: "100vw", height: "100vh", display: "block" }}
      />
      <canvas
        ref={mainCanvasRef}
        className="absolute inset-0"
        style={{ mixBlendMode: "lighten", width: "100vw", height: "100vh", display: "block" }}
      />
    </div>
  );
};

export default Fireworks2;
