import * as THREE from 'three';
import { angDiff, easeOutBack } from './util';
import type { Collider, PotSpot } from './world';

interface Pot {
  x: number;
  y: number;
  z: number;
  ground: boolean;
  mesh: THREE.Mesh;
  state: 'intact' | 'broken' | 'growing';
  t: number;
}

/** Smashable clay pots. */
export class Pots {
  totalBroken = 0;
  private list: Pot[] = [];

  constructor(scene: THREE.Scene, spots: PotSpot[]) {
    const profile: THREE.Vector2[] = [
      new THREE.Vector2(0.001, 0),
      new THREE.Vector2(0.17, 0.02),
      new THREE.Vector2(0.24, 0.13),
      new THREE.Vector2(0.27, 0.3),
      new THREE.Vector2(0.2, 0.44),
      new THREE.Vector2(0.145, 0.48),
      new THREE.Vector2(0.18, 0.54),
      new THREE.Vector2(0.16, 0.56),
    ];
    const geo = new THREE.LatheGeometry(profile, 9);
    geo.computeVertexNormals();
    for (const s of spots) {
      const mat = new THREE.MeshLambertMaterial({ color: '#a96644', flatShading: true });
      mat.color.offsetHSL((Math.random() - 0.5) * 0.02, 0, (Math.random() - 0.5) * 0.08);
      const mesh = new THREE.Mesh(geo, mat);
      mesh.position.set(s.x, s.y, s.z);
      mesh.rotation.y = Math.random() * Math.PI;
      mesh.castShadow = true;
      scene.add(mesh);
      this.list.push({ x: s.x, y: s.y, z: s.z, ground: s.ground, mesh, state: 'intact', t: 0 });
    }
  }

  /** Colliders for intact walk-level pots. */
  colliders(): Collider[] {
    const out: Collider[] = [];
    for (const p of this.list) {
      if (p.state !== 'broken' && p.ground) out.push({ x: p.x, z: p.z, r: 0.3 });
    }
    return out;
  }

  /**
   * Smash intact pots inside the blade arc (and within vertical reach).
   * Calls onSmash(x, y, z) per pot; returns the number smashed.
   */
  tryHit(
    hx: number,
    hz: number,
    heroY: number,
    bladeYaw: number,
    radius: number,
    halfArc: number,
    onSmash: (x: number, y: number, z: number) => void,
  ): number {
    let n = 0;
    for (const p of this.list) {
      if (p.state !== 'intact') continue;
      const dx = p.x - hx;
      const dz = p.z - hz;
      const d = Math.hypot(dx, dz);
      if (d > radius || Math.abs(p.y - heroY) > 1.05) continue;
      if (Math.abs(angDiff(Math.atan2(dx, dz), bladeYaw)) > halfArc + 0.12 / Math.max(0.5, d)) continue;
      p.state = 'broken';
      p.t = 28 + Math.random() * 8;
      p.mesh.visible = false;
      this.totalBroken++;
      n++;
      onSmash(p.x, p.y, p.z);
    }
    return n;
  }

  update(dt: number): void {
    for (const p of this.list) {
      if (p.state === 'broken') {
        p.t -= dt;
        if (p.t <= 0) {
          p.state = 'growing';
          p.t = 0;
          p.mesh.visible = true;
        }
      } else if (p.state === 'growing') {
        p.t += dt / 0.35;
        if (p.t >= 1) {
          p.state = 'intact';
          p.mesh.scale.setScalar(1);
        } else {
          p.mesh.scale.setScalar(Math.max(0.01, easeOutBack(p.t)));
        }
      }
    }
  }
}
