import { initWorld, onWindowResize, disposeWorld } from './world.js';
import {
  initAgents, updateAgents, getAgentCount,
  disposeAgents, getAgentStates, applyAgentStates,
  getTimmyGroup, applySlap, getCameraShakeStrength,
  TIMMY_WORLD_POS,
} from './agents.js';
import { initEffects, updateEffects, disposeEffects } from './effects.js';
import { initUI, updateUI } from './ui.js';
import { initInteraction, disposeInteraction, registerSlapTarget } from './interaction.js';
import { initWebSocket, getConnectionState, getJobCount } from './websocket.js';
import { initPaymentPanel } from './payment.js';
import { initSessionPanel } from './session.js';
import { initNostrIdentity } from './nostr-identity.js';
import { warmup as warmupEdgeWorker, onReady as onEdgeWorkerReady } from './edge-worker-client.js';
import { setEdgeWorkerReady } from './ui.js';
import { initTimmyId } from './timmy-id.js';
import { AGENT_DEFS } from './agent-defs.js';
import { initNavigation, updateNavigation, disposeNavigation } from './navigation.js';
import { initHudLabels, updateHudLabels, disposeHudLabels } from './hud-labels.js';

let running = false;
let canvas  = null;
let _lastTime = performance.now();

function buildWorld(firstInit, stateSnapshot) {
  const { scene, camera, renderer } = initWorld(canvas);
  canvas = renderer.domElement;

  initEffects(scene);
  initAgents(scene);

  if (stateSnapshot) applyAgentStates(stateSnapshot);

  // Navigation replaces OrbitControls
  initNavigation(camera, renderer);

  initInteraction(camera, renderer);
  registerSlapTarget(getTimmyGroup(), applySlap);

  // AR floating labels
  initHudLabels(camera, AGENT_DEFS, TIMMY_WORLD_POS);

  if (firstInit) {
    initUI();
    initWebSocket(scene);
    initPaymentPanel();
    initSessionPanel();
    void initNostrIdentity('/api');
    warmupEdgeWorker();
    onEdgeWorkerReady(() => setEdgeWorkerReady());
    void initTimmyId();
  }

  const ac = new AbortController();
  window.addEventListener('resize', () => onWindowResize(camera, renderer), { signal: ac.signal });

  let frameCount  = 0;
  let lastFpsTime = performance.now();
  let currentFps  = 0;

  running = true;

  function animate() {
    if (!running) return;
    requestAnimationFrame(animate);

    const now = performance.now();
    const deltaMs = now - _lastTime;
    _lastTime = now;

    frameCount++;
    if (now - lastFpsTime >= 1000) {
      currentFps  = Math.round(frameCount * 1000 / (now - lastFpsTime));
      frameCount  = 0;
      lastFpsTime = now;
    }

    // FPS navigation
    updateNavigation(deltaMs);

    updateEffects(now);
    updateAgents(now);
    updateUI({
      fps: currentFps,
      agentCount: getAgentCount(),
      jobCount: getJobCount(),
      connectionState: getConnectionState(),
    });

    // Camera shake
    const shakeStr = getCameraShakeStrength();
    let sx = 0, sy = 0;
    if (shakeStr > 0) {
      const mag = shakeStr * 0.22;
      sx = (Math.random() - 0.5) * mag;
      sy = (Math.random() - 0.5) * mag * 0.45;
      camera.position.x += sx;
      camera.position.y += sy;
    }

    renderer.render(scene, camera);

    if (shakeStr > 0) {
      camera.position.x -= sx;
      camera.position.y -= sy;
    }

    // AR label positions (after render so NDC is current)
    updateHudLabels(camera, renderer);
  }

  animate();
  return { scene, renderer, ac };
}

function teardown({ scene, renderer, ac }) {
  running = false;
  ac.abort();
  disposeNavigation();
  disposeInteraction();
  disposeHudLabels();
  disposeEffects();
  disposeAgents();
  disposeWorld(renderer, scene);
}

function main() {
  const $overlay = document.getElementById('webgl-recovery-overlay');
  let handle = buildWorld(true, null);

  canvas.addEventListener('webglcontextlost', event => {
    event.preventDefault();
    running = false;
    if ($overlay) $overlay.style.display = 'flex';
  });

  canvas.addEventListener('webglcontextrestored', () => {
    const snapshot = getAgentStates();
    teardown(handle);
    _lastTime = performance.now();
    handle = buildWorld(false, snapshot);
    if ($overlay) $overlay.style.display = 'none';
  });
}

main();

if (import.meta.env.PROD && 'serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register(import.meta.env.BASE_URL + 'sw.js').catch(() => {});
  });
}
