const PARTICLE_COUNT = 400;
const SPAWN_DURATION = 700;
const VX_RANGE = 0.5;
const VY_MIN = 0.3;
const VY_RANGE = 0.2;
const WIDTH_MIN = 3;
const WIDTH_RANGE = 4;
const HEIGHT_MIN = 2;
const HEIGHT_RANGE = 3;
const ROTATION_SPEED_RANGE = 0.15;
const DRIFT_STRENGTH = 0.03;
const GRAVITY = 0.007;
const VX_DAMPING = 0.995;
const SWAY_AMPLITUDE = 0.3;
const SWAY_SPEED_MIN = 0.02;
const SWAY_SPEED_RANGE = 0.03;
const OFFSCREEN_MARGIN = 10;
const TOTAL_HEIGHT = 192 * 2;

const COLORS = [
  "#fb0018",
  "#0059f3",
  "#f3e300",
  "#00fb00",
  "#fb8afb",
  "#fb9200",
];

export type ConfettiParticle = {
  x: number;
  y: number;
  vx: number;
  vy: number;
  width: number;
  height: number;
  color: string;
  rotation: number;
  rotationSpeed: number;
  swayPhase: number;
  swaySpeed: number;
};

type Spawner = {
  frame: number;
  accumulator: number;
  particleCount: number;
  duration: number;
};

const particles: ConfettiParticle[] = [];
const spawners: Spawner[] = [];

const spawnParticle = () => {
  particles.push({
    x: Math.random() * LOGICAL_WIDTH,
    y: -(Math.random() * 20),
    vx: (Math.random() - 0.5) * VX_RANGE,
    vy: VY_MIN + Math.random() * VY_RANGE,
    width: WIDTH_MIN + Math.random() * WIDTH_RANGE,
    height: HEIGHT_MIN + Math.random() * HEIGHT_RANGE,
    color: COLORS[Math.floor(Math.random() * COLORS.length)]!,
    rotation: Math.random() * Math.PI * 2,
    rotationSpeed: (Math.random() - 0.5) * ROTATION_SPEED_RANGE,
    swayPhase: Math.random() * Math.PI * 2,
    swaySpeed: SWAY_SPEED_MIN + Math.random() * SWAY_SPEED_RANGE,
  });
};

const spawn = (particleCount = PARTICLE_COUNT, duration = SPAWN_DURATION) => {
  spawners.push({ frame: 0, accumulator: 0, particleCount, duration });
};

const update = () => {
  for (let s = spawners.length - 1; s >= 0; s -= 1) {
    const spawner = spawners[s]!;
    if (spawner.frame < spawner.duration) {
      const progress = spawner.frame / spawner.duration;
      const inv = 1 - progress;
      const rate = inv * inv;
      spawner.accumulator +=
        rate * (spawner.particleCount / spawner.duration) * 3;

      const count = Math.floor(spawner.accumulator);
      spawner.accumulator -= count;

      for (let i = 0; i < count; i += 1) {
        spawnParticle();
      }

      spawner.frame += 1;
    } else {
      spawners.splice(s, 1);
    }
  }

  for (let i = particles.length - 1; i >= 0; i -= 1) {
    const p = particles[i]!;
    p.vy += GRAVITY;
    p.vx += (Math.random() - 0.5) * DRIFT_STRENGTH;
    p.vx *= VX_DAMPING;
    p.swayPhase += p.swaySpeed;
    p.x += p.vx + Math.sin(p.swayPhase) * SWAY_AMPLITUDE;
    p.y += p.vy;
    p.rotation += p.rotationSpeed;

    if (p.y > TOTAL_HEIGHT + OFFSCREEN_MARGIN) {
      particles.splice(i, 1);
    }
  }
};

export const useConfetti = () => ({ particles, spawn, update });
