import * as THREE from 'three';
import { Weapon } from './Weapon';
import { Player } from './Player';
import { lightningDamage, lightningJumps, lightningJumpRange, lightningCooldown } from './Skills';

const CHAIN_FIRST_RANGE = 14;
const FLASH_TIME = 0.35;

export class ChainLightning extends Weapon {
  constructor() {
    super();
    this.COOLDOWN1_TIME = lightningCooldown(1);
  }

  skill1(_groundPoint: THREE.Vector3, player: Player): void {
    const skills = player.game.skillSystem;
    const level = skills.getLevel('chainlightning');
    if (level <= 0) return;
    if (this.cooldown1 > 0) return;

    this.COOLDOWN1_TIME = lightningCooldown(level);
    this.cooldown1 = this.COOLDOWN1_TIME;

    const game = player.game;
    const damage = lightningDamage(level);
    const maxJumps = lightningJumps(level);
    const jumpRange = lightningJumpRange(level);

    // Chain through enemies, always the closest un-hit one
    const start = player.body.position.clone();
    start.y += 0.8;
    const chainPoints: THREE.Vector3[] = [start];
    const hit: Set<import('./Enemy').Enemy> = new Set();
    let from = player.body.position.clone();

    for (let i = 0; i < maxJumps; i++) {
      let best: import('./Enemy').Enemy | null = null;
      let bestDist = i === 0 ? CHAIN_FIRST_RANGE : jumpRange;
      for (const enemy of game.enemies) {
        if (enemy.dead || hit.has(enemy)) continue;
        const dist = enemy.body.position.distanceTo(from);
        if (dist < bestDist) {
          bestDist = dist;
          best = enemy;
        }
      }
      if (!best) break;

      hit.add(best);
      const fromPoint = from.clone();
      best.takeDamage(damage, game, true, fromPoint);
      const point = best.body.position.clone();
      point.y += 0.8;
      chainPoints.push(point);
      from = best.body.position;
    }

    if (chainPoints.length < 2) return;

    this.spawnVisual(game.scene, chainPoints, hit);
  }

  skill2(): void {
    // Not implemented
  }

  private spawnVisual(
    scene: THREE.Scene,
    points: THREE.Vector3[],
    hit: Set<import('./Enemy').Enemy>
  ) {
    const group = new THREE.Group();
    scene.add(group);

    // Segments between chain points
    for (let i = 0; i < points.length - 1; i++) {
      const lineGeom = new THREE.BufferGeometry().setFromPoints([
        points[i],
        points[i + 1],
      ]);
      const lineMat = new THREE.LineBasicMaterial({
        color: 0x88ccff,
        transparent: true,
        opacity: 0.9,
        blending: THREE.AdditiveBlending,
        depthWrite: false,
      });
      const line = new THREE.Line(lineGeom, lineMat);
      group.add(line);

      // Jagged inner bolt
      const mid = new THREE.Vector3()
        .addVectors(points[i], points[i + 1])
        .multiplyScalar(0.5);
      mid.x += (Math.random() - 0.5) * 1.2;
      mid.y += (Math.random() - 0.5) * 1.2;
      mid.z += (Math.random() - 0.5) * 1.2;
      const boltGeom = new THREE.BufferGeometry().setFromPoints([
        points[i], mid, points[i + 1],
      ]);
      const boltMat = new THREE.LineBasicMaterial({
        color: 0xffffff,
        transparent: true,
        opacity: 0.9,
        blending: THREE.AdditiveBlending,
        depthWrite: false,
      });
      const bolt = new THREE.Line(boltGeom, boltMat);
      group.add(bolt);
    }

    // Flash spheres on hit enemies
    const flashes: THREE.Mesh[] = [];
    for (const enemy of hit) {
      const flashGeom = new THREE.SphereGeometry(0.7, 12, 12);
      const flashMat = new THREE.MeshBasicMaterial({
        color: 0x88ccff,
        transparent: true,
        opacity: 0.7,
        blending: THREE.AdditiveBlending,
        depthWrite: false,
      });
      const flash = new THREE.Mesh(flashGeom, flashMat);
      flash.position.copy(enemy.body.position);
      flash.position.y += 0.8;
      group.add(flash);
      flashes.push(flash);
    }

    const startTime = performance.now();
    const update = () => {
      const elapsed = (performance.now() - startTime) / 1000;
      if (elapsed > FLASH_TIME) {
        group.removeFromParent();
        for (const child of [...group.children]) {
          if (child instanceof THREE.Line || child instanceof THREE.Mesh) {
            child.geometry.dispose();
            (child.material as THREE.Material).dispose();
          }
        }
        return;
      }
      const fade = 1 - elapsed / FLASH_TIME;
      for (const child of group.children) {
        if (child instanceof THREE.Line || child instanceof THREE.Mesh) {
          (child.material as THREE.Material & { opacity: number }).opacity = fade;
        }
      }
      for (const flash of flashes) {
        const s = 0.7 + elapsed * 3;
        flash.scale.set(s, s, s);
      }
      requestAnimationFrame(update);
    };
    update();
  }
}
