import * as THREE from 'three';
import type { Game } from './Game';
import { Enemy } from './Enemy';
import { playSound } from './SoundManager';
import { FallingRock } from './FallingRock';

const MAX_HEALTH = 6000;
const HIT_FLASH_TIME = 0.14;
const MODEL_SCALE = 1.7;
const IDEAL_DIST = 4.5;
const MIN_DIST = 3.2;
const MOVE_SPEED = 5;
const TURN_SPEED_1 = 2.2;
const TURN_SPEED_2 = 3.5;
const DASH_COOLDOWN = 8;
const DASH_COOLDOWN_2 = 4;
const DASH_WINDUP = 0.6;
const DASH_SPEED = 34;
const DASH_MAX_DIST = 30;
const DASH_DAMAGE = 35;
const WALL_LIMIT = 46;
const BOSS_STUN_TIME = 3.5;
const SWING_COOLDOWN = 5;
const SWING_COOLDOWN_2 = 3.5;
const SWING_WINDUP = 0.45;
const SWING_TIME = 0.3;
const SWING_RANGE = 5.2;
const SWING_ARC = (90 * Math.PI) / 180;
const SWING_DAMAGE = 60;
const STOMP_COOLDOWN = 10;
const STOMP_COOLDOWN_2 = 8;
const STOMP_JUMP_TIME = 1.0;
const STOMP_JUMP_HEIGHT = 4;
const STOMP_RADIUS = 11;
const STOMP_DAMAGE = 15;
const STOMP_STUN = 1.2;
const ENRAGE_RATIO = 0.4;
const ROCK_COUNT = 7;
const ROCK_COUNT_2 = 10;

type State = 'idle' | 'dashwindup' | 'dash' | 'dashstop' | 'stunned' | 'swingwindup' | 'swing' | 'jump';

export class Minotaurus extends Enemy {
  private model: THREE.Group;
  private game: Game | null = null;
  private waveTime = 0;
  private state: State = 'idle';
  private stateTime = 0;
  private yaw = 0;
  private enraged = false;
  private hitFlash = 0;

  private dashTimer = 3.5;
  private swingTimer = 2;
  private stompTimer = 6;
  private doubleDashLeft = 0;

  private dashDir = new THREE.Vector3();
  private dashDist = 0;
  private dashHitPlayer = false;

  private swingHit = false;

  private jumpFrom = new THREE.Vector3();
  private jumpTo = new THREE.Vector3();

  private axeGroup: THREE.Group;
  private eyeMat!: THREE.MeshBasicMaterial;
  private furMat!: THREE.MeshToonMaterial;
  private darkMat!: THREE.MeshToonMaterial;
  private starsGroup: THREE.Group;
  private telegraphArc!: THREE.Mesh;
  private swingArc!: THREE.Mesh;
  private dashLine!: THREE.Mesh;
  private shockRing!: THREE.Mesh;
  private shockRingMat!: THREE.MeshBasicMaterial;
  private legs: THREE.Group[] = [];
  private moving = false;

  private rocks: FallingRock[] = [];
  private cracks: { group: THREE.Group; life: number }[] = [];

  constructor() {
    super();
    this.health = MAX_HEALTH;
    this.maxHealth = MAX_HEALTH;
    this.isBoss = true;
    this.displayName = 'Minotaurus';
    this.speed = 0;
    this.xp = 200;
    this.contactDps = 0;
    this.contactRadius = 0;
    this.guaranteedDrop = true;

    this.model = new THREE.Group();
    this.model.scale.setScalar(MODEL_SCALE);
    // Kein fadeOutModel: beim Tod schrumpft der ganze Body inkl. Effekten
    this.body.add(this.model);

    this.furMat = new THREE.MeshToonMaterial({ color: 0x6b4a2f });
    this.darkMat = new THREE.MeshToonMaterial({ color: 0x4a3220 });
    const hornMat = new THREE.MeshToonMaterial({ color: 0xd8c8a8 });
    const axeMat = new THREE.MeshToonMaterial({ color: 0x9999aa });
    const metalMat = new THREE.MeshToonMaterial({ color: 0x8a8a99 });
    const darkMetalMat = new THREE.MeshToonMaterial({ color: 0x555566 });
    this.eyeMat = new THREE.MeshBasicMaterial({ color: 0xffcc33 });

    // Beine (Gruppen mit Hueft-Gelenk, laufen animiert)
    for (const side of [-1, 1]) {
      const legGroup = new THREE.Group();
      legGroup.position.set(side * 0.45, 0.75, 0);
      const leg = new THREE.Mesh(new THREE.CylinderGeometry(0.24, 0.3, 0.8, 10), this.darkMat);
      leg.position.y = -0.4;
      leg.castShadow = true;
      this.registerFadeMesh(leg);
      legGroup.add(leg);
      const hoof = new THREE.Mesh(new THREE.BoxGeometry(0.3, 0.16, 0.42), metalMat);
      hoof.position.set(0, -0.78, 0.06);
      this.registerFadeMesh(hoof);
      legGroup.add(hoof);
      const guard = new THREE.Mesh(new THREE.BoxGeometry(0.26, 0.4, 0.1), metalMat);
      guard.position.set(0, -0.4, 0.2);
      this.registerFadeMesh(guard);
      legGroup.add(guard);
      this.model.add(legGroup);
      this.legs.push(legGroup);
    }

    // Torso
    const torso = new THREE.Mesh(new THREE.BoxGeometry(1.4, 1.3, 1.0), this.furMat);
    torso.position.y = 1.15;
    torso.castShadow = true;
    this.registerFadeMesh(torso);
    this.model.add(torso);

    // Vorder-Panzerung: Brustplatte
    const chest = new THREE.Mesh(new THREE.BoxGeometry(1.1, 0.8, 0.1), metalMat);
    chest.position.set(0, 1.18, 0.48);
    chest.castShadow = true;
    this.registerFadeMesh(chest);
    this.model.add(chest);

    // Schultern (gepanzert vorn)
    for (const side of [-1, 1]) {
      const shoulder = new THREE.Mesh(new THREE.SphereGeometry(0.42, 12, 10), this.furMat);
      shoulder.position.set(side * 0.85, 1.6, 0);
      shoulder.castShadow = true;
      this.registerFadeMesh(shoulder);
      this.model.add(shoulder);
      const pauldron = new THREE.Mesh(new THREE.BoxGeometry(0.6, 0.3, 0.4), metalMat);
      pauldron.position.set(side * 0.85, 1.68, 0.3);
      this.registerFadeMesh(pauldron);
      this.model.add(pauldron);
    }

    // Brustpanzer-Gürtel
    const belt = new THREE.Mesh(new THREE.BoxGeometry(1.2, 0.16, 0.75), darkMetalMat);
    belt.position.set(0, 0.68, 0);
    this.registerFadeMesh(belt);
    this.model.add(belt);

    // Linker Arm
    const arm = new THREE.Mesh(new THREE.CylinderGeometry(0.2, 0.26, 0.9, 8), this.furMat);
    arm.position.set(-0.85, 0.85, 0);
    arm.castShadow = true;
    this.registerFadeMesh(arm);
    this.model.add(arm);

    // Rechter Arm mit Axt (schwingt beim Angriff)
    this.axeGroup = new THREE.Group();
    this.axeGroup.position.set(0.85, 1.0, 0);
    this.model.add(this.axeGroup);
    const axeArm = new THREE.Mesh(new THREE.CylinderGeometry(0.2, 0.26, 0.9, 8), this.furMat);
    axeArm.position.y = -0.15;
    axeArm.castShadow = true;
    this.registerFadeMesh(axeArm);
    this.axeGroup.add(axeArm);
    const handle = new THREE.Mesh(new THREE.CylinderGeometry(0.08, 0.08, 1.8, 8), this.darkMat);
    handle.rotation.x = Math.PI / 2;
    handle.position.z = 0.6;
    handle.castShadow = true;
    this.registerFadeMesh(handle);
    this.axeGroup.add(handle);
    const blade = new THREE.Mesh(new THREE.BoxGeometry(0.5, 0.7, 0.12), axeMat);
    blade.position.z = 1.35;
    blade.castShadow = true;
    this.registerFadeMesh(blade);
    this.axeGroup.add(blade);

    // Kopf
    const head = new THREE.Mesh(new THREE.BoxGeometry(0.7, 0.6, 0.75), this.furMat);
    head.position.y = 2.05;
    head.castShadow = true;
    this.registerFadeMesh(head);
    this.model.add(head);

    // Schnauze (+Z)
    const snout = new THREE.Mesh(new THREE.BoxGeometry(0.34, 0.24, 0.4), this.darkMat);
    snout.position.set(0, 1.92, 0.55);
    this.registerFadeMesh(snout);
    this.model.add(snout);

    // Hörner
    for (const side of [-1, 1]) {
      const horn = new THREE.Mesh(new THREE.ConeGeometry(0.09, 0.55, 8), hornMat);
      horn.position.set(side * 0.28, 2.35, 0.1);
      horn.rotation.set(-1.1, 0, side * 0.45);
      horn.castShadow = true;
      this.registerFadeMesh(horn);
      this.model.add(horn);
    }

    // Augen (+Z, gelb -> rot in Phase 2)
    for (const side of [-1, 1]) {
      const eye = new THREE.Mesh(new THREE.SphereGeometry(0.09, 8, 6), this.eyeMat);
      eye.position.set(side * 0.2, 2.12, 0.4);
      this.registerFadeMesh(eye);
      this.model.add(eye);
    }

    // Stirnpanzer vorn
    const brow = new THREE.Mesh(new THREE.BoxGeometry(0.6, 0.22, 0.08), metalMat);
    brow.position.set(0, 2.24, 0.38);
    this.registerFadeMesh(brow);
    this.model.add(brow);

    // --- Effekte an den Body haengen (nicht skaliert, drehen aber mit) ---

    // Stun-Sterne um den Kopf
    this.starsGroup = new THREE.Group();
    const starGeom = new THREE.OctahedronGeometry(0.14);
    const starMat = new THREE.MeshBasicMaterial({ color: 0xffdd33 });
    for (let i = 0; i < 4; i++) {
      const a = (i / 4) * Math.PI * 2;
      const star = new THREE.Mesh(starGeom, starMat);
      star.position.set(Math.cos(a) * 1.4, 3.7, Math.sin(a) * 1.4);
      this.starsGroup.add(star);
    }
    this.starsGroup.visible = false;
    this.body.add(this.starsGroup);

    // Axt-Telegraf-Bogen (Trefferzone) auf dem Boden.
    // Achtung: RingGeometry-Winkel starten an lokaler +X -> um 90° versetzen,
    // damit der Halbkreis nach vorn (+Z, Blickrichtung) zeigt.
    const arcMat = new THREE.MeshBasicMaterial({
      color: 0xff4444,
      transparent: true,
      opacity: 0,
      side: THREE.DoubleSide,
      depthWrite: false,
    });
    const arcGeom = new THREE.RingGeometry(
      SWING_RANGE - 0.7, SWING_RANGE - 0.2, 48, 1,
      -SWING_ARC - Math.PI / 2, SWING_ARC * 2
    );
    this.telegraphArc = new THREE.Mesh(arcGeom, arcMat);
    this.telegraphArc.rotation.x = -Math.PI / 2;
    this.telegraphArc.position.y = 0.05;
    this.telegraphArc.visible = false;
    this.body.add(this.telegraphArc);

    // Axt-Sweep-Visual: heller Bogen fegt von rechts nach links ueber den Boden
    const swingArcMat = new THREE.MeshBasicMaterial({
      color: 0xffaa88,
      transparent: true,
      opacity: 0,
      side: THREE.DoubleSide,
      depthWrite: false,
    });
    this.swingArc = new THREE.Mesh(arcGeom, swingArcMat);
    this.swingArc.rotation.x = -Math.PI / 2;
    this.swingArc.position.y = 0.05;
    this.swingArc.visible = false;
    this.body.add(this.swingArc);

    // Dash-Richtungslinie
    const lineMat = new THREE.MeshBasicMaterial({
      color: 0xff3333,
      transparent: true,
      opacity: 0,
      depthWrite: false,
    });
    this.dashLine = new THREE.Mesh(new THREE.BoxGeometry(0.14, 0.02, 12), lineMat);
    this.dashLine.position.y = 0.06;
    this.dashLine.position.z = 8;
    this.dashLine.visible = false;
    this.body.add(this.dashLine);

    // Schockwellen-Ring
    this.shockRingMat = new THREE.MeshBasicMaterial({
      color: 0xff5533,
      transparent: true,
      opacity: 0,
      side: THREE.DoubleSide,
      depthWrite: false,
    });
    this.shockRing = new THREE.Mesh(new THREE.RingGeometry(0.9, 1.0, 48), this.shockRingMat);
    this.shockRing.rotation.x = -Math.PI / 2;
    this.shockRing.position.y = 0.06;
    this.shockRing.visible = false;
    this.body.add(this.shockRing);
  }

  playAnim() {
    // Prozedurale Optik
  }

  updateAnimation(dt: number) {
    this.updateFadeDeath(dt, { duration: 1.2, sink: 0.5 });
  }

  update(dt: number, game: Game) {
    this.game = game;
    this.waveTime += dt;

    // Hit-Feedback: kurz weiss aufblitzen + Puls
    if (this.hitFlash > 0) {
      this.hitFlash -= dt;
      this.furMat.color.set(0xffffff);
      this.darkMat.color.set(0xffffff);
      this.model.scale.setScalar(MODEL_SCALE * (1 + (this.hitFlash / HIT_FLASH_TIME) * 0.05));
    } else {
      this.furMat.color.set(0x6b4a2f);
      this.darkMat.color.set(0x4a3220);
      this.model.scale.setScalar(MODEL_SCALE);
    }

    // Phase 2 ab 40% HP
    if (!this.enraged && this.health < MAX_HEALTH * ENRAGE_RATIO) {
      this.enraged = true;
      this.eyeMat.color.set(0xff2222);
      playSound('damage', 0.9);
      game.triggerCameraShake(0.35, 0.5);
    }

    switch (this.state) {
      case 'idle': this.updateIdle(dt, game); break;
      case 'dashwindup': this.updateDashWindup(dt, game); break;
      case 'dash': this.updateDash(dt, game); break;
      case 'dashstop': this.updateDashStop(dt); break;
      case 'stunned': this.updateStunned(dt); break;
      case 'swingwindup': this.updateSwingWindup(dt, game); break;
      case 'swing': this.updateSwing(dt, game); break;
      case 'jump': this.updateJump(dt, game); break;
    }

    // Stun-Sterne animieren
    this.starsGroup.visible = this.state === 'stunned';
    if (this.starsGroup.visible) {
      this.starsGroup.rotation.y += dt * 3;
      for (const star of this.starsGroup.children) {
        star.rotation.x += dt * 5;
        star.rotation.y += dt * 4;
        star.position.y = 3.7 + Math.sin(this.waveTime * 6) * 0.1;
      }
    }

    // Bein-Animation: Laufen / Dash-Haltung / sonst still
    if (this.state === 'dash' || this.state === 'dashwindup') {
      for (const leg of this.legs) leg.rotation.x = 0.5;
    } else if (this.state === 'jump') {
      for (const leg of this.legs) leg.rotation.x = 0.25;
    } else if (this.state === 'idle' && this.moving) {
      const step = Math.sin(this.waveTime * 9);
      this.legs[0].rotation.x = step * 0.55;
      this.legs[1].rotation.x = -step * 0.55;
    } else {
      for (const leg of this.legs) leg.rotation.x = 0;
    }

    // Schockwellen-Ring expandieren
    if (this.shockRing.visible) {
      const s = this.shockRing.scale.x + dt * 26;
      this.shockRing.scale.setScalar(s);
      this.shockRingMat.opacity = Math.max(0, 0.5 - (s / 11) * 0.5);
      if (s >= 11) this.shockRing.visible = false;
    }

    // Felsen
    for (let i = this.rocks.length - 1; i >= 0; i--) {
      if (!this.rocks[i].update(dt, game)) {
        this.rocks[i].dispose();
        this.rocks.splice(i, 1);
      }
    }

    // Wandrisse verblassen
    for (let i = this.cracks.length - 1; i >= 0; i--) {
      const c = this.cracks[i];
      c.life -= dt;
      const op = Math.max(0, c.life / 2.5) * 0.6;
      c.group.traverse((obj) => {
        if (obj instanceof THREE.Mesh) {
          (obj.material as THREE.Material & { opacity: number }).opacity = op;
        }
      });
      if (c.life <= 0) {
        game.scene.remove(c.group);
        this.disposeGroup(c.group);
        this.cracks.splice(i, 1);
      }
    }
  }

  private updateIdle(dt: number, game: Game) {
    const playerPos = game.player.body.position;
    const dx = playerPos.x - this.body.position.x;
    const dz = playerPos.z - this.body.position.z;
    const dist = Math.hypot(dx, dz);

    // Turn-Lag: langsam zum Spieler drehen
    const target = Math.atan2(dx, dz);
    const turnSpeed = this.enraged ? TURN_SPEED_2 : TURN_SPEED_1;
    this.yaw = this.lerpAngle(this.yaw, target, Math.min(1, turnSpeed * dt));
    this.body.rotation.y = this.yaw;

    // Abstand halten: nie zu nah, nie zu weit
    this.moving = false;
    const moveDir = new THREE.Vector3();
    if (dist > IDEAL_DIST + 1 && dist > 0.01) {
      moveDir.set(dx / dist, 0, dz / dist);
      this.moving = true;
    } else if (dist < MIN_DIST && dist > 0.01) {
      moveDir.set(-dx / dist, 0, -dz / dist);
      this.moving = true;
    }
    if (moveDir.lengthSq() > 0) {
      this.body.position.x += moveDir.x * MOVE_SPEED * dt;
      this.body.position.z += moveDir.z * MOVE_SPEED * dt;
      game.clampToArena(this.body.position);
    }

    this.dashTimer -= dt;
    this.swingTimer -= dt;
    this.stompTimer -= dt;
    if (this.dashTimer <= 0) {
      this.dashTimer = this.enraged ? DASH_COOLDOWN_2 : DASH_COOLDOWN;
      this.doubleDashLeft = this.enraged ? 1 : 0;
      this.startDashWindup(game);
    } else if (this.swingTimer <= 0) {
      this.swingTimer = this.enraged ? SWING_COOLDOWN_2 : SWING_COOLDOWN;
      this.startSwingWindup();
    } else if (this.stompTimer <= 0) {
      this.stompTimer = this.enraged ? STOMP_COOLDOWN_2 : STOMP_COOLDOWN;
      this.startJump(game);
    }
  }

  // --- Dash ---

  private startDashWindup(game: Game) {
    this.state = 'dashwindup';
    this.stateTime = DASH_WINDUP;
    this.dashDir.set(
      game.player.body.position.x - this.body.position.x,
      0,
      game.player.body.position.z - this.body.position.z
    );
    if (this.dashDir.lengthSq() < 0.01) this.dashDir.set(0, 0, 1);
    this.dashDir.normalize();
    this.dashHitPlayer = false;
    this.yaw = Math.atan2(this.dashDir.x, this.dashDir.z);
    this.body.rotation.y = this.yaw;
    this.dashLine.visible = true;
    playSound('spawn', 0.6);
  }

  private updateDashWindup(dt: number, game: Game) {
    this.stateTime -= dt;
    (this.dashLine.material as THREE.MeshBasicMaterial).opacity =
      0.3 + 0.4 * Math.abs(Math.sin(this.waveTime * 14));
    if (this.stateTime <= 0) {
      this.dashLine.visible = false;
      this.state = 'dash';
      this.dashDist = 0;
      playSound('spawn', 0.8);
    }
  }

  private updateDash(dt: number, game: Game) {
    const step = DASH_SPEED * dt;
    this.dashDist += step;
    this.body.position.x += this.dashDir.x * step;
    this.body.position.z += this.dashDir.z * step;

    if (!this.dashHitPlayer && game.player.health > 0) {
      const dx = game.player.body.position.x - this.body.position.x;
      const dz = game.player.body.position.z - this.body.position.z;
      if (Math.hypot(dx, dz) < 1.7) {
        this.dashHitPlayer = true;
        game.player.damage(DASH_DAMAGE, this.body.position);
        playSound('damage', 0.9);
      }
    }

    if (Math.abs(this.body.position.x) > WALL_LIMIT || Math.abs(this.body.position.z) > WALL_LIMIT) {
      game.clampToArena(this.body.position);
      this.hitWall(game);
      return;
    }
    if (this.dashDist >= DASH_MAX_DIST) {
      this.endDash(game);
    }
  }

  private endDash(game: Game) {
    if (this.doubleDashLeft > 0) {
      this.doubleDashLeft--;
      this.startDashWindup(game);
    } else {
      this.state = 'dashstop';
      this.stateTime = 0.3;
    }
  }

  private updateDashStop(dt: number) {
    this.stateTime -= dt;
    if (this.stateTime <= 0) this.state = 'idle';
  }

  private hitWall(game: Game) {
    this.state = 'stunned';
    this.stateTime = BOSS_STUN_TIME;
    this.spawnWallCracks(game);
    playSound('explosion', 0.5);
  }

  private updateStunned(dt: number) {
    this.stateTime -= dt;
    // Schwindel-Wackeln
    this.model.rotation.z = Math.sin(this.waveTime * 20) * 0.08;
    if (this.stateTime <= 0) {
      this.model.rotation.z = 0;
      this.state = 'idle';
    }
  }

  private spawnWallCracks(game: Game) {
    const group = new THREE.Group();
    const crackMat = new THREE.MeshBasicMaterial({
      color: 0x111111,
      transparent: true,
      opacity: 0.6,
      depthWrite: false,
    });
    const onXWall = Math.abs(this.body.position.x) > Math.abs(this.body.position.z);
    for (let i = 0; i < 4; i++) {
      const len = 0.8 + Math.random() * 1.2;
      const geo = new THREE.BoxGeometry(
        onXWall ? 0.06 : len,
        0.06,
        onXWall ? len : 0.06
      );
      const crack = new THREE.Mesh(geo, crackMat);
      if (onXWall) {
        crack.position.set(
          Math.sign(this.body.position.x) * 49.2,
          0.5 + Math.random() * 1.4,
          this.body.position.z + (Math.random() - 0.5) * 2.4
        );
      } else {
        crack.position.set(
          this.body.position.x + (Math.random() - 0.5) * 2.4,
          0.5 + Math.random() * 1.4,
          Math.sign(this.body.position.z) * 49.2
        );
      }
      crack.rotation.y = Math.random() * Math.PI;
      group.add(crack);
    }
    game.scene.add(group);
    this.cracks.push({ group, life: 2.5 });
  }

  // --- Axt-Schwung ---

  private startSwingWindup() {
    this.state = 'swingwindup';
    this.stateTime = SWING_WINDUP;
    this.swingHit = false;
    // Axt anheben (leicht), bereit fuer den horizontalen Sweep
    this.axeGroup.rotation.x = -0.35;
    this.axeGroup.rotation.y = 1.0;
    this.telegraphArc.visible = true;
    playSound('spawn', 0.5);
  }

  private updateSwingWindup(dt: number, game: Game) {
    this.stateTime -= dt;
    (this.telegraphArc.material as THREE.MeshBasicMaterial).opacity =
      0.3 + 0.4 * Math.abs(Math.sin(this.waveTime * 12));
    if (this.stateTime <= 0) {
      this.telegraphArc.visible = false;
      this.state = 'swing';
      this.stateTime = SWING_TIME;
    }
  }

  private updateSwing(dt: number, game: Game) {
    this.stateTime -= dt;
    const t = 1 - this.stateTime / SWING_TIME;

    // Axt fegt horizontal von rechts nach links vor dem Koerper
    this.axeGroup.rotation.y = 1.0 - t * 2.0;
    this.axeGroup.rotation.x = -0.35 + t * 0.35;

    // Hell: Halbkreis fegt ueber den Boden
    this.swingArc.visible = true;
    this.swingArc.rotation.y = SWING_ARC * 0.85 * (1 - 2 * t);
    const fade = Math.min(1, t * 6, (1 - t) * 6);
    (this.swingArc.material as THREE.MeshBasicMaterial).opacity = fade * 0.7;

    // Treffer in der Mitte des Schwungs
    if (!this.swingHit && t >= 0.35) {
      this.swingHit = true;
      const playerPos = game.player.body.position;
      const dx = playerPos.x - this.body.position.x;
      const dz = playerPos.z - this.body.position.z;
      const dist = Math.hypot(dx, dz);
      if (dist < SWING_RANGE) {
        const ang = Math.atan2(dx, dz);
        // Ueberspringbar: Treffer nur am Boden
        if (Math.abs(this.angleDiff(this.yaw, ang)) < SWING_ARC && playerPos.y <= 0.6) {
          game.player.damage(SWING_DAMAGE, this.body.position);
          playSound('damage', 0.9);
        }
      }
    }

    if (this.stateTime <= 0) {
      this.axeGroup.rotation.x = 0;
      this.axeGroup.rotation.y = 0;
      this.swingArc.visible = false;
      this.state = 'idle';
    }
  }

  // --- Sprung + Stampf ---

  private startJump(game: Game) {
    this.state = 'jump';
    this.stateTime = STOMP_JUMP_TIME;
    this.jumpFrom.copy(this.body.position);
    this.jumpTo.set(game.player.body.position.x, 0, game.player.body.position.z);
    playSound('spawn', 0.7);
  }

  private updateJump(dt: number, game: Game) {
    this.stateTime -= dt;
    const t = 1 - Math.max(0, this.stateTime) / STOMP_JUMP_TIME;
    this.body.position.x = this.jumpFrom.x + (this.jumpTo.x - this.jumpFrom.x) * t;
    this.body.position.z = this.jumpFrom.z + (this.jumpTo.z - this.jumpFrom.z) * t;
    this.body.position.y = 0.15 + Math.sin(t * Math.PI) * STOMP_JUMP_HEIGHT;

    if (this.stateTime <= 0) {
      this.body.position.y = 0.15;
      this.landStomp(game);
    }
  }

  private landStomp(game: Game) {
    // Schockwelle: stunnt den Spieler (ueberspringbar)
    this.shockRing.visible = true;
    this.shockRing.scale.setScalar(0.2);
    this.shockRingMat.opacity = 0.5;
    playSound('explosion', 0.7);

    const playerPos = game.player.body.position;
    const dist = Math.hypot(
      playerPos.x - this.body.position.x,
      playerPos.z - this.body.position.z
    );
    if (dist < STOMP_RADIUS && playerPos.y <= 0.6) {
      game.player.stunTime = STOMP_STUN;
      game.player.damage(STOMP_DAMAGE, this.body.position);
    }

    // Steinschlag um den Spieler
    const count = this.enraged ? ROCK_COUNT_2 : ROCK_COUNT;
    for (let i = 0; i < count; i++) {
      const a = Math.random() * Math.PI * 2;
      const r = 2 + Math.random() * 6;
      const rock = new FallingRock(new THREE.Vector3(
        playerPos.x + Math.cos(a) * r,
        0,
        playerPos.z + Math.sin(a) * r
      ));
      game.scene.add(rock.body);
      this.rocks.push(rock);
    }

    this.state = 'idle';
  }

  // --- Schaden ---

  override takeDamage(
    damage: number,
    game: Game,
    withKnockback = true,
    sourcePos?: THREE.Vector3
  ) {
    const src = sourcePos ?? game.player.body.position;
    const fwd = new THREE.Vector3(Math.sin(this.yaw), 0, Math.cos(this.yaw));
    const incoming = new THREE.Vector3().subVectors(src, this.body.position);
    incoming.y = 0;
    if (incoming.lengthSq() < 0.0001) incoming.copy(fwd);
    incoming.normalize();

    // Nur von hinten verletzbar: vorn wird geblockt
    if (fwd.dot(incoming) > 0.15) {
      game.spawnDamageText('BLOCK', this.body.position, '#99aabb');
      playSound('sword_hit1', 0.6);
      return;
    }

    super.takeDamage(damage, game, withKnockback, sourcePos);
    this.hitFlash = HIT_FLASH_TIME;
  }

  protected onDeath(_game: Game) {
    this.cleanupEffects();
    this.beginFadeDeath();
  }

  override dispose() {
    super.dispose();
    this.cleanupEffects();
  }

  private cleanupEffects() {
    this.starsGroup.visible = false;
    this.dashLine.visible = false;
    this.telegraphArc.visible = false;
    this.swingArc.visible = false;
    this.shockRing.visible = false;
    this.furMat.color.set(0x6b4a2f);
    this.darkMat.color.set(0x4a3220);
    for (const rock of this.rocks) rock.dispose();
    this.rocks = [];
    if (this.game) {
      for (const c of this.cracks) {
        this.game.scene.remove(c.group);
        this.disposeGroup(c.group);
      }
    }
    this.cracks = [];
  }

  private disposeGroup(group: THREE.Group) {
    group.traverse((obj) => {
      if (obj instanceof THREE.Mesh) {
        obj.geometry.dispose();
        (obj.material as THREE.Material).dispose();
      }
    });
  }

  private lerpAngle(a: number, b: number, t: number): number {
    return a + this.angleDiff(a, b) * t;
  }

  private angleDiff(a: number, b: number): number {
    let diff = b - a;
    while (diff > Math.PI) diff -= Math.PI * 2;
    while (diff < -Math.PI) diff += Math.PI * 2;
    return diff;
  }
}
