import * as THREE from 'three';
import { scene } from './world.js'; // Import the scene
import { setAgentState, setSpeechBubble, applyAgentStates, setMood, TIMMY_WORLD_POS } from './agents.js';
import { appendSystemMessage, appendDebateMessage, showCostTicker, updateCostTicker } from './ui.js';
import { sentiment } from './edge-worker-client.js';
import { setLabelState, setLabelLastTask } from './hud-labels.js';
import { createJobIndicator, dissolveJobIndicator } from './effects.js';
import { getPubkey } from './nostr-identity.js';

function resolveWsUrl() {
  const explicit = import.meta.env.VITE_WS_URL;
  if (explicit) return explicit;
  const proto = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
  return `${proto}//${window.location.host}/api/ws`;
}

const WS_URL = resolveWsUrl();

let ws = null;
let connectionState = 'disconnected';
let jobCount = 0;
let reconnectTimer = null;
let visitorId = null;
const RECONNECT_DELAY_MS = 5000;

// Map to keep track of active job indicator positions for offsetting
const _jobIndicatorOffsets = new Map();
let _nextJobOffsetIndex = 0;

export function initWebSocket(_scene) {
  visitorId = crypto.randomUUID();
  connect();
}

function connect() {
  if (ws) { ws.onclose = null; ws.close(); }
  connectionState = 'connecting';

  try {
    ws = new WebSocket(WS_URL);
  } catch {
    connectionState = 'disconnected';
    scheduleReconnect();
    return;
  }

  ws.onopen = () => {
    connectionState = 'connected';
    clearTimeout(reconnectTimer);
    const npub = getPubkey();
    send({ type: 'visitor_enter', visitorId, visitorName: 'visitor', npub });
  };

  ws.onmessage = event => {
    try { handleMessage(JSON.parse(event.data)); } catch { /* ignore */ }
  };

  ws.onerror = () => { connectionState = 'disconnected'; };
  ws.onclose = () => { connectionState = 'disconnected'; scheduleReconnect(); };
}

function scheduleReconnect() {
  clearTimeout(reconnectTimer);
  reconnectTimer = setTimeout(connect, RECONNECT_DELAY_MS);
}

function send(payload) {
  if (ws && ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify(payload));
  }
}

function handleMessage(msg) {
  switch (msg.type) {
    case 'ping':
      send({ type: 'pong' });
      break;

    case 'world_state': {
      if (msg.agentStates) applyAgentStates(msg.agentStates);
      if (msg.recentEvents) {
        const last = msg.recentEvents.slice(-3);
        last.forEach(ev => appendSystemMessage(ev.summary || ev.type));
      }
      break;
    }

    case 'timmy_state': {
      break;
    }

    case 'agent_state': {
      if (msg.agentId && msg.state) {
        setAgentState(msg.agentId, msg.state);
        setLabelState(msg.agentId, msg.state);
      }
      break;
    }

    case 'job_started': {
      jobCount++;
      if (msg.agentId) {
        setAgentState(msg.agentId, 'active');
        setLabelState(msg.agentId, 'active');
      }
      appendSystemMessage(`job ${(msg.jobId || '').slice(0, 8)} started`);

      // Spawn 3D job indicator
      if (msg.jobId && msg.category) {
        const offsetMultiplier = _jobIndicatorOffsets.size; // Simple way to spread them out
        const indicatorPosition = TIMMY_WORLD_POS.clone().add(
          new THREE.Vector3(
            (offsetMultiplier % 2 === 0 ? 1 : -1) * (Math.floor(offsetMultiplier / 2) + 1) * 0.7, // Alternate left/right
            3.5, // Height above Timmy
            -0.5
          )
        );
        const indicator = createJobIndicator(msg.category, msg.jobId, indicatorPosition);
        scene.add(indicator);
        _jobIndicatorOffsets.set(msg.jobId, indicatorPosition); // Store position, not index, for cleaner removal
      }
      break;
    }

    case 'agent_task_summary': {
      if (msg.agentId && msg.summary) {
        setLabelLastTask(msg.agentId, msg.summary);
      }
      break;
    }

    case 'job_completed': {
      if (jobCount > 0) jobCount--;
      if (msg.agentId) {
        setAgentState(msg.agentId, 'idle');
        setLabelState(msg.agentId, 'idle');
        setLabelLastTask(msg.agentId, `job ${(msg.jobId || '').slice(0, 8)} completed`);
      }
      appendSystemMessage(`job ${(msg.jobId || '').slice(0, 8)} complete`);

      // Dissolve 3D job indicator
      if (msg.jobId) {
        dissolveJobIndicator(msg.jobId, scene);
        _jobIndicatorOffsets.delete(msg.jobId);
      }
      break;
    }

    case 'chat': {
      if (msg.agentId === 'timmy') {
        // Timmy's AI reply: show in speech bubble + event log
        if (msg.text) setSpeechBubble(msg.text);
        appendSystemMessage('Timmy: ' + (msg.text || '').slice(0, 80));
        // Sentiment-driven facial expression on inbound Timmy messages
        if (msg.text) {
          sentiment(msg.text).then(s => {
            setMood(s.label);
            setTimeout(() => setMood(null), 10_000);
          }).catch(() => {});
        }
      } else if (msg.agentId === 'visitor') {
        // Another visitor's message: event log only (don't hijack the speech bubble)
        appendSystemMessage((msg.text || '').slice(0, 80));
      } else {
        // System agent messages (delta payment confirmations, etc.): speech bubble
        if (msg.text) setSpeechBubble(msg.text);
      }
      break;
    }

    case 'agent_debate': {
      // Debate messages from Beta-A, Beta-B, or final verdict (#21)
      const isVerdict = msg.position === 'verdict';
      appendDebateMessage(msg.agent, msg.argument, isVerdict, msg.accepted);
      if (isVerdict) {
        setSpeechBubble(msg.argument);
      }
      break;
    }

    case 'cost_update': {
      // Real-time cost ticker (#68): show estimated cost when job starts,
      // update to final charged amount when job completes.
      if (msg.isFinal) {
        updateCostTicker(msg.sats, true);
      } else {
        showCostTicker(msg.sats);
      }
      break;
    }

    case 'agent_commentary': {
      // Agent narration during job lifecycle
      if (msg.text) {
        setSpeechBubble(msg.text);
        appendSystemMessage(`${msg.agentId}: ${(msg.text || '').slice(0, 80)}`);
      }
      break;
    }

    case 'agent_count':
    case 'visitor_count':
      break;

    default:
      break;
  }
}

export function sendVisitorMessage(text) {
  send({ type: 'visitor_message', visitorId, text });
}

export function getConnectionState() { return connectionState; }
export function getJobCount() { return jobCount; }
