// ═══════════════════════════════════════════════════════
//  CVC Dashboard — WebSocket Client
// ═══════════════════════════════════════════════════════
import type { WSMessage } from './types';

type WSCallback = (msg: WSMessage) => void;

export class DashboardWS {
  private ws: WebSocket | null = null;
  private url: string;
  private listeners: WSCallback[] = [];
  private reconnectTimer: ReturnType<typeof setTimeout> | null = null;
  private reconnectDelay: number = 2000;
  private maxReconnectDelay: number = 30000;
  private alive: boolean = true;

  constructor() {
    const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
    this.url = `${proto}//${location.host}/ws/dashboard`;
  }

  connect(): void {
    if (this.ws?.readyState === WebSocket.OPEN) return;
    try {
      this.ws = new WebSocket(this.url);
    } catch {
      this.scheduleReconnect();
      return;
    }

    this.ws.onopen = () => {
      this.reconnectDelay = 2000;
      this.updateIndicator(true);
    };

    this.ws.onmessage = (ev: MessageEvent) => {
      try {
        const msg: WSMessage = JSON.parse(ev.data);
        for (const cb of this.listeners) cb(msg);
      } catch { /* ignore bad frames */ }
    };

    this.ws.onclose = () => {
      this.updateIndicator(false);
      if (this.alive) this.scheduleReconnect();
    };

    this.ws.onerror = () => {
      this.ws?.close();
    };
  }

  on(cb: WSCallback): void {
    this.listeners.push(cb);
  }

  off(cb: WSCallback): void {
    this.listeners = this.listeners.filter(l => l !== cb);
  }

  send(msg: WSMessage): void {
    if (this.ws?.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify(msg));
    }
  }

  disconnect(): void {
    this.alive = false;
    if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
    this.ws?.close();
  }

  private scheduleReconnect(): void {
    if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
    this.reconnectTimer = setTimeout(() => {
      this.connect();
    }, this.reconnectDelay);
    this.reconnectDelay = Math.min(this.reconnectDelay * 1.5, this.maxReconnectDelay);
  }

  private updateIndicator(connected: boolean): void {
    const dot = document.querySelector('#ws-status .ws-dot');
    const label = document.querySelector('#ws-status .ws-label');
    if (dot) {
      dot.classList.toggle('connected', connected);
      dot.classList.toggle('disconnected', !connected);
    }
    if (label) label.textContent = connected ? 'Connected' : 'Disconnected';
  }
}
