import * as THREE from 'three';
import type { Game } from './Game';
import { Enemy } from './Enemy';
import { playLoopingSound } from './SoundManager';
import { loadGLB } from './MeshLoader';
import type { GLTF } from 'three/examples/jsm/loaders/GLTFLoader.js';
import { clone as cloneSkeleton } from 'three/examples/jsm/utils/SkeletonUtils.js';

let spiderGLTF: GLTF | null = null;
const ANIMATIONS_ENABLED = true;
export async function initSpiderModel() {
  try {
    spiderGLTF = await loadGLB('spider');
  } catch {
    console.warn('Failed to load spider model');
  }
}

// Wiederverwendbarer Spinnen-Modell-Builder (für Spinne UND Spinnenkönigin).
// tint färbt die Textur mit ein (0xffffff = Original).
export function buildSpiderModel(scale: number, tint: number): THREE.Group | null {
  if (!spiderGLTF) return null;
  const model = cloneSkeleton(spiderGLTF.scene) as THREE.Group;
  model.scale.set(scale, scale, scale);
  const spiderTexture = new THREE.TextureLoader().load('./textures/spider_animation2.png');
  spiderTexture.flipY = false;
  spiderTexture.colorSpace = THREE.SRGBColorSpace;
  model.traverse((child) => {
    if (child instanceof THREE.Mesh) {
      child.castShadow = true;
      child.material = new THREE.MeshToonMaterial({ map: spiderTexture, color: tint });
    }
  });
  return model;
}

// Animationen des Spinnen-Modells (leer, wenn nicht geladen)
export function getSpiderAnimations(): THREE.AnimationClip[] {
  return spiderGLTF ? spiderGLTF.animations : [];
}

export class Spider extends Enemy {
  constructor() {
    super();
    this.stopWalkSound = playLoopingSound('spiderwalking', 0.3);

    const model = buildSpiderModel(0.5, 0xffffff);
    if (model) {
      this.body.add(model);

      if (getSpiderAnimations().length > 0 && ANIMATIONS_ENABLED) {
        this.mixer = new THREE.AnimationMixer(model);
        for (const clip of getSpiderAnimations()) {
          const action = this.mixer.clipAction(clip);
          this.clips.set(clip.name, action);
        }
        this.playAnim('stand');
      }
    } else {
      // Fallback
      const abdomenGeom = new THREE.SphereGeometry(0.5, 8, 8);
      const bodyMat = new THREE.MeshToonMaterial({ color: 0x333333 });
      const abdomen = new THREE.Mesh(abdomenGeom, bodyMat);
      abdomen.scale.set(1, 0.6, 1.3);
      abdomen.position.y = 0.6;
      abdomen.castShadow = true;
      this.body.add(abdomen);

      const cephalothoraxGeom = new THREE.SphereGeometry(0.3, 8, 8);
      const cephalothorax = new THREE.Mesh(cephalothoraxGeom, bodyMat);
      cephalothorax.position.y = 0.6;
      cephalothorax.position.z = 0.5;
      cephalothorax.castShadow = true;
      this.body.add(cephalothorax);

      const eyeGeom = new THREE.SphereGeometry(0.06, 6, 6);
      const eyeMat = new THREE.MeshBasicMaterial({ color: 0xff0000 });
      for (const sign of [-1, 1]) {
        const eye = new THREE.Mesh(eyeGeom, eyeMat);
        eye.position.set(sign * 0.12, 0.85, 0.7);
        this.body.add(eye);
      }

      const legGeom = new THREE.CylinderGeometry(0.04, 0.04, 0.8, 4);
      for (let i = 0; i < 8; i++) {
        const leg = new THREE.Mesh(legGeom, new THREE.MeshToonMaterial({ color: 0x222222 }));
        const angle = (i / 8) * Math.PI * 2;
        const side = i < 4 ? 1 : -1;
        leg.position.set(Math.cos(angle) * 0.35, 0.3, Math.sin(angle) * 0.35);
        leg.rotation.z = side * 0.6;
        leg.rotation.x = angle;
        leg.castShadow = true;
        this.body.add(leg);
      }
    }
  }

  playAnim(name: string, loop = true, speed = 1) {
    if (!this.clips.has(name) || this.currentAnim === name) return;
    // Stop the previous animation so it doesn't blend with the new one
    if (this.currentAnim) {
      const prev = this.clips.get(this.currentAnim);
      if (prev) prev.stop();
    }
    this.currentAnim = name;
    const action = this.clips.get(name)!;
    action.reset();
    action.setLoop(loop ? THREE.LoopRepeat : THREE.LoopOnce, loop ? Infinity : 1);
    action.clampWhenFinished = !loop;
    action.setEffectiveTimeScale(speed);
    action.play();
  }

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

  update(dt: number, game: Game) {
    const toPlayer = new THREE.Vector3()
      .subVectors(game.player.body.position, this.body.position);
    toPlayer.y = 0;

    const dist = toPlayer.length();
    if (dist > 0.1) {
      const dir = toPlayer.normalize();
      this.body.lookAt(
        this.body.position.x + dir.x,
        this.body.position.y,
        this.body.position.z + dir.z
      );

      this.playAnim('walk');

      let moveDir = dir.multiplyScalar(this.speed);
      if (this.knockback > 0) {
        moveDir = dir.multiplyScalar(-this.speed);
        this.knockback -= dt;
      }
      this.body.position.x += moveDir.x * dt;
      this.body.position.z += moveDir.z * dt;
      game.clampToArena(this.body.position);
    } else {
      this.playAnim('stand');
    }
  }

  protected onDeath(_game: Game) {
    this.playAnim('die', false);
  }
}
