import * as THREE from 'three';
import { Weapon } from './Weapon';
import { Player } from './Player';
import { loadGLB } from './MeshLoader';
import { playSound } from './SoundManager';
import { SlashEffect, isInSlashArc } from './SwordTrail';

export class Sword extends Weapon {
  private mixer: THREE.AnimationMixer | null = null;
  private clips: Map<string, THREE.AnimationAction> = new Map();

  constructor() {
    super();
    this.COOLDOWN1_TIME = 0.5;

    this.mesh.position.set(-0.35, 0.3, 0.35);
    this.mesh.scale.set(0.2, 0.2, 0.2);
    this.mesh.rotation.set(0.3, 0, 0.3);

    // Fallback geometry
    const bladeGeom = new THREE.BoxGeometry(0.08, 1.0, 0.15);
    const handleGeom = new THREE.CylinderGeometry(0.05, 0.05, 0.2, 8);
    const guardGeom = new THREE.BoxGeometry(0.25, 0.06, 0.06);
    const metalMat = new THREE.MeshStandardMaterial({ color: 0xcccccc, metalness: 0.9, roughness: 0.3 });
    const darkMat = new THREE.MeshStandardMaterial({ color: 0x444444, roughness: 0.6 });

    const blade = new THREE.Mesh(bladeGeom, metalMat);
    blade.position.y = 0.4;
    blade.castShadow = true;
    const guard = new THREE.Mesh(guardGeom, metalMat);
    guard.position.y = -0.1;
    const handle = new THREE.Mesh(handleGeom, darkMat);
    handle.position.y = -0.25;
    this.mesh.add(blade);
    this.mesh.add(guard);
    this.mesh.add(handle);
    this._fallback = true;
  }

  async init() {
    try {
      const gltf = await loadGLB('sword');
      const model = gltf.scene;
      model.scale.set(1, 1, 1);
      const swordTexture = new THREE.TextureLoader().load('./textures/sword.png');
      swordTexture.flipY = false;
      swordTexture.colorSpace = THREE.SRGBColorSpace;
      model.traverse((child) => {
        if (child instanceof THREE.Mesh) {
          child.castShadow = true;
          child.material = new THREE.MeshToonMaterial({ map: swordTexture });
        }
      });

      // Remove fallback geometry
      while (this.mesh.children.length > 0) {
        this.mesh.remove(this.mesh.children[0]);
      }
      this._fallback = false;
      this.mesh.add(model);

      if (gltf.animations.length > 0) {
        this.mixer = new THREE.AnimationMixer(model);
        for (const clip of gltf.animations) {
          const action = this.mixer.clipAction(clip);
          this.clips.set(clip.name, action);
        }
        const normal = this.clips.get('normal');
        if (normal) {
          normal.play();
        }
      }
    } catch (e) {
      console.warn('Failed to load sword model', e);
    }
  }

  private _fallback = false;

  updateAnimation(dt: number) {
    if (this.mixer) this.mixer.update(dt);
  }

  skill1(_groundPoint: THREE.Vector3, player: Player): void {
    if (this.cooldown1 > 0) return;
    this.cooldown1 = this.COOLDOWN1_TIME;

    playSound('sword_hit1', 0.5);

    // Play skill1 animation
    const action = this.clips.get('skill1');
    if (action) {
      action.reset();
      action.setLoop(THREE.LoopOnce, 1);
      action.setEffectiveTimeScale(2);
      action.play();
    }

    const { game } = player;

    const range = player.stats.swordRange;
    const damage = player.stats.swordDamage;

    // Aim direction: where the blob actually faces (toward the mouse point)
    const aim = new THREE.Vector3()
      .subVectors(game.groundPoint, player.body.position);
    aim.y = 0;
    if (aim.lengthSq() < 0.001) {
      aim.set(0, 0, -1).applyQuaternion(player.body.quaternion);
      aim.y = 0;
    }
    aim.normalize();

    // Slash trail feedback
    const slash = new SlashEffect(player.body.position, aim, game.scene, range);
    game.slashEffects.push(slash);

    for (const enemy of game.enemies) {
      if (enemy.dead) continue;
      if (!enemy.canBeSlashHit()) continue;
      if (isInSlashArc(player.body.position, aim, enemy.body.position, range)) {
        enemy.takeDamage(damage, game, true, player.body.position);
      }
    }

    // Destroy enemy projectiles caught in the slash
    game.destroyProjectilesInArc(player.body.position, aim, range);
  }

  skill2(): void {
    // Not implemented
  }
}
