import * as THREE from 'three';
import { Sword } from './Sword';
import { Staff } from './Staff';
import { loadGLB } from './MeshLoader';
import type { Game } from './Game';

export class Player {
  body: THREE.Group;
  leftHandWeapon: Staff;
  rightHandWeapon: Sword;
  velocity = new THREE.Vector3();
  health = 100;
  MAX_HEALTH = 100;
  game!: Game;
  jumpVelocity = 0;
  onGround = true;
  mixer!: THREE.AnimationMixer;
  private clips: Map<string, THREE.AnimationAction> = new Map();
  private currentAnim = '';

  constructor() {
    this.body = new THREE.Group();

    // Helmet
    const helmetGeom = new THREE.SphereGeometry(0.45, 8, 8, 0, Math.PI * 2, 0, Math.PI / 2);
    const helmetMat = new THREE.MeshToonMaterial({ color: 0x888888 });
    const helmet = new THREE.Mesh(helmetGeom, helmetMat);
    helmet.position.y = 0.9;
    helmet.castShadow = true;
    this.body.add(helmet);

    // Try loading the real helmet model (replaces the placeholder sphere)
    loadGLB('helmet').then((gltf) => {
      const realHelmet = gltf.scene;
      const helmetTexture = new THREE.TextureLoader().load('./textures/helmet.png');
      helmetTexture.flipY = false;
      helmetTexture.colorSpace = THREE.SRGBColorSpace;
      realHelmet.traverse((child) => {
        if (child instanceof THREE.Mesh) {
          child.castShadow = true;
          child.material = new THREE.MeshToonMaterial({ map: helmetTexture });
        }
      });
      realHelmet.scale.set(0.35, 0.35, 0.35);
      realHelmet.position.set(0, 0.9, -0.2);
      realHelmet.rotation.set(-0.4, 0, 0);
      this.body.remove(helmet);
      this.body.add(realHelmet);
    }).catch(() => {
      // Keep placeholder helmet
    });

    // Shadow disc
    const shadowGeom = new THREE.CircleGeometry(0.4, 16);
    const shadowMat = new THREE.MeshBasicMaterial({
      color: 0x000000, transparent: true, opacity: 0.3,
    });
    const shadow = new THREE.Mesh(shadowGeom, shadowMat);
    shadow.rotation.x = -Math.PI / 2;
    shadow.position.y = -0.48;
    shadow.renderOrder = 999;
    this.body.add(shadow);

    // Weapons
    this.leftHandWeapon = new Staff();
    this.rightHandWeapon = new Sword();
    this.body.add(this.leftHandWeapon.mesh);
    this.body.add(this.rightHandWeapon.mesh);
  }

  async init() {
    try {
      const gltf = await loadGLB('blob');
      const model = gltf.scene;
      model.scale.set(0.5, 0.5, 0.5);
      model.traverse((child) => {
        if (child instanceof THREE.Mesh) {
          child.castShadow = true;
          child.receiveShadow = true;
          const mat = new THREE.MeshToonMaterial({
            color: 0x44aa44,
          });
          child.material = mat;
        }
      });
      this.body.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);
        }
        this.playAnimation('stand');
      }
    } catch (e) {
      console.warn('Failed to load blob model, using fallback', e);
      // Fallback capsule
      const bodyGeom = new THREE.CapsuleGeometry(0.5, 0.5, 8, 16);
      const bodyMat = new THREE.MeshToonMaterial({ color: 0x44aa44 });
      const bodyMesh = new THREE.Mesh(bodyGeom, bodyMat);
      bodyMesh.position.y = 0.75;
      bodyMesh.castShadow = true;
      this.body.add(bodyMesh);
    }
  }

  playAnimation(name: string, loop = true, speed = 1) {
    if (!this.clips.has(name)) return;
    if (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.weight = 1;
    action.play();
    return action;
  }

  stopAnimation(name: string) {
    const action = this.clips.get(name);
    if (action) action.stop();
  }

  getCurrentAnimation(): string {
    return this.currentAnim;
  }

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

  setGame(game: Game) {
    this.game = game;
  }

  jump() {
    if (this.onGround) {
      this.jumpVelocity = 8;
      this.onGround = false;
      this.playAnimation('jump', false);
    }
  }

  switchHands() {
    const temp = this.leftHandWeapon;
    this.leftHandWeapon = this.rightHandWeapon as unknown as Staff;
    this.rightHandWeapon = temp as unknown as Sword;

    const leftPos = this.leftHandWeapon.mesh.position.clone();
    const rightPos = this.rightHandWeapon.mesh.position.clone();
    this.leftHandWeapon.mesh.position.copy(rightPos);
    this.rightHandWeapon.mesh.position.copy(leftPos);

    const leftRot = this.leftHandWeapon.mesh.rotation.clone();
    const rightRot = this.rightHandWeapon.mesh.rotation.clone();
    this.leftHandWeapon.mesh.rotation.copy(rightRot);
    this.rightHandWeapon.mesh.rotation.copy(leftRot);
  }

  heal(amount: number) {
    this.health = Math.min(this.health + amount, this.MAX_HEALTH);
  }

  die() {
    this.playAnimation('die', false);
  }
}
