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

export class Crab extends Enemy {
  private model: THREE.Group;
  private clawL: THREE.Mesh;
  private clawR: THREE.Mesh;
  private time = Math.random() * Math.PI * 2;
  private side = Math.random() < 0.5 ? 1 : -1;

  constructor() {
    super();
    this.health = 90;
    this.speed = 4.5;
    this.xp = 20;
    this.contactRadius = 2.2;

    this.model = new THREE.Group();
    this.fadeOutModel = this.model;
    this.body.add(this.model);

    const shellMat = new THREE.MeshToonMaterial({ color: 0xdd5533 });
    const darkMat = new THREE.MeshToonMaterial({ color: 0x993322 });

    // Shell body
    const shell = new THREE.Mesh(new THREE.SphereGeometry(0.55, 18, 14), shellMat);
    shell.scale.set(1, 0.55, 0.85);
    shell.position.y = 0.35;
    shell.castShadow = true;
    this.registerFadeMesh(shell);
    this.model.add(shell);

    // Shell spots
    for (const [dx, dz] of [[0, -0.15], [0.25, 0.1], [-0.25, 0.1]] as const) {
      const spot = new THREE.Mesh(new THREE.SphereGeometry(0.08, 8, 8), darkMat);
      spot.scale.set(1, 0.4, 1);
      spot.position.set(dx, 0.62, dz);
      this.registerFadeMesh(spot);
      this.model.add(spot);
    }

    // Eye stalks (on the armored front side, +Z)
    const stalkMat = new THREE.MeshToonMaterial({ color: 0xdd5533 });
    const eyeMat = new THREE.MeshToonMaterial({ color: 0x111122 });
    for (const side of [-1, 1]) {
      const stalk = new THREE.Mesh(new THREE.CylinderGeometry(0.035, 0.035, 0.22, 6), stalkMat);
      stalk.position.set(side * 0.22, 0.55, 0.42);
      this.registerFadeMesh(stalk);
      this.model.add(stalk);
      const eye = new THREE.Mesh(new THREE.SphereGeometry(0.09, 10, 8), eyeMat);
      eye.position.set(side * 0.22, 0.68, 0.42);
      this.registerFadeMesh(eye);
      this.model.add(eye);
    }

    // Legs
    const legGeom = new THREE.CapsuleGeometry(0.05, 0.25, 4, 8);
    for (const side of [-1, 1]) {
      for (const legZ of [-0.3, 0.3]) {
        const leg = new THREE.Mesh(legGeom, shellMat);
        leg.position.set(side * 0.45, 0.15, legZ);
        leg.rotation.z = side * 0.6;
        this.registerFadeMesh(leg);
        this.model.add(leg);
      }
    }

    // Big claws (front, open/close)
    const clawGeom = new THREE.SphereGeometry(0.22, 12, 10);
    this.clawL = new THREE.Mesh(clawGeom, shellMat);
    this.clawR = new THREE.Mesh(clawGeom, shellMat);
    this.clawL.position.set(-0.45, 0.45, 0.5);
    this.clawR.position.set(0.45, 0.45, 0.5);
    this.clawL.scale.set(1.2, 0.7, 1.4);
    this.clawR.scale.set(1.2, 0.7, 1.4);
    this.clawL.castShadow = true;
    this.clawR.castShadow = true;
    this.registerFadeMesh(this.clawL);
    this.registerFadeMesh(this.clawR);
    this.model.add(this.clawL, this.clawR);
  }

  playAnim() {
    // Procedural visuals only
  }

  updateAnimation(dt: number) {
    this.updateFadeDeath(dt, { duration: 0.8, sink: 0.4 });
  }

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

    // Claw pinch
    const pinch = 1 + Math.sin(this.time * 4) * 0.35;
    this.clawL.scale.set(1.2, 0.7 * pinch, 1.4);
    this.clawR.scale.set(1.2, 0.7 * pinch, 1.4);

    const toPlayer = new THREE.Vector3()
      .subVectors(game.player.body.position, this.body.position);
    toPlayer.y = 0;
    const dist = toPlayer.length();

    // Face the player
    if (dist > 0.1) {
      this.body.lookAt(
        this.body.position.x + toPlayer.x,
        this.body.position.y,
        this.body.position.z + toPlayer.z
      );
    }

    // Strafe sideways around the player
    if (dist > 3) {
      const perp = new THREE.Vector3(-toPlayer.z, 0, toPlayer.x).normalize();
      if (this.knockback > 0) {
        this.knockback -= dt;
        const away = toPlayer.clone().normalize().multiplyScalar(-this.speed);
        this.body.position.x += away.x * dt;
        this.body.position.z += away.z * dt;
      } else {
        // Flip side occasionally to circle
        if (Math.random() < dt * 0.3) this.side *= -1;
        this.body.position.x += perp.x * this.side * this.speed * dt;
        this.body.position.z += perp.z * this.side * this.speed * dt;
      }
      game.clampToArena(this.body.position);
    } else if (dist > 0.1) {
      const dir = toPlayer.normalize();
      if (this.knockback > 0) {
        this.knockback -= dt;
        this.body.position.x -= dir.x * this.speed * dt;
        this.body.position.z -= dir.z * this.speed * dt;
      }
    }
  }

  takeDamage(
    damage: number,
    game: Game,
    withKnockback = true,
    sourcePos?: THREE.Vector3
  ) {
    // Armored from the front: reduce damage coming from the facing side
    if (sourcePos) {
      const front = new THREE.Vector3(0, 0, 1)
        .applyQuaternion(this.body.quaternion);
      front.y = 0;
      front.normalize();
      const incoming = new THREE.Vector3()
        .subVectors(sourcePos, this.body.position);
      incoming.y = 0;
      incoming.normalize();
      if (front.dot(incoming) > 0.3) {
        damage *= 0.4;
      }
    }
    super.takeDamage(damage, game, withKnockback, sourcePos);
  }

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