import { CameraC_internal } from "@hitplay/playable_template";
import { Object3D, Vector3 } from "three";
import { GameLoop } from "../core/GameLoop";

const _targetWorldPos  = new Vector3();
const _lookAheadTarget = new Vector3();
const _desired         = new Vector3();

export class FollowCameraC {
  static offset    = new Vector3();
  static lerpSpeed = 6;
  static paused    = false;

  // Camera shifts toward where the player is facing.
  // After the player stops the lerp continues to drift — creating the
  // "camera settles into the facing direction" feel.
  static lookAheadStrength  = 1.5;  // world units to shift ahead
  static lookAheadLerpSpeed = 2;    // low value → slow drift after stop

  private static target: Object3D | null = null;
  private static _lookAheadCurrent = new Vector3();

  static init(target: Object3D) {
    this.target = target;
    const camera = CameraC_internal.camera!;
    target.getWorldPosition(_targetWorldPos);
    // Camera config is authored for a character at origin, so the camera's
    // world position IS the intended relative offset from any character position.
    this.offset.copy(camera.position);
    // Immediately place camera relative to the actual character position
    // (not config origin) so the character is visible from frame one.
    camera.position.copy(_targetWorldPos).add(this.offset);
    GameLoop.register((delta) => this.update(delta));
  }

  // Re-syncs base offset after a config-driven camera move (e.g. resize).
  // CameraC.setCamera() already moved the camera to the config position,
  // which equals the desired relative offset — just re-read it.
  static syncAndSnap() {
    const camera = CameraC_internal.camera;
    if (!this.target || !camera) return;
    this.offset.copy(camera.position);
    this.target.getWorldPosition(_targetWorldPos);
    camera.position.copy(_targetWorldPos).add(this.offset);
    this._lookAheadCurrent.set(0, 0, 0);
  }

  private static update(delta: number) {
    if (this.paused) return;
    const camera = CameraC_internal.camera;
    if (!this.target || !camera) return;

    this.target.getWorldPosition(_targetWorldPos);

    // Look-ahead: smoothly track the player's forward direction.
    // target.rotation.y is the mesh Y-axis rotation set by PlayerC.
    // When the player stops, the lerp keeps drifting toward the last
    // facing direction — the "settle after stop" effect comes for free.
    const yaw = this.target.rotation.y;
    _lookAheadTarget.set(
      Math.sin(yaw) * this.lookAheadStrength,
      0,
      Math.cos(yaw) * this.lookAheadStrength,
    );
    this._lookAheadCurrent.lerp(_lookAheadTarget, Math.min(1, this.lookAheadLerpSpeed * delta));

    _desired.copy(_targetWorldPos).add(this.offset).add(this._lookAheadCurrent);
    camera.position.lerp(_desired, Math.min(1, this.lerpSpeed * delta));
  }
}
