import * as vscode from "vscode";
import WebSocket from "ws";

export type EventType =
  | "instance_activated"
  | "instance_deactivated"
  | "tool_call"
  | "server_registered"
  | "server_enabled"
  | "server_disabled"
  | "health_check"
  | "error";

export interface BridgeEvent {
  type: EventType;
  data: Record<string, unknown>;
  timestamp: string;
}

/**
 * WebSocket client for real-time bridge event streaming.
 * Implements the Observer pattern -- emits events to registered listeners.
 */
export class BridgeWebSocket {
  private _ws: WebSocket | null = null;
  private _reconnectTimer: ReturnType<typeof setTimeout> | null = null;
  private _emitter = new vscode.EventEmitter<BridgeEvent>();
  private _connected = false;
  private _disposed = false;
  private _reconnectAttempts = 0;
  private readonly _maxReconnectAttempts = 20;
  private readonly _baseReconnectDelay = 1000;

  /** Event fired when a bridge event is received. */
  readonly onEvent = this._emitter.event;

  get isConnected(): boolean {
    return this._connected;
  }

  /** Connect to the bridge WebSocket endpoint. */
  connect(): void {
    if (this._disposed) {
      return;
    }

    const config = vscode.workspace.getConfiguration("mcpGrid");
    const host = config.get<string>("bridgeHost", "127.0.0.1");
    const port = config.get<number>("bridgePort", 19420);
    const url = `ws://${host}:${port}/ws`;

    try {
      this._ws = new WebSocket(url);

      this._ws.on("open", () => {
        this._connected = true;
        this._reconnectAttempts = 0;
        console.log("[MCP Grid] WebSocket connected");
      });

      this._ws.on("message", (data: WebSocket.Data) => {
        try {
          const event = JSON.parse(data.toString()) as BridgeEvent;
          this._emitter.fire(event);
        } catch {
          console.warn("[MCP Grid] Invalid WebSocket message:", data.toString().substring(0, 100));
        }
      });

      this._ws.on("close", () => {
        this._connected = false;
        console.log("[MCP Grid] WebSocket disconnected");
        this._scheduleReconnect();
      });

      this._ws.on("error", (err) => {
        console.warn("[MCP Grid] WebSocket error:", err.message);
        this._connected = false;
      });
    } catch (err) {
      console.warn("[MCP Grid] Failed to create WebSocket:", err);
      this._scheduleReconnect();
    }
  }

  /** Disconnect from the bridge. */
  disconnect(): void {
    this._clearReconnectTimer();
    if (this._ws) {
      this._ws.close();
      this._ws = null;
    }
    this._connected = false;
  }

  /** Dispose all resources. */
  dispose(): void {
    this._disposed = true;
    this.disconnect();
    this._emitter.dispose();
  }

  private _scheduleReconnect(): void {
    if (this._disposed || this._reconnectAttempts >= this._maxReconnectAttempts) {
      return;
    }

    this._clearReconnectTimer();

    // Exponential backoff with jitter, capped at 15 seconds
    const delay = Math.min(
      this._baseReconnectDelay * Math.pow(1.5, this._reconnectAttempts) +
        Math.random() * 500,
      15_000,
    );

    this._reconnectTimer = setTimeout(() => {
      this._reconnectAttempts++;
      this.connect();
    }, delay);
  }

  private _clearReconnectTimer(): void {
    if (this._reconnectTimer) {
      clearTimeout(this._reconnectTimer);
      this._reconnectTimer = null;
    }
  }
}
