/* ============================================================================
 * FLUXO ZAP · Realtime (WebSocket)
 * ----------------------------------------------------------------------------
 * Conexão persistente com o backend pra receber eventos em tempo real:
 *   - Nova mensagem chegou
 *   - IA está digitando / agindo
 *   - Status de mensagem mudou (entregue, lida)
 *   - Lead mudou de etapa
 *   - Novo prospect no radar
 *   - Métricas do dashboard atualizaram
 *
 * Eventos seguem o formato: { type, payload, ts }
 * Veja BACKEND-CONTRACTS.md → seção "WebSocket Events" pra schema completo.
 * ========================================================================= */

class FluxoRealtime {
  constructor() {
    this.ws = null;
    this.handlers = new Map(); // type → Set<fn>
    this.connected = false;
    this.reconnectTimer = null;
    this.reconnectAttempts = 0;
  }

  connect() {
    const cfg = window.FluxoConfig;
    if (!cfg.ENABLE_REALTIME) return;
    if (cfg.USE_MOCKS) {
      // Em modo mock o "realtime" é simulado pelo mock-server
      if (window.FluxoMockServer) window.FluxoMockServer.startRealtime(this);
      return;
    }

    const token = typeof cfg.AUTH_TOKEN === 'function' ? cfg.AUTH_TOKEN() : cfg.AUTH_TOKEN;
    const ws = typeof cfg.WORKSPACE_ID === 'function' ? cfg.WORKSPACE_ID() : cfg.WORKSPACE_ID;
    const url = `${cfg.WS_URL}?token=${encodeURIComponent(token || '')}&workspace=${encodeURIComponent(ws || '')}`;

    this.ws = new WebSocket(url);

    this.ws.onopen = () => {
      this.connected = true;
      this.reconnectAttempts = 0;
      this.emit('__connected', {});
    };

    this.ws.onmessage = (event) => {
      try {
        const data = JSON.parse(event.data);
        this.emit(data.type, data.payload, data);
      } catch (err) {
        console.warn('[FluxoRealtime] invalid event', err);
      }
    };

    this.ws.onclose = () => {
      this.connected = false;
      this.emit('__disconnected', {});
      this._scheduleReconnect();
    };

    this.ws.onerror = (err) => {
      console.warn('[FluxoRealtime] ws error', err);
    };
  }

  disconnect() {
    if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
    if (this.ws) {
      this.ws.close();
      this.ws = null;
    }
  }

  _scheduleReconnect() {
    const cfg = window.FluxoConfig;
    const backoff = Math.min(cfg.REALTIME_RECONNECT_MS * Math.pow(1.5, this.reconnectAttempts), 30_000);
    this.reconnectAttempts++;
    this.reconnectTimer = setTimeout(() => this.connect(), backoff);
  }

  /**
   * Inscreve em um tipo de evento.
   *
   * Tipos disponíveis (veja BACKEND-CONTRACTS.md):
   *   - message.received  → nova msg do cliente
   *   - message.sent      → msg enviada (com ack)
   *   - message.status    → entregue/lida
   *   - ai.thinking       → IA processando
   *   - ai.typing         → IA digitando
   *   - ai.sent           → IA enviou
   *   - ai.handoff        → IA transferiu pra humano
   *   - lead.created
   *   - lead.updated
   *   - lead.stage_changed
   *   - lead.score_changed
   *   - radar.new_prospect
   *   - dashboard.tick    → atualização periódica dos KPIs
   *
   * @returns função de unsubscribe
   */
  on(eventType, handler) {
    if (!this.handlers.has(eventType)) {
      this.handlers.set(eventType, new Set());
    }
    this.handlers.get(eventType).add(handler);
    return () => this.handlers.get(eventType).delete(handler);
  }

  emit(type, payload, raw) {
    const set = this.handlers.get(type);
    if (set) set.forEach(fn => {
      try { fn(payload, raw); } catch (err) { console.error(err); }
    });
    // Wildcard
    const all = this.handlers.get('*');
    if (all) all.forEach(fn => fn({ type, payload }, raw));
  }

  // Enviar evento pro servidor (ex: marcar como digitando)
  send(type, payload) {
    if (!this.connected || !this.ws) return false;
    this.ws.send(JSON.stringify({ type, payload, ts: Date.now() }));
    return true;
  }
}

window.FluxoRealtime = new FluxoRealtime();
