import { Object3D, Raycaster, Vector3 } from "three";
import * as TWEEN from "@tweenjs/tween.js";
import { CameraC_internal } from "@hitplay/playable_template";
import { Trigger } from "./TriggerC";
import { TestSceneC } from "./TestSceneC";
import { LootC } from "./LootC";
import { GameLoop } from "../core/GameLoop";
import { images } from "../resources/resources";
import { worldToScreen } from "../utils/screen";

// --- Tunables ---
const COST          = 15;                    // wood needed to fully pay the zone
const ZONE_FALLBACK = new Vector3(0, 0, 0);  // zone spot if UI_Interactive_Zone_02 is missing
const ZONE_OFFSET   = new Vector3(0, 0, -4); // shift the zone toward the player (off the crates)
const ZONE_SCALE    = 1.6;                   // scale the pad up for presence
const ICON_LIFT     = 0.04;                  // wood icon height above the pad (local)
const TRIGGER_HALF  = 1.0;                   // enter-trigger half size (x/z)
const PAY_INTERVAL  = 0.09;                  // seconds between flying planks
const FLY_MS        = 420;                   // UI → zone flight time
const FLY_ARC_PX    = 90;                    // height of the flight arc (px)
const PLANK_PX      = 26;                    // flying plank size (px)
const FILL_GROW     = 0.15;                  // how much the pad grows when full
const PULSE_MS      = 160;                   // per-plank pulse time

const _scratch = new Vector3(); // reused so positioning never allocates

/**
 * Pay zone: the player stands on the UI_Tool_Zone pad and the wood collected in
 * the top-right counter flies into it, plank by plank. When COST is reached the
 * pad pops and disappears.
 */
export class PayZoneC {
  private static zone: Object3D | null = null;
  private static trigger: Trigger | null = null;
  private static tweens = new TWEEN.Group(); // our tween group, pumped each frame

  private static inside = false;     // is the player standing on the pad
  private static done = false;       // already paid in full
  private static paid = 0;           // wood delivered so far
  private static payCooldown = 0;    // seconds until the next plank may fly

  private static baseScale = new Vector3(1, 1, 1); // pad resting scale (tweens multiply it)
  private static pulseTween: TWEEN.Tween<{ progress: number }> | null = null;

  static init() {
    const zone = TestSceneC.payZone;
    if (!zone) { console.warn("[PayZone] UI_Tool_Zone not found"); return; }
    this.zone = zone;

    // Where to put the pad: UI_Interactive_Zone_02's spot, shifted toward the
    // player, sitting on the real surface (raycast — the road is above the sand).
    const target = _scratch.copy(ZONE_FALLBACK);
    if (TestSceneC.interactiveZone) TestSceneC.interactiveZone.getWorldPosition(target);
    target.x += ZONE_OFFSET.x;
    target.z += ZONE_OFFSET.z;
    target.y = this.sampleSurfaceY(target.x, target.z) + 0.02;

    // Make the wood icon a child of the pad so it moves/vanishes with it.
    const icon = TestSceneC.payZoneIcon;
    if (icon) {
      zone.attach(icon);
      icon.position.set(0, ICON_LIFT, 0);
      icon.visible = true;
    }

    // Place, scale, remember the resting scale, show.
    zone.position.copy(target);
    zone.scale.multiplyScalar(ZONE_SCALE);
    this.baseScale.copy(zone.scale);
    zone.visible = true;

    // Trigger that tells us when the player is on the pad.
    this.trigger = new Trigger(
      { x: target.x, y: target.y + 0.5, z: target.z },
      { x: TRIGGER_HALF, y: 1.0, z: TRIGGER_HALF },
      { onEnter: () => { this.inside = true; }, onExit: () => { this.inside = false; } },
    );

    GameLoop.register((delta) => this.update(delta));
  }

  private static update(delta: number) {
    this.tweens.update();
    if (this.done || !this.inside) return;

    // One plank at a time: wait out the cooldown, stop if paid or out of wood.
    this.payCooldown -= delta;
    if (this.payCooldown > 0) return;
    if (this.paid >= COST || LootC.getBalance() <= 0) return;

    // Spend one wood and fly it into the zone.
    LootC.spend(1);
    this.paid++;
    this.payCooldown = PAY_INTERVAL;
    this.flyOnePlank();

    if (this.paid >= COST) this.complete();
  }

  // Fly one HTML plank from the UI counter to the zone along an arc.
  private static flyOnePlank() {
    const from = LootC.uiIconScreenCenter();
    const to = this.zoneScreenCenter();
    if (!to) return;

    const img = document.createElement("img");
    img.src = images.woodIconUrl;
    img.style.cssText =
      `position:fixed; left:0; top:0; width:${PLANK_PX}px; height:${PLANK_PX}px;` +
      `z-index:1000; pointer-events:none; transform:translate(-50%,-50%); will-change:left,top;`;
    document.body.appendChild(img);

    const anim = { progress: 0 };
    new TWEEN.Tween(anim, this.tweens)
      .to({ progress: 1 }, FLY_MS)
      .easing(TWEEN.Easing.Quadratic.In)
      .onUpdate(({ progress }) => {
        const x = from.x + (to.x - from.x) * progress;
        const y = from.y + (to.y - from.y) * progress - Math.sin(Math.PI * progress) * FLY_ARC_PX; // arc
        img.style.left = `${x}px`;
        img.style.top = `${y}px`;
      })
      .onComplete(() => { img.remove(); this.onPlankArrived(); })
      .start();
  }

  // A plank reached the zone → pulse the pad (bigger the fuller it is).
  private static onPlankArrived() {
    const zone = this.zone;
    if (!zone || this.done) return;

    const grow = FILL_GROW * (this.paid / COST); // resting scale bump for the current fill level
    this.pulseTween?.stop(); // drop the previous pulse so they don't fight
    const anim = { progress: 0 };
    this.pulseTween = new TWEEN.Tween(anim, this.tweens)
      .to({ progress: 1 }, PULSE_MS)
      .easing(TWEEN.Easing.Back.Out)
      .onUpdate(({ progress }) => {
        const scale = 1 + grow + (1 - progress) * 0.12; // settle at fill size with a small punch
        zone.scale.set(this.baseScale.x * scale, this.baseScale.y * scale, this.baseScale.z * scale);
      })
      .onComplete(() => { this.pulseTween = null; })
      .start();
  }

  // Paid in full: grow, then shrink to nothing.
  private static complete() {
    this.done = true;
    this.trigger?.destroy();
    this.trigger = null;
    this.pulseTween?.stop();
    this.pulseTween = null;

    const zone = this.zone;
    if (!zone) return;

    const setScale = (scale: number) =>
      zone.scale.set(this.baseScale.x * scale, this.baseScale.y * scale, this.baseScale.z * scale);

    const grow = new TWEEN.Tween({ progress: 0 }, this.tweens)
      .to({ progress: 1 }, 180)
      .easing(TWEEN.Easing.Back.Out)
      .onUpdate(({ progress }) => setScale(1 + 0.45 * progress));
    const vanish = new TWEEN.Tween({ progress: 0 }, this.tweens)
      .to({ progress: 1 }, 260)
      .easing(TWEEN.Easing.Back.In)
      .onUpdate(({ progress }) => setScale(1.45 * Math.max(0, 1 - progress)))
      .onComplete(() => {
        zone.visible = false;
        if (TestSceneC.payZoneIcon) TestSceneC.payZoneIcon.visible = false; // hide the icon too
      });

    grow.chain(vanish);
    grow.start();
    console.log("[PayZone] paid in full");
  }

  // Ground height (road/sand) at an x/z via a downward ray.
  private static sampleSurfaceY(x: number, z: number): number {
    const env = TestSceneC.environment;
    if (env) {
      const ray = new Raycaster(new Vector3(x, 5, z), new Vector3(0, -1, 0));
      const hits = ray.intersectObject(env, true);
      if (hits.length) return hits[0].point.y;
    }
    return TestSceneC.groundY;
  }

  // Zone world center → screen pixels (target for the flying planks).
  private static zoneScreenCenter(): { x: number; y: number } | null {
    const cam = CameraC_internal.camera;
    const canvas = document.querySelector("canvas");
    const zone = this.zone;
    if (!cam || !canvas || !zone) return null;
    const rect = canvas.getBoundingClientRect();
    _scratch.copy(zone.position); _scratch.y += 0.3; // aim a little above the pad's base
    return worldToScreen(_scratch, cam, rect);
  }
}
