import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

let controls;
let _canvas;
let _camera      = null;
let _timmyGroup  = null;
let _applySlap   = null;
const _raycaster = new THREE.Raycaster();
const _pointer   = new THREE.Vector2();
const _noCtxMenu = e => e.preventDefault();

// ── registerSlapTarget ────────────────────────────────────────────────────────
// Call after initAgents() with Timmy's group and the applySlap function.
// The capture-phase pointerdown listener will hit-test against the group and
// call applySlap(hitPoint) — also suppressing the OrbitControls drag.

export function registerSlapTarget(timmyGroup, applyFn) {
  _timmyGroup = timmyGroup;
  _applySlap  = applyFn;
}

export function initInteraction(camera, renderer) {
  _camera = camera;

  controls = new OrbitControls(camera, renderer.domElement);
  controls.enableDamping      = true;
  controls.dampingFactor      = 0.05;
  controls.screenSpacePanning = false;
  controls.minDistance        = 5;
  controls.maxDistance        = 80;
  controls.maxPolarAngle      = Math.PI / 2.1;
  controls.target.set(0, 0, 0);
  controls.update();

  _canvas = renderer.domElement;
  _canvas.addEventListener('contextmenu', _noCtxMenu);

  // Capture phase so we intercept before OrbitControls' bubble-phase handler.
  // If Timmy is hit we call stopImmediatePropagation() to suppress the orbit drag.
  _canvas.addEventListener('pointerdown', _onPointerDown, { capture: true });

  // touchstart fallback for older mobile browsers that lack Pointer Events
  if (!window.PointerEvent) {
    _canvas.addEventListener('touchstart', _onTouchStart, { capture: true, passive: false });
  }
}

function _hitTest(clientX, clientY) {
  if (!_timmyGroup || !_applySlap || !_camera) return false;

  const rect = _canvas.getBoundingClientRect();
  _pointer.x = ((clientX - rect.left) / rect.width)  *  2 - 1;
  _pointer.y = ((clientY - rect.top)  / rect.height) * -2 + 1;

  _raycaster.setFromCamera(_pointer, _camera);
  const hits = _raycaster.intersectObject(_timmyGroup, true);

  if (hits.length > 0) {
    _applySlap(hits[0].point);
    // 150 ms lockout to avoid accidental orbit drag immediately after a slap
    if (controls) {
      controls.enabled = false;
      setTimeout(() => { if (controls) controls.enabled = true; }, 150);
    }
    return true;
  }
  return false;
}

function _onPointerDown(event) {
  if (_hitTest(event.clientX, event.clientY)) {
    event.stopImmediatePropagation(); // block OrbitControls drag
  }
}

function _onTouchStart(event) {
  if (!event.touches || event.touches.length === 0) return;
  const t = event.touches[0];
  if (_hitTest(t.clientX, t.clientY)) {
    event.stopImmediatePropagation();
    event.preventDefault(); // suppress subsequent mouse events (ghost click)
  }
}

export function updateControls() {
  if (controls) controls.update();
}

/**
 * Dispose OrbitControls event listeners.
 * Called before context-loss teardown.
 */
export function disposeInteraction() {
  if (_canvas) {
    _canvas.removeEventListener('contextmenu', _noCtxMenu);
    _canvas.removeEventListener('pointerdown', _onPointerDown, { capture: true });
    if (!window.PointerEvent) {
      _canvas.removeEventListener('touchstart', _onTouchStart, { capture: true });
    }
    _canvas = null;
  }
  if (controls) {
    controls.dispose();
    controls = null;
  }
  _camera     = null;
  _timmyGroup = null;
  _applySlap  = null;
}
