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

const MAX_HEALTH = 16000;
const HIT_FLASH_TIME = 0.14;
const MODEL_SCALE = 1.6;

const FLY_TIME = 4.5;
const FLY_TIME_2 = 3.5;
const FLY_HEIGHT = 7;
const FLY_RADIUS = 9;
const FLY_FIREBALL_CD = 1.8;

const DIVEWINDUP_TIME = 1.0;
const DIVEWINDUP_TIME_2 = 0.7;
const DIVE_TIME = 0.9;
const DIVE_IMPACT_RADIUS = 2.8;
const DIVE_DAMAGE = 30;
const DIVERECOVER_TIME = 0.4;

const LANDED_TIME = 4.0;
const LANDED_TIME_2 = 2.5;
const TAKEOFF_TIME = 0.8;
const TAKEOFF_HEIGHT = 6;

const BREATH_WINDUP = 0.9;
const BREATH_TIME = 1.1;
const BREATH_RANGE = 11;
const BREATH_HALF_ANGLE = 0.65;
const BREATH_DAMAGE = 35;

const FIREBALL_SPREAD = 0.35;
const FIREBALL_SPEED = 13;
const FIREBALL_GRAVITY = 6;
const FIREBALL_HOMING = 3.2;
const FIREBALL_DAMAGE = 20;
const FIREBALL_HIT_RADIUS = 1.1;
const FIREBALL_COUNT = 3;
const FIREBALL_COUNT_2 = 5;

const ENRAGE_RATIO = 0.35;
const WALL_LIMIT = 48;

type State = 'fly' | 'divewindup' | 'dive' | 'diverecover' | 'landed' | 'takeoff';

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

  private skinMat!: THREE.MeshToonMaterial;
  private darkMat!: THREE.MeshToonMaterial;
  private bellyMat!: THREE.MeshToonMaterial;
  private wingMat!: THREE.MeshToonMaterial;
  private eyeMat!: THREE.MeshBasicMaterial;
  private shadow!: THREE.Mesh;
  private shadowMat!: THREE.MeshBasicMaterial;

  private wingL!: THREE.Group;
  private wingR!: THREE.Group;
  private jaw!: THREE.Group;
  private tailGroup!: THREE.Group;
  private breathGroup!: THREE.Group;
  private breathMat!: THREE.MeshBasicMaterial;
  private telegraphArc!: THREE.Mesh;
  private telegraphArcMat!: THREE.MeshBasicMaterial;

  private flyAngle = Math.random() * Math.PI * 2;
  private flyFireballTimer = 1.5;
  private diveTarget = new THREE.Vector3();
  private diveFrom = new THREE.Vector3();
  private diveShadow!: THREE.Mesh;
  private diveShadowMat!: THREE.MeshBasicMaterial;
  private diveShadowActive = false;
  private diveShadowAdded = false;
  private breathHit = false;
  private breathActive = false;

  private fireballs: DragonFireball[] = [];
  private impactFlashes: { mesh: THREE.Mesh; mat: THREE.MeshBasicMaterial; life: number }[] = [];
  private breathPhase = 'idle';
  private breathTime = 0;

  constructor() {
    super();
    this.health = MAX_HEALTH;
    this.maxHealth = MAX_HEALTH;
    this.isBoss = true;
    this.displayName = 'Drache';
    this.speed = 0;
    this.xp = 400;
    this.contactDps = 30;
    this.contactRadius = 3.6;
    this.guaranteedDrop = true;

    this.model = new THREE.Group();
    this.model.scale.setScalar(MODEL_SCALE);
    this.fadeOutModel = this.model;
    this.body.add(this.model);
    this.stateTime = FLY_TIME;

    this.skinMat = new THREE.MeshToonMaterial({ color: 0xbb4433 });
    this.darkMat = new THREE.MeshToonMaterial({ color: 0x772a1e });
    this.bellyMat = new THREE.MeshToonMaterial({ color: 0xe8c890 });
    this.wingMat = new THREE.MeshToonMaterial({ color: 0x993322, side: THREE.DoubleSide });
    this.eyeMat = new THREE.MeshBasicMaterial({ color: 0xffcc33 });
    const boneMat = new THREE.MeshToonMaterial({ color: 0xf0e6cf });
    let boneFadeRegistered = false;
    const registerBone = (mesh: THREE.Mesh) => {
      // Knochen-Material einmal fürs Fade-Death registrieren (wird geteilt)
      if (!boneFadeRegistered) {
        this.registerFadeMesh(mesh);
        boneFadeRegistered = true;
      }
    };

    // Hilfspunkt: Strebe/Knochen zwischen zwei Punkten
    const strut = (
      a: THREE.Vector3, b: THREE.Vector3, r1: number, r2: number
    ): THREE.Mesh => {
      const dir = new THREE.Vector3().subVectors(b, a);
      const len = dir.length();
      const mesh = new THREE.Mesh(new THREE.CylinderGeometry(r2, r1, len, 6), this.darkMat);
      mesh.position.copy(a).addScaledVector(dir, 0.5);
      mesh.quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), dir.normalize());
      return mesh;
    };

    // --- Körper (Blickrichtung +Z) ---

    // Brust, Rumpfmitte, Hüfte (überlappende Kugeln für organische Silhouette)
    const chest = new THREE.Mesh(new THREE.SphereGeometry(0.8, 18, 12), this.skinMat);
    chest.scale.set(0.95, 0.92, 1.05);
    chest.position.set(0, 1.4, 0.85);
    chest.castShadow = true;
    this.registerFadeMesh(chest);
    this.model.add(chest);

    const torso = new THREE.Mesh(new THREE.SphereGeometry(1.0, 20, 14), this.skinMat);
    torso.scale.set(1, 0.85, 1.5);
    torso.position.set(0, 1.5, -0.1);
    torso.castShadow = true;
    this.registerFadeMesh(torso);
    this.model.add(torso);

    const hips = new THREE.Mesh(new THREE.SphereGeometry(0.85, 18, 12), this.skinMat);
    hips.scale.set(1.0, 0.9, 1.1);
    hips.position.set(0, 1.45, -0.85);
    hips.castShadow = true;
    this.registerFadeMesh(hips);
    this.model.add(hips);

    // Bauchplatten (hell, segmentiert)
    for (let i = 0; i < 5; i++) {
      const plate = new THREE.Mesh(
        new THREE.SphereGeometry(0.58 - i * 0.06, 14, 8),
        this.bellyMat
      );
      plate.scale.set(0.95, 0.5, 0.8);
      plate.position.set(0, 1.02 - i * 0.02, 1.15 - i * 0.62);
      this.registerFadeMesh(plate);
      this.model.add(plate);
    }

    // Schwanz (geschwungen, mit Zacken und Spadel-Spitze)
    this.tailGroup = new THREE.Group();
    this.tailGroup.position.set(0, 1.35, -1.5);
    this.model.add(this.tailGroup);
    for (let i = 0; i < 7; i++) {
      const radius = 0.46 - i * 0.055;
      const seg = new THREE.Mesh(
        new THREE.SphereGeometry(radius, 12, 8),
        i % 2 === 0 ? this.skinMat : this.darkMat
      );
      const y = -0.13 * i + Math.max(0, i - 3) * 0.18;
      seg.position.set(0, y, -0.15 - i * 0.45);
      seg.scale.set(1, 0.92, 1.15);
      this.registerFadeMesh(seg);
      this.tailGroup.add(seg);
      if (i % 2 === 0 && i < 5) {
        const spike = new THREE.Mesh(
          new THREE.ConeGeometry(0.09 - i * 0.012, 0.32 - i * 0.03, 5),
          this.darkMat
        );
        spike.position.set(0, y + radius * 0.9 + 0.1, -0.15 - i * 0.45);
        spike.rotation.x = -0.3;
        this.registerFadeMesh(spike);
        this.tailGroup.add(spike);
      }
    }
    const tailTip = new THREE.Mesh(new THREE.ConeGeometry(0.3, 0.9, 4), this.darkMat);
    tailTip.rotation.x = -Math.PI / 2;
    tailTip.scale.x = 0.4;
    tailTip.position.set(0, -0.35, -3.3);
    this.registerFadeMesh(tailTip);
    this.tailGroup.add(tailTip);

    // Hals (S-Kurve nach oben)
    for (const [y, z, r] of [[1.95, 1.3, 0.46], [2.2, 1.55, 0.4]]) {
      const neck = new THREE.Mesh(new THREE.SphereGeometry(r, 14, 10), this.skinMat);
      neck.scale.set(0.85, 0.95, 1.1);
      neck.position.set(0, y, z);
      this.registerFadeMesh(neck);
      this.model.add(neck);
    }

    // Kopf-Gruppe (+Z)
    const headGroup = new THREE.Group();
    headGroup.position.set(0, 2.4, 1.8);
    headGroup.rotation.x = 0.06;
    this.model.add(headGroup);

    const skull = new THREE.Mesh(new THREE.SphereGeometry(0.55, 16, 12), this.skinMat);
    skull.scale.set(0.95, 0.9, 1.15);
    skull.castShadow = true;
    this.registerFadeMesh(skull);
    headGroup.add(skull);

    // Schnauze (+Z)
    const snout = new THREE.Mesh(new THREE.BoxGeometry(0.44, 0.32, 0.7), this.skinMat);
    snout.position.set(0, -0.1, 0.72);
    this.registerFadeMesh(snout);
    headGroup.add(snout);

    // Stirnleiste (grimmiger Blick)
    const brow = new THREE.Mesh(new THREE.BoxGeometry(0.62, 0.14, 0.26), this.darkMat);
    brow.position.set(0, 0.3, 0.36);
    brow.rotation.x = 0.12;
    this.registerFadeMesh(brow);
    headGroup.add(brow);

    // Augen (+Z, ragen aus dem Kopf heraus)
    for (const side of [-1, 1]) {
      const eye = new THREE.Mesh(new THREE.SphereGeometry(0.12, 8, 6), this.eyeMat);
      eye.position.set(side * 0.3, 0.2, 0.48);
      headGroup.add(eye);
    }

    // Hörner (nach hinten geschwungen, zwei Paare)
    for (const side of [-1, 1]) {
      const horn = new THREE.Mesh(new THREE.ConeGeometry(0.09, 0.75, 6), this.darkMat);
      horn.position.set(side * 0.28, 0.42, -0.12);
      horn.rotation.set(-1.15, 0, side * 0.35);
      this.registerFadeMesh(horn);
      headGroup.add(horn);
      const horn2 = new THREE.Mesh(new THREE.ConeGeometry(0.06, 0.4, 6), this.darkMat);
      horn2.position.set(side * 0.17, 0.3, -0.34);
      horn2.rotation.set(-1.3, 0, side * 0.25);
      this.registerFadeMesh(horn2);
      headGroup.add(horn2);
    }

    // Ohr-Fransen (seitlich nach hinten)
    for (const side of [-1, 1]) {
      const frill = new THREE.Mesh(new THREE.ConeGeometry(0.12, 0.45, 5), this.darkMat);
      frill.position.set(side * 0.48, 0.1, -0.18);
      frill.rotation.set(0.35, 0, side * -1.75);
      frill.scale.set(1, 1, 0.5);
      this.registerFadeMesh(frill);
      headGroup.add(frill);
    }

    // Nüstern
    for (const side of [-1, 1]) {
      const nostril = new THREE.Mesh(new THREE.SphereGeometry(0.04, 6, 4), this.darkMat);
      nostril.position.set(side * 0.1, 0.06, 1.0);
      headGroup.add(nostril);
    }

    // Zähne im Oberkiefer (zeigen nach unten)
    for (const side of [-1, 1]) {
      for (const z of [0.72, 0.88, 1.02]) {
        const tooth = new THREE.Mesh(new THREE.ConeGeometry(0.032, 0.11, 5), boneMat);
        tooth.rotation.x = Math.PI;
        tooth.position.set(side * 0.15, -0.3, z);
        registerBone(tooth);
        headGroup.add(tooth);
      }
    }

    // Kiefer (beweglich, öffnet sich beim Feuerspeer)
    this.jaw = new THREE.Group();
    this.jaw.position.set(0, -0.28, 0.28);
    const jawMesh = new THREE.Mesh(new THREE.BoxGeometry(0.38, 0.16, 0.62), this.darkMat);
    jawMesh.position.set(0, -0.05, 0.3);
    this.registerFadeMesh(jawMesh);
    this.jaw.add(jawMesh);
    for (const side of [-1, 1]) {
      for (const z of [0.42, 0.56]) {
        const tooth = new THREE.Mesh(new THREE.ConeGeometry(0.028, 0.09, 5), boneMat);
        tooth.position.set(side * 0.11, 0.06, z);
        registerBone(tooth);
        this.jaw.add(tooth);
      }
    }
    this.jaw.rotation.x = 0.12;
    headGroup.add(this.jaw);

    // Rückenplatten (vom Nacken bis in den Schwanz)
    const backPlates: [number, number, number, number][] = [
      [2.25, 0.95, 0.15, 0.48],
      [2.38, 0.35, 0.19, 0.6],
      [2.4, -0.25, 0.2, 0.64],
      [2.32, -0.85, 0.17, 0.52],
      [1.9, -1.5, 0.14, 0.44],
      [1.62, -2.4, 0.12, 0.38],
      [1.45, -3.3, 0.1, 0.32],
    ];
    for (const [y, z, r, h] of backPlates) {
      const plate = new THREE.Mesh(new THREE.ConeGeometry(r, h, 5), this.darkMat);
      plate.position.set(0, y, z);
      plate.rotation.x = -0.22;
      this.registerFadeMesh(plate);
      this.model.add(plate);
    }

    // Beine (Hinterbeine kräftiger, Vorderbeine schlanker)
    for (const side of [-1, 1]) {
      // Hinterbein
      const thigh = new THREE.Mesh(new THREE.SphereGeometry(0.5, 12, 10), this.skinMat);
      thigh.scale.set(0.75, 1.1, 0.95);
      thigh.position.set(side * 0.72, 1.0, -0.8);
      this.registerFadeMesh(thigh);
      this.model.add(thigh);
      const shin = new THREE.Mesh(new THREE.CylinderGeometry(0.13, 0.19, 0.75, 8), this.darkMat);
      shin.position.set(side * 0.8, 0.45, -0.9);
      shin.rotation.x = 0.12;
      this.registerFadeMesh(shin);
      this.model.add(shin);
      const hindFoot = new THREE.Mesh(new THREE.BoxGeometry(0.42, 0.16, 0.6), this.skinMat);
      hindFoot.position.set(side * 0.8, 0.09, -0.78);
      this.registerFadeMesh(hindFoot);
      this.model.add(hindFoot);
      for (const dx of [-0.14, 0, 0.14]) {
        const claw = new THREE.Mesh(new THREE.ConeGeometry(0.05, 0.2, 5), boneMat);
        claw.rotation.x = Math.PI / 2;
        claw.position.set(side * 0.8 + dx, 0.09, -0.44);
        registerBone(claw);
        this.model.add(claw);
      }

      // Vorderbein
      const upperArm = new THREE.Mesh(new THREE.SphereGeometry(0.3, 12, 8), this.skinMat);
      upperArm.scale.set(0.8, 1.15, 0.9);
      upperArm.position.set(side * 0.62, 1.05, 0.8);
      this.registerFadeMesh(upperArm);
      this.model.add(upperArm);
      const foreArm = new THREE.Mesh(new THREE.CylinderGeometry(0.1, 0.15, 0.7, 8), this.darkMat);
      foreArm.position.set(side * 0.6, 0.5, 0.85);
      this.registerFadeMesh(foreArm);
      this.model.add(foreArm);
      const foreFoot = new THREE.Mesh(new THREE.BoxGeometry(0.34, 0.14, 0.5), this.skinMat);
      foreFoot.position.set(side * 0.6, 0.08, 0.95);
      this.registerFadeMesh(foreFoot);
      this.model.add(foreFoot);
      for (const dx of [-0.11, 0, 0.11]) {
        const claw = new THREE.Mesh(new THREE.ConeGeometry(0.045, 0.18, 5), boneMat);
        claw.rotation.x = Math.PI / 2;
        claw.position.set(side * 0.6 + dx, 0.08, 1.24);
        registerBone(claw);
        this.model.add(claw);
      }
    }

    // --- Flügel (Fledermaus-Silhouette mit Membran und Fingerknochen) ---
    const wingShape = new THREE.Shape();
    wingShape.moveTo(0.2, 0.1);
    wingShape.quadraticCurveTo(0.8, -0.12, 1.5, 0.0); // Vorderkante zum Handgelenk
    wingShape.quadraticCurveTo(2.5, 0.0, 3.05, 0.95); // zur Flügelspitze
    // Hinterkante: Fingerspitzen mit konkaven Bögen dazwischen
    wingShape.quadraticCurveTo(2.2, 1.0, 2.7, 1.65);
    wingShape.quadraticCurveTo(1.7, 1.45, 2.0, 2.1);
    wingShape.quadraticCurveTo(1.05, 1.6, 1.1, 2.25);
    wingShape.quadraticCurveTo(0.4, 1.2, 0.15, 1.5);
    wingShape.closePath();
    const wingGeom = new THREE.ShapeGeometry(wingShape, 10);

    const buildWing = (): THREE.Group => {
      const wing = new THREE.Group();
      const membrane = new THREE.Mesh(wingGeom, this.wingMat);
      membrane.rotation.x = -Math.PI / 2;
      membrane.castShadow = true;
      this.registerFadeMesh(membrane);
      wing.add(membrane);

      // Flügelknochen: Arm, Außenkante, ein Fingerstrahl
      const bones = [
        strut(new THREE.Vector3(0, 0, 0), new THREE.Vector3(1.5, 0.06, 0), 0.07, 0.05),
        strut(new THREE.Vector3(1.5, 0.06, 0), new THREE.Vector3(3.05, 0.1, -0.95), 0.05, 0.028),
        strut(new THREE.Vector3(1.3, 0.03, -0.25), new THREE.Vector3(2.0, 0.05, -2.1), 0.04, 0.02),
      ];
      for (const bone of bones) {
        this.registerFadeMesh(bone);
        wing.add(bone);
      }

      // Daumenkralle am Handgelenk
      const thumb = new THREE.Mesh(new THREE.ConeGeometry(0.045, 0.22, 5), boneMat);
      thumb.rotation.x = Math.PI / 2;
      thumb.position.set(1.55, 0.06, 0.14);
      registerBone(thumb);
      wing.add(thumb);
      return wing;
    };

    this.wingR = buildWing();
    this.wingR.position.set(0.7, 2.25, -0.2);
    this.model.add(this.wingR);

    this.wingL = buildWing();
    this.wingL.position.set(-0.7, 2.25, -0.2);
    this.wingL.scale.x = -1; // spiegeln für linke Seite
    this.model.add(this.wingL);

    // --- Effekte ---

    // Boden-Schatten (folgt dem Drachen)
    this.shadowMat = new THREE.MeshBasicMaterial({
      color: 0x000000,
      transparent: true,
      opacity: 0.3,
    });
    this.shadow = new THREE.Mesh(new THREE.CircleGeometry(2.2, 24), this.shadowMat);
    this.shadow.rotation.x = -Math.PI / 2;
    this.shadow.renderOrder = 998;
    this.body.add(this.shadow);

    // Telegraf-Schatten am Sturz-Ziel (wächst)
    this.diveShadowMat = new THREE.MeshBasicMaterial({
      color: 0x000000,
      transparent: true,
      opacity: 0,
      depthWrite: false,
    });
    this.diveShadow = new THREE.Mesh(new THREE.CircleGeometry(2.4, 24), this.diveShadowMat);
    this.diveShadow.rotation.x = -Math.PI / 2;
    this.diveShadow.position.y = 0.05;
    this.diveShadow.scale.setScalar(0.3);
    this.diveShadow.visible = false;

    // Feuer-Telegraf (Bogen vor dem Drachen) und Feuer-Kegel
    this.telegraphArcMat = new THREE.MeshBasicMaterial({
      color: 0xff6644,
      transparent: true,
      opacity: 0,
      side: THREE.DoubleSide,
      depthWrite: false,
    });
    const arcGeom = new THREE.RingGeometry(
      BREATH_RANGE - 0.8, BREATH_RANGE - 0.2, 48, 1,
      -BREATH_HALF_ANGLE - Math.PI / 2, BREATH_HALF_ANGLE * 2
    );
    this.telegraphArc = new THREE.Mesh(arcGeom, this.telegraphArcMat);
    this.telegraphArc.rotation.x = -Math.PI / 2;
    this.telegraphArc.position.y = 0.05;
    this.telegraphArc.visible = false;
    this.body.add(this.telegraphArc);

    this.breathGroup = new THREE.Group();
    this.breathMat = new THREE.MeshBasicMaterial({
      color: 0xff8833,
      transparent: true,
      opacity: 0,
      blending: THREE.AdditiveBlending,
      depthWrite: false,
      side: THREE.DoubleSide,
    });
    const flame = new THREE.Mesh(new THREE.ConeGeometry(1.8, BREATH_RANGE, 16, 1, true), this.breathMat);
    flame.rotation.x = Math.PI / 2;
    flame.position.z = BREATH_RANGE / 2;
    this.breathGroup.add(flame);
    // auf Maulhöhe und leicht nach unten geneigt
    this.breathGroup.position.y = 3.3;
    this.breathGroup.rotation.x = 0.3;
    this.breathGroup.visible = false;
    this.body.add(this.breathGroup);
  }

  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
    if (this.hitFlash > 0) {
      this.hitFlash -= dt;
      this.skinMat.color.set(0xffffff);
      this.darkMat.color.set(0xffffff);
      this.model.scale.setScalar(MODEL_SCALE * (1 + (this.hitFlash / HIT_FLASH_TIME) * 0.05));
    } else {
      this.skinMat.color.set(this.enraged ? 0x992211 : 0xbb4433);
      this.darkMat.color.set(this.enraged ? 0x551108 : 0x772a1e);
      this.model.scale.setScalar(MODEL_SCALE);
    }

    // Enrage unter 35% HP
    if (!this.enraged && this.health < MAX_HEALTH * ENRAGE_RATIO) {
      this.enraged = true;
      this.eyeMat.color.set(0xff2200);
      playSound('damage', 0.9);
      game.triggerCameraShake(0.35, 0.5);
    }

    // Flügel-Flattern (schneller im Flug)
    const flapSpeed = this.state === 'fly' || this.state === 'takeoff' ? 9 : 2;
    const flap = Math.sin(this.waveTime * flapSpeed) * (this.state === 'fly' ? 0.7 : 0.2);
    this.wingL.rotation.z = flap;
    this.wingR.rotation.z = -flap;
    // Flügel-Einstellwinkel (wegen scale.x-Spiegelung auf beiden Seiten gleich)
    this.wingL.rotation.x = 0.3;
    this.wingR.rotation.x = 0.3;

    // Schwebe-Wippen im Flug und sanftes Schwanz-Schwenken
    const airborne = this.state === 'fly' || this.state === 'takeoff';
    this.model.position.y = airborne ? Math.sin(this.waveTime * 2.3) * 0.15 : 0;
    this.tailGroup.rotation.y = Math.sin(this.waveTime * 1.7) * 0.12;

    // Schatten bleibt am Boden
    this.shadow.position.y = 0.06 - this.body.position.y;
    const shadowScale = 0.6 + this.body.position.y * 0.15;
    this.shadow.scale.setScalar(shadowScale);

    // Sturz-Telegraf-Schatten
    if (this.diveShadowActive) {
      if (!this.diveShadowAdded) {
        game.scene.add(this.diveShadow);
        this.diveShadowAdded = true;
      }
      const t = 1 - this.stateTime / DIVEWINDUP_TIME;
      this.diveShadow.position.set(this.diveTarget.x, 0.05, this.diveTarget.z);
      this.diveShadow.scale.setScalar(0.3 + t * 0.9);
      this.diveShadowMat.opacity = 0.2 + t * 0.35;
    } else {
      this.diveShadow.visible = false;
    }

    switch (this.state) {
      case 'fly': this.updateFly(dt, game); break;
      case 'divewindup': this.updateDiveWindup(dt, game); break;
      case 'dive': this.updateDive(dt, game); break;
      case 'diverecover': this.updateDiveRecover(dt); break;
      case 'landed': this.updateLanded(dt, game); break;
      case 'takeoff': this.updateTakeoff(dt); break;
    }

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

    // Aufprall-Blitzflächen
    for (let i = this.impactFlashes.length - 1; i >= 0; i--) {
      const f = this.impactFlashes[i];
      f.life -= dt;
      const t = 1 - Math.max(0, f.life) / 0.35;
      f.mesh.scale.setScalar(0.5 + t * 7);
      f.mat.opacity = 0.7 * (1 - t);
      if (f.life <= 0) {
        game.scene.remove(f.mesh);
        f.mesh.geometry.dispose();
        f.mat.dispose();
        this.impactFlashes.splice(i, 1);
      }
    }
  }

  // --- Flug ---

  private updateFly(dt: number, game: Game) {
    this.stateTime -= dt;
    this.body.position.y = FLY_HEIGHT;

    // Kreisen über dem Spieler
    const playerPos = game.player.body.position;
    this.flyAngle += dt * 0.8;
    const cx = playerPos.x;
    const cz = playerPos.z;
    const x = cx + Math.cos(this.flyAngle) * FLY_RADIUS;
    const z = cz + Math.sin(this.flyAngle) * FLY_RADIUS;
    this.body.position.x = THREE.MathUtils.clamp(x, -WALL_LIMIT + 3, WALL_LIMIT - 3);
    this.body.position.z = THREE.MathUtils.clamp(z, -WALL_LIMIT + 3, WALL_LIMIT - 3);

    // Flugrichtung anschauen (kreisen)
    const lookX = cx + Math.cos(this.flyAngle + 0.5) * FLY_RADIUS;
    const lookZ = cz + Math.sin(this.flyAngle + 0.5) * FLY_RADIUS;
    this.body.lookAt(lookX, this.body.position.y, lookZ);

    // Feuerbälle abwerfen
    this.flyFireballTimer -= dt;
    if (this.flyFireballTimer <= 0) {
      this.flyFireballTimer = FLY_FIREBALL_CD;
      this.throwFireballs(game);
    }

    if (this.stateTime <= 0) {
      // Sturzangriff vorbereiten
      this.state = 'divewindup';
      this.stateTime = this.enraged ? DIVEWINDUP_TIME_2 : DIVEWINDUP_TIME;
      this.doubleDiveLeft = this.enraged ? 1 : 0;
      this.diveTarget.copy(game.player.body.position);
      this.diveTarget.y = 0;
      this.game?.clampToArena(this.diveTarget);
      this.diveShadow.visible = true;
      this.diveShadow.position.set(this.diveTarget.x, 0.05, this.diveTarget.z);
      this.diveShadowActive = true;
      playSound('spawn', 0.7);
    }
  }

  // --- Sturzangriff ---

  private updateDiveWindup(dt: number, game: Game) {
    this.stateTime -= dt;
    this.body.position.y = FLY_HEIGHT;
    // Über dem Ziel schweben
    this.body.position.x += (this.diveTarget.x - this.body.position.x) * Math.min(1, dt * 4);
    this.body.position.z += (this.diveTarget.z - this.body.position.z) * Math.min(1, dt * 4);
    this.body.lookAt(
      this.diveTarget.x,
      this.body.position.y,
      this.diveTarget.z
    );

    if (this.stateTime <= 0) {
      this.diveShadowActive = false;
      this.diveShadow.visible = false;
      this.state = 'dive';
      this.stateTime = DIVE_TIME;
      this.diveFrom.copy(this.body.position);
      playSound('spawn', 0.9);
    }
  }

  private updateDive(dt: number, game: Game) {
    this.stateTime -= dt;
    const t = 1 - Math.max(0, this.stateTime) / DIVE_TIME;
    const ease = t * t;
    this.body.position.x = this.diveFrom.x + (this.diveTarget.x - this.diveFrom.x) * ease;
    this.body.position.z = this.diveFrom.z + (this.diveTarget.z - this.diveFrom.z) * ease;
    this.body.position.y = FLY_HEIGHT * (1 - ease);

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

  private diveImpact(game: Game) {
    // Aufprall: Schock + Schaden (überspringbar: nur am Boden)
    game.triggerCameraShake(0.4, 0.5);
    playSound('explosion', 0.8);

    const flashMat = new THREE.MeshBasicMaterial({
      color: 0xff8844,
      transparent: true,
      opacity: 0.7,
      side: THREE.DoubleSide,
      depthWrite: false,
    });
    const flash = new THREE.Mesh(new THREE.RingGeometry(0.9, 1.1, 32), flashMat);
    flash.rotation.x = -Math.PI / 2;
    flash.position.set(this.body.position.x, 0.07, this.body.position.z);
    game.scene.add(flash);
    this.impactFlashes.push({ mesh: flash, mat: flashMat, life: 0.35 });

    if (game.player.health > 0 && game.player.body.position.y <= 0.8) {
      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) < DIVE_IMPACT_RADIUS) {
        game.player.damage(DIVE_DAMAGE, this.body.position);
      }
    }

    if (this.doubleDiveLeft > 0) {
      this.doubleDiveLeft--;
      this.state = 'diverecover';
      this.stateTime = DIVERECOVER_TIME;
    } else {
      this.state = 'landed';
      this.stateTime = this.enraged ? LANDED_TIME_2 : LANDED_TIME;
      this.breathPhase = 'idle';
      this.breathHit = false;
      this.breathActive = false;
    }
  }

  private updateDiveRecover(dt: number) {
    this.stateTime -= dt;
    if (this.stateTime <= 0) {
      // Doppel-Sturz: neues Ziel = aktuelle Spielerposition
      this.state = 'divewindup';
      this.stateTime = this.enraged ? DIVEWINDUP_TIME_2 : DIVEWINDUP_TIME;
      const p = this.game?.player.body.position;
      if (p) {
        this.diveTarget.set(p.x, 0, p.z);
        this.game?.clampToArena(this.diveTarget);
      }
      this.diveShadow.visible = true;
      this.diveShadowActive = true;
    }
  }

  // --- Gelandet (Angriffs-Fenster) ---

  private updateLanded(dt: number, game: Game) {
    this.stateTime -= dt;
    this.body.position.y = 0.15;

    // Zum Spieler drehen
    const playerPos = game.player.body.position;
    const toPlayer = new THREE.Vector3().subVectors(playerPos, this.body.position);
    toPlayer.y = 0;
    if (toPlayer.length() > 0.1) {
      this.body.lookAt(
        this.body.position.x + toPlayer.x,
        this.body.position.y,
        this.body.position.z + toPlayer.z
      );
    }

    // Feuerspeer: Windup -> aktiv -> fertig
    if (this.breathPhase === 'idle') {
      this.breathPhase = 'windup';
      this.breathTime = BREATH_WINDUP;
      this.breathHit = false;
      this.telegraphArc.visible = true;
      playSound('spawn', 0.6);
    } else if (this.breathPhase === 'windup') {
      this.breathTime -= dt;
      (this.telegraphArc.material as THREE.MeshBasicMaterial).opacity =
        0.3 + 0.4 * Math.abs(Math.sin(this.waveTime * 12));
      if (this.breathTime <= 0) {
        this.breathPhase = 'active';
        this.breathTime = BREATH_TIME;
        this.telegraphArc.visible = false;
        this.breathGroup.visible = true;
      this.breathActive = true;
      this.jaw.rotation.x = 0.55;
      }
    } else if (this.breathPhase === 'active') {
      this.breathTime -= dt;
      const t = 1 - this.breathTime / BREATH_TIME;
      this.breathMat.opacity = 0.35 + Math.sin(t * Math.PI) * 0.4;
      this.breathGroup.scale.z = 0.8 + Math.sin(t * Math.PI * 2) * 0.15;

      // Treffer in der Mitte des Feuers
      if (!this.breathHit && t >= 0.4) {
        this.breathHit = true;
        const dx = playerPos.x - this.body.position.x;
        const dz = playerPos.z - this.body.position.z;
        const dist = Math.hypot(dx, dz);
        if (dist < BREATH_RANGE && playerPos.y <= 0.6) {
          const ang = Math.atan2(dx, dz);
          const yaw = this.body.rotation.y;
          let diff = ang - yaw;
          while (diff > Math.PI) diff -= Math.PI * 2;
          while (diff < -Math.PI) diff += Math.PI * 2;
          if (Math.abs(diff) < BREATH_HALF_ANGLE) {
            game.player.damage(BREATH_DAMAGE, this.body.position);
            playSound('explosion', 0.7);
          }
        }
      }
      if (this.breathTime <= 0) {
        this.breathPhase = 'done';
        this.breathGroup.visible = false;
        this.breathMat.opacity = 0;
        this.jaw.rotation.x = 0.12;
      }
    }

    if (this.stateTime <= 0) {
      this.state = 'takeoff';
      this.stateTime = TAKEOFF_TIME;
      this.telegraphArc.visible = false;
      this.breathGroup.visible = false;
      playSound('spawn', 0.6);
    }
  }

  // --- Abheben ---

  private updateTakeoff(dt: number) {
    this.stateTime -= dt;
    const t = 1 - Math.max(0, this.stateTime) / TAKEOFF_TIME;
    this.body.position.y = 0.15 + t * TAKEOFF_HEIGHT;
    if (this.stateTime <= 0) {
      this.state = 'fly';
      this.stateTime = this.enraged ? FLY_TIME_2 : FLY_TIME;
      this.flyFireballTimer = 1.2;
      this.breathPhase = 'idle';
    }
  }

  // --- Feuerbälle (Flug) ---

  private throwFireballs(game: Game) {
    const from = new THREE.Vector3(this.body.position.x, this.body.position.y - 2, this.body.position.z);
    const target = game.player.body.position.clone();
    const count = this.enraged ? FIREBALL_COUNT_2 : FIREBALL_COUNT;
    const baseDir = new THREE.Vector3().subVectors(target, from);
    baseDir.y = 0;
    const horizDist = baseDir.length();
    baseDir.normalize();
    const perp = new THREE.Vector3(-baseDir.z, 0, baseDir.x);

    // Ballistisches Zielen: Abwärtsgeschwindigkeit so wählen, dass der Ball
    // die Spielerhöhe genau beim Überflug erreicht (sonst segeln sie drüber).
    const flightTime = Math.max(0.3, horizDist / FIREBALL_SPEED);
    const dropVy = (target.y - from.y + 0.5 * FIREBALL_GRAVITY * flightTime * flightTime) / flightTime;

    for (let i = 0; i < count; i++) {
      const off = (i / (count - 1) - 0.5) * 2 * FIREBALL_SPREAD;
      const dir = new THREE.Vector3()
        .addScaledVector(baseDir, 1)
        .addScaledVector(perp, off)
        .normalize()
        .multiplyScalar(FIREBALL_SPEED);
      dir.y = dropVy;
      const fb = new DragonFireball(from.clone(), dir);
      game.scene.add(fb.body);
      this.fireballs.push(fb);
    }
    playSound('spawn', 0.5);
  }

  // --- Schaden ---

  override takeDamage(
    damage: number,
    game: Game,
    withKnockback = true,
    sourcePos?: THREE.Vector3
  ) {
    if (this.dead) return;
    // Im Flug unverwundbar: nur am Boden (landed) verletzbar
    if (this.state !== 'landed') 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() {
    for (const fb of this.fireballs) fb.dispose();
    this.fireballs = [];
    if (this.game) {
      for (const f of this.impactFlashes) {
        this.game.scene.remove(f.mesh);
        f.mesh.geometry.dispose();
        f.mat.dispose();
      }
    }
    this.impactFlashes = [];
    this.diveShadowActive = false;
    this.diveShadow.visible = false;
    if (this.diveShadowAdded && this.game) {
      this.game.scene.remove(this.diveShadow);
    }
    this.diveShadowAdded = false;
    this.telegraphArc.visible = false;
    this.breathGroup.visible = false;
    this.skinMat.color.set(0xbb4433);
    this.darkMat.color.set(0x772a1e);
    this.eyeMat.color.set(0xffcc33);
    this.model.scale.setScalar(MODEL_SCALE);
  }
}

// Feuerball: gerades Projektil mit leichter Gravitation
class DragonFireball {
  body: THREE.Group;
  private vel: THREE.Vector3;
  private life = 4;
  dead = false;

  constructor(from: THREE.Vector3, dir: THREE.Vector3) {
    this.body = new THREE.Group();
    this.body.position.copy(from);
    this.vel = dir.clone();

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

    const coreMat = new THREE.MeshBasicMaterial({
      color: 0xffdd55,
      blending: THREE.AdditiveBlending,
      depthWrite: false,
    });
    const core = new THREE.Mesh(new THREE.SphereGeometry(0.24, 10, 10), coreMat);
    this.body.add(core);
  }

  update(dt: number, game: Game): boolean {
    this.life -= dt;

    // Homing: sanft in Richtung des aktuellen Spielerstandorts lenken,
    // damit die Bälle nicht über den Kopf segeln. Der begrenzte Lenkwinkel
    // (FIREBALL_HOMING als Lerp-Rate) hält sie ausweichbar.
    if (game.player.health > 0) {
      const toPlayer = new THREE.Vector3()
        .subVectors(game.player.body.position, this.body.position)
        .normalize();
      const desired = toPlayer.multiplyScalar(FIREBALL_SPEED);
      this.vel.lerp(desired, Math.min(1, dt * FIREBALL_HOMING));
    }

    this.body.position.addScaledVector(this.vel, dt);

    if (this.body.position.y < 0.15) {
      this.body.position.y = 0.15;
    }

    if (game.player.health > 0) {
      const dist = this.body.position.distanceTo(game.player.body.position);
      if (dist < FIREBALL_HIT_RADIUS) {
        game.player.damage(FIREBALL_DAMAGE, this.body.position);
        this.dead = true;
        return false;
      }
    }

    if (this.life <= 0) {
      this.dead = true;
      return false;
    }
    return true;
  }

  dispose() {
    this.body.removeFromParent();
    for (const child of [...this.body.children]) {
      if (child instanceof THREE.Mesh) {
        child.geometry.dispose();
        (child.material as THREE.Material).dispose();
      }
    }
  }
}
