import * as THREE from 'three';
import { playSound } from './SoundManager';

const flameTexture = new THREE.TextureLoader().load('./textures/flame.png');
flameTexture.colorSpace = THREE.SRGBColorSpace;

export class Fireball {
  body: THREE.Group;
  light: THREE.PointLight;
  direction: THREE.Vector3;
  lifetime = 1;
  exploding = false;
  timer = 0;
  damageDone = false;
  damage = 100;
  explosionRadius = 8;
  private trailParticles: THREE.Points;
  private trailVelocities: Float32Array;
  private trailLives: Float32Array;
  private trailMaxLife = 0.6;

  constructor(start: THREE.Vector3, direction: THREE.Vector3) {
    this.direction = direction.clone();
    this.body = new THREE.Group();
    this.body.position.copy(start);

    // Fireball core (additive glow)
    const coreGeom = new THREE.SphereGeometry(0.45, 16, 16);
    const coreMat = new THREE.MeshBasicMaterial({
      color: 0xffcc44,
      blending: THREE.AdditiveBlending,
      depthWrite: false,
      transparent: true,
    });
    const core = new THREE.Mesh(coreGeom, coreMat);
    this.body.add(core);

    const glowGeom = new THREE.SphereGeometry(0.75, 16, 16);
    const glowMat = new THREE.MeshBasicMaterial({
      color: 0xff6600, transparent: true, opacity: 0.45,
      blending: THREE.AdditiveBlending,
      depthWrite: false,
    });
    const glow = new THREE.Mesh(glowGeom, glowMat);
    this.body.add(glow);

    // Sprite with flame texture
    const spriteMat = new THREE.SpriteMaterial({
      map: flameTexture,
      color: 0xffaa33,
      blending: THREE.AdditiveBlending,
      depthWrite: false,
      transparent: true,
    });
    const sprite = new THREE.Sprite(spriteMat);
    sprite.scale.set(2.2, 2.2, 1);
    this.body.add(sprite);

    // Trail particles (flame sprites behind the fireball)
    const trailCount = 30;
    const trailGeom = new THREE.BufferGeometry();
    const positions = new Float32Array(trailCount * 3);
    trailGeom.setAttribute('position', new THREE.BufferAttribute(positions, 3));
    const trailMat = new THREE.PointsMaterial({
      map: flameTexture,
      color: 0xff8833,
      size: 1.0,
      blending: THREE.AdditiveBlending,
      depthWrite: false,
      transparent: true,
      opacity: 0.7,
    });
    this.trailParticles = new THREE.Points(trailGeom, trailMat);
    this.body.add(this.trailParticles);
    this.trailVelocities = new Float32Array(trailCount * 3);
    this.trailLives = new Float32Array(trailCount).fill(0);

    // Strong dynamic light (original: radius 100, orange, with shadow renderer)
    this.light = new THREE.PointLight(0xff7722, 80, 45, 1.5);
    this.light.position.set(0, 0.5, 0);
    this.light.castShadow = true;
    this.light.shadow.mapSize.width = 256;
    this.light.shadow.mapSize.height = 256;
    this.light.shadow.camera.near = 0.5;
    this.light.shadow.camera.far = 45;
    this.light.shadow.bias = -0.005;
    this.body.add(this.light);
  }

  updateTrail(dt: number, pos: THREE.Vector3) {
    const attr = this.trailParticles.geometry.getAttribute('position') as THREE.BufferAttribute;
    const arr = attr.array as Float32Array;

    for (let i = 0; i < this.trailLives.length; i++) {
      if (this.trailLives[i] > 0) {
        this.trailLives[i] -= dt;
        arr[i * 3] += this.trailVelocities[i * 3] * dt;
        arr[i * 3 + 1] += this.trailVelocities[i * 3 + 1] * dt;
        arr[i * 3 + 2] += this.trailVelocities[i * 3 + 2] * dt;
        if (this.trailLives[i] <= 0) {
          arr[i * 3] = pos.x;
          arr[i * 3 + 1] = pos.y;
          arr[i * 3 + 2] = pos.z;
        }
      } else {
        // Spawn new trail particle
        arr[i * 3] = pos.x;
        arr[i * 3 + 1] = pos.y;
        arr[i * 3 + 2] = pos.z;
        this.trailVelocities[i * 3] = (Math.random() - 0.5) * 1.5;
        this.trailVelocities[i * 3 + 1] = (Math.random() - 0.5) * 1.5;
        this.trailVelocities[i * 3 + 2] = (Math.random() - 0.5) * 1.5;
        this.trailLives[i] = this.trailMaxLife * (0.5 + Math.random());
      }
    }
    attr.needsUpdate = true;
  }

  explode(scene: THREE.Scene) {
    if (this.exploding) return;
    this.exploding = true;
    this.timer = 0;
    this.damageDone = false;

    playSound('explosion', 0.6);

    // Hide the fireball core and trail
    for (const child of [...this.body.children]) {
      if (child instanceof THREE.Mesh || child instanceof THREE.Sprite || child instanceof THREE.Points) {
        child.visible = false;
      }
    }

    // Bright flash
    const flashGeom = new THREE.SphereGeometry(3, 16, 16);
    const flashMat = new THREE.MeshBasicMaterial({
      color: 0xffdd66, transparent: true, opacity: 0.9,
      blending: THREE.AdditiveBlending,
      depthWrite: false,
    });
    const flash = new THREE.Mesh(flashGeom, flashMat);
    flash.position.copy(this.body.position);
    scene.add(flash);

    // Big explosion light pulse
    const flashLight = new THREE.PointLight(0xff8833, 200, 35, 1.5);
    flashLight.position.copy(this.body.position);
    scene.add(flashLight);

    const startTime = performance.now();
    const updateFlash = () => {
      const elapsed = (performance.now() - startTime) / 1000;
      if (elapsed > 0.6) {
        flash.removeFromParent();
        flash.geometry.dispose();
        flashMat.dispose();
        scene.remove(flashLight);
        return;
      }
      const scale = 1 + elapsed * 8;
      flash.scale.set(scale, scale, scale);
      flashMat.opacity = 0.9 * (1 - elapsed / 0.6);
      flashLight.intensity = 200 * (1 - elapsed / 0.6);
      requestAnimationFrame(updateFlash);
    };
    updateFlash();

    // Fire particle burst (original: 50 flame particles)
    this.spawnExplosionParticles(scene, './textures/flame.png', 0xffaa33, 60, 8, 1.5);

    // Debris stones (original: stone particles with gravity)
    this.spawnExplosionParticles(scene, './textures/flame.png', 0x555544, 25, 6, 1.0, true);
  }

  private spawnExplosionParticles(
    scene: THREE.Scene,
    texPath: string,
    color: number,
    count: number,
    speed: number,
    size: number,
    withGravity = false
  ) {
    const tex = new THREE.TextureLoader().load(texPath);
    tex.colorSpace = THREE.SRGBColorSpace;
    const geom = new THREE.BufferGeometry();
    const positions = new Float32Array(count * 3);
    const velocities: THREE.Vector3[] = [];
    const lives: number[] = [];
    const maxLives: number[] = [];

    for (let i = 0; i < count; i++) {
      positions[i * 3] = this.body.position.x;
      positions[i * 3 + 1] = this.body.position.y;
      positions[i * 3 + 2] = this.body.position.z;
      velocities.push(new THREE.Vector3(
        (Math.random() - 0.5) * speed * 2,
        Math.random() * speed,
        (Math.random() - 0.5) * speed * 2
      ));
      const life = 0.5 + Math.random() * 1.2;
      lives.push(life);
      maxLives.push(life);
    }

    geom.setAttribute('position', new THREE.BufferAttribute(positions, 3));
    const mat = new THREE.PointsMaterial({
      map: tex,
      color,
      size,
      blending: THREE.AdditiveBlending,
      depthWrite: false,
      transparent: true,
    });
    const points = new THREE.Points(geom, mat);
    points.renderOrder = 999;
    scene.add(points);

    const startTime = performance.now();
    const update = () => {
      const elapsed = (performance.now() - startTime) / 1000;
      if (elapsed > 2) {
        points.removeFromParent();
        geom.dispose();
        mat.dispose();
        tex.dispose();
        return;
      }
      const attr = geom.getAttribute('position') as THREE.BufferAttribute;
      const arr = attr.array as Float32Array;
      let allDead = true;
      for (let i = 0; i < count; i++) {
        lives[i] -= 0.016;
        if (lives[i] <= 0) continue;
        allDead = false;
        const v = velocities[i];
        if (withGravity) v.y -= 9.8 * 0.016;
        arr[i * 3] += v.x * 0.016;
        arr[i * 3 + 1] += v.y * 0.016;
        arr[i * 3 + 2] += v.z * 0.016;
      }
      attr.needsUpdate = true;
      mat.opacity = Math.min(1, elapsed / 2) * 0.9;
      if (allDead) {
        points.removeFromParent();
        geom.dispose();
        mat.dispose();
        tex.dispose();
        return;
      }
      requestAnimationFrame(update);
    };
    update();
  }
}
