/**
 * navigation.js — FPS-style first-person navigation for the Tower world.
 *
 * Desktop : Click canvas to lock pointer → WASD to move, mouse to look. Esc to release.
 * Mobile  : Left-side joystick to move, drag right side to look.
 *
 * Exports:
 *   initNavigation(camera, renderer)
 *   updateNavigation(deltaMs)    — call each frame; returns true if camera moved
 *   disposeNavigation()
 */

import * as THREE from 'three';

const EYE_HEIGHT        = 1.75;   // camera Y (units)
const MOVE_SPEED        = 5.5;    // units / second
const MOUSE_SENSITIVITY = 0.0022; // rad / pixel
const TOUCH_SENSITIVITY = 0.0030; // rad / pixel
const MAX_PITCH         = Math.PI / 2.1;
const BOUNDS            = 11.5;   // half-side of roamable square (floor is 28×28)
const JOY_DEADZONE      = 0.08;

let _camera  = null;
let _canvas  = null;
let _yaw     = 0;
let _pitch   = 0.08;
let _yawΔ    = 0;
let _pitchΔ  = 0;
let _keys    = {};
let _joy     = { x: 0, y: 0 };
let _locked  = false;
let _mobile  = false;

// ── touch tracking ────────────────────────────────────────────────────────────
let _joyTouchId   = null;
let _joyOrigin    = { x: 0, y: 0 };
let _lookTouchId  = null;
let _lookLast     = { x: 0, y: 0 };

const JOY_HALF_W = 55; // px — joystick knob travel radius

// ── DOM refs ──────────────────────────────────────────────────────────────────
let $joyNub   = null;
let $joyPad   = null;
let $lockHint = null;

// ── helpers ───────────────────────────────────────────────────────────────────
const _tmp = new THREE.Vector3();
const _fwd = new THREE.Vector3();
const _rgt = new THREE.Vector3();
const _UP  = new THREE.Vector3(0, 1, 0);

function _applyRotation() {
  const qY = new THREE.Quaternion().setFromAxisAngle(_UP, _yaw);
  const qX = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1, 0, 0), _pitch);
  _camera.quaternion.copy(qY).multiply(qX);
}

// ── keyboard ──────────────────────────────────────────────────────────────────
function _onKeyDown(e) { _keys[e.code] = true; }
function _onKeyUp(e)   { delete _keys[e.code]; }

// ── pointer lock (desktop) ────────────────────────────────────────────────────
function _requestLock() { _canvas.requestPointerLock(); }
function _onLockChange() {
  _locked = document.pointerLockElement === _canvas;
  if ($lockHint) $lockHint.style.display = _locked ? 'none' : 'flex';
}
function _onMouseMove(e) {
  if (!_locked) return;
  _yawΔ   -= e.movementX * MOUSE_SENSITIVITY;
  _pitchΔ -= e.movementY * MOUSE_SENSITIVITY;
}

// ── touch (mobile) ────────────────────────────────────────────────────────────
function _onTouchStart(e) {
  for (const t of e.changedTouches) {
    const leftHalf  = t.clientX < window.innerWidth * 0.45;
    const botHalf   = t.clientY > window.innerHeight * 0.45;

    if (leftHalf && botHalf && _joyTouchId === null) {
      // Joystick zone — initialise centred on first touch point
      _joyTouchId = t.identifier;
      _joyOrigin  = { x: t.clientX, y: t.clientY };
      _updateJoy(t.clientX, t.clientY);

      if ($joyPad) {
        const offset = JOY_HALF_W + 12;
        $joyPad.style.left   = `${Math.max(offset, Math.min(window.innerWidth  - offset, t.clientX) - JOY_HALF_W)}px`;
        $joyPad.style.bottom = `${window.innerHeight - Math.min(window.innerHeight - 12, t.clientY) - JOY_HALF_W}px`;
        $joyPad.style.opacity = '1';
      }
    } else if (!leftHalf && _lookTouchId === null) {
      // Look zone
      _lookTouchId = t.identifier;
      _lookLast    = { x: t.clientX, y: t.clientY };
    }
  }
}
function _onTouchMove(e) {
  e.preventDefault();
  for (const t of e.changedTouches) {
    if (t.identifier === _joyTouchId) {
      _updateJoy(t.clientX, t.clientY);
    } else if (t.identifier === _lookTouchId) {
      const dx = t.clientX - _lookLast.x;
      const dy = t.clientY - _lookLast.y;
      _yawΔ   -= dx * TOUCH_SENSITIVITY;
      _pitchΔ -= dy * TOUCH_SENSITIVITY;
      _lookLast = { x: t.clientX, y: t.clientY };
    }
  }
}
function _onTouchEnd(e) {
  for (const t of e.changedTouches) {
    if (t.identifier === _joyTouchId) {
      _joyTouchId = null;
      _joy = { x: 0, y: 0 };
      if ($joyNub) $joyNub.style.transform = 'translate(-50%,-50%)';
      if ($joyPad) $joyPad.style.opacity   = '0.35';
    }
    if (t.identifier === _lookTouchId) _lookTouchId = null;
  }
}

function _updateJoy(cx, cy) {
  const dx = cx - _joyOrigin.x;
  const dy = cy - _joyOrigin.y;
  const dist   = Math.sqrt(dx * dx + dy * dy);
  const travel = Math.min(dist, JOY_HALF_W);
  const angle  = Math.atan2(dy, dx);
  const nx = Math.cos(angle) * travel;
  const ny = Math.sin(angle) * travel;
  _joy.x = nx / JOY_HALF_W;
  _joy.y = ny / JOY_HALF_W;
  if ($joyNub) $joyNub.style.transform = `translate(calc(-50% + ${nx}px), calc(-50% + ${ny}px))`;
}

// ── public API ────────────────────────────────────────────────────────────────
export function initNavigation(camera, renderer) {
  _camera = camera;
  _canvas = renderer.domElement;
  _mobile = 'ontouchstart' in window && !window.matchMedia('(pointer:fine)').matches;

  _camera.position.set(0, EYE_HEIGHT, 9);
  _yaw   = 0;
  _pitch = 0.08;
  _applyRotation();

  $joyNub   = document.getElementById('joy-nub');
  $joyPad   = document.getElementById('joy-pad');
  $lockHint = document.getElementById('lock-hint');

  window.addEventListener('keydown', _onKeyDown);
  window.addEventListener('keyup',   _onKeyUp);

  if (_mobile) {
    _canvas.addEventListener('touchstart',  _onTouchStart, { passive: false });
    _canvas.addEventListener('touchmove',   _onTouchMove,  { passive: false });
    _canvas.addEventListener('touchend',    _onTouchEnd,   { passive: false });
    _canvas.addEventListener('touchcancel', _onTouchEnd,   { passive: false });
    if ($joyPad) $joyPad.style.display = 'flex';
  } else {
    _canvas.addEventListener('click', _requestLock);
    document.addEventListener('pointerlockchange', _onLockChange);
    document.addEventListener('mousemove', _onMouseMove);
    if ($lockHint) $lockHint.style.display = 'flex';
    if ($joyPad)  $joyPad.style.display   = 'none';
  }
}

export function updateNavigation(deltaMs) {
  if (!_camera) return false;
  const dt = Math.min(deltaMs / 1000, 0.05);

  // Apply look
  if (_yawΔ !== 0 || _pitchΔ !== 0) {
    _yaw   += _yawΔ;
    _pitch  = Math.max(-MAX_PITCH, Math.min(MAX_PITCH, _pitch + _pitchΔ));
    _yawΔ   = 0;
    _pitchΔ = 0;
    _applyRotation();
  }

  // Keyboard input
  let mx = 0, mz = 0;
  if (_keys['KeyW'] || _keys['ArrowUp'])    mz -= 1;
  if (_keys['KeyS'] || _keys['ArrowDown'])  mz += 1;
  if (_keys['KeyA'] || _keys['ArrowLeft'])  mx -= 1;
  if (_keys['KeyD'] || _keys['ArrowRight']) mx += 1;

  // Joystick (additive)
  const jx = Math.abs(_joy.x) > JOY_DEADZONE ? _joy.x : 0;
  const jy = Math.abs(_joy.y) > JOY_DEADZONE ? _joy.y : 0;
  mx += jx;
  mz += jy;

  if (mx !== 0 || mz !== 0) {
    const len = Math.sqrt(mx * mx + mz * mz);
    mx /= len; mz /= len;

    _camera.getWorldDirection(_fwd);
    _fwd.y = 0;
    if (_fwd.lengthSq() < 0.001) _fwd.set(0, 0, -1);
    _fwd.normalize();
    _rgt.crossVectors(_UP, _fwd).normalize();

    _tmp.set(0, 0, 0)
      .addScaledVector(_fwd, -mz)
      .addScaledVector(_rgt, -mx);

    _camera.position.addScaledVector(_tmp, MOVE_SPEED * dt);
    _camera.position.x = Math.max(-BOUNDS, Math.min(BOUNDS, _camera.position.x));
    _camera.position.z = Math.max(-BOUNDS, Math.min(BOUNDS, _camera.position.z));
    _camera.position.y = EYE_HEIGHT;
    return true;
  }
  return false;
}

export function disposeNavigation() {
  window.removeEventListener('keydown', _onKeyDown);
  window.removeEventListener('keyup',   _onKeyUp);
  if (_mobile) {
    _canvas?.removeEventListener('touchstart',  _onTouchStart);
    _canvas?.removeEventListener('touchmove',   _onTouchMove);
    _canvas?.removeEventListener('touchend',    _onTouchEnd);
    _canvas?.removeEventListener('touchcancel', _onTouchEnd);
  } else {
    _canvas?.removeEventListener('click', _requestLock);
    document.removeEventListener('pointerlockchange', _onLockChange);
    document.removeEventListener('mousemove', _onMouseMove);
    if (document.pointerLockElement === _canvas) document.exitPointerLock();
  }
}
