import * as http from "http";
import * as vscode from "vscode";

export interface ServerInfo {
  id: string;
  name: string;
  description: string;
  category: string;
  transport: string;
  enabled: boolean;
  tool_count?: number;
  config_options?: ConfigOption[];
}

export interface ConfigOption {
  name: string;
  description: string;
  type: string;
  default?: string;
  required: boolean;
}

export interface InstanceToolInfo {
  name: string;
  description: string;
  input_schema?: Record<string, unknown>;
  server_id?: string;
}

export interface InstanceInfo {
  instance_id: string;
  server_id: string;
  state: string;
  created_at: string;
  last_activity_at?: string;
  total_tool_calls: number;
  total_errors: number;
  tools: InstanceToolInfo[];
}

export interface MetricsSummary {
  total_calls: number;
  successful_calls: number;
  failed_calls: number;
  avg_latency_ms: number;
  active_instances: number;
  total_servers: number;
}

export interface RecentCall {
  tool_name: string;
  server_id: string;
  success: boolean;
  latency_ms: number;
  timestamp: string;
  error_message?: string;
}

export interface BridgeStatus {
  running: boolean;
  uptime_seconds?: number;
  active_instances: number;
  total_servers: number;
}

/**
 * HTTP client for communicating with the MCP Grid bridge management API.
 *
 * The management API runs on port 19420 (configurable) alongside the MCP
 * server process. All endpoints are under /api/.
 */
export class BridgeClient {
  private _baseUrl: string;

  constructor() {
    this._baseUrl = this._buildBaseUrl();
  }

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

  /** Refresh the base URL from current configuration. */
  refreshConfig(): void {
    this._baseUrl = this._buildBaseUrl();
  }

  /** Check if the bridge is reachable. */
  async isAlive(): Promise<boolean> {
    try {
      await this._get("/api/status");
      return true;
    } catch {
      return false;
    }
  }

  /** Get bridge status. */
  async getStatus(): Promise<BridgeStatus> {
    return this._get<BridgeStatus>("/api/status");
  }

  /** List all registered servers (full catalog). */
  async listServers(): Promise<ServerInfo[]> {
    return this._get<ServerInfo[]>("/api/servers");
  }

  /** List only user-installed servers (excludes pre-loaded catalog). */
  async listInstalledServers(): Promise<ServerInfo[]> {
    return this._get<ServerInfo[]>("/api/servers?exclude_source=catalog");
  }

  /** Get details for a specific server. */
  async getServer(serverId: string): Promise<ServerInfo> {
    return this._get<ServerInfo>(
      `/api/servers/${encodeURIComponent(serverId)}`
    );
  }

  /** Enable a server. */
  async enableServer(serverId: string): Promise<void> {
    await this._request(
      "PATCH",
      `/api/servers/${encodeURIComponent(serverId)}/toggle`,
      { enabled: true }
    );
  }

  /** Disable a server. */
  async disableServer(serverId: string): Promise<void> {
    await this._request(
      "PATCH",
      `/api/servers/${encodeURIComponent(serverId)}/toggle`,
      { enabled: false }
    );
  }

  /** List active instances. */
  async listInstances(): Promise<InstanceInfo[]> {
    return this._get<InstanceInfo[]>("/api/instances");
  }

  /** Activate a server instance. */
  async activateServer(
    serverId: string,
    config?: Record<string, string>
  ): Promise<{ instance_id: string }> {
    return this._request<{ instance_id: string }>("POST", "/api/instances", {
      server_id: serverId,
      config,
    });
  }

  /** Deactivate an instance. */
  async deactivateInstance(instanceId: string): Promise<void> {
    await this._request(
      "DELETE",
      `/api/instances/${encodeURIComponent(instanceId)}`
    );
  }

  /** Get metrics summary. */
  async getMetrics(): Promise<MetricsSummary> {
    return this._get<MetricsSummary>("/api/metrics/summary");
  }

  /** Get recent tool calls. */
  async getRecentCalls(limit: number = 50): Promise<RecentCall[]> {
    return this._get<RecentCall[]>(`/api/metrics/calls?limit=${limit}`);
  }

  /** Register a new server with full metadata. */
  async registerServer(
    server: Record<string, unknown>
  ): Promise<ServerInfo> {
    return this._request<ServerInfo>("POST", "/api/servers", server);
  }

  /** Import an MCP config. */
  async importConfig(
    config: Record<string, unknown>
  ): Promise<{ imported: number }> {
    return this._request<{ imported: number }>(
      "POST",
      "/api/servers/import",
      config
    );
  }

  // -- Low-level HTTP helpers --

  private _get<T>(path: string): Promise<T> {
    return new Promise((resolve, reject) => {
      const url = `${this._baseUrl}${path}`;
      http
        .get(url, { timeout: 10000 }, (res) => {
          let data = "";
          res.on("data", (chunk) => (data += chunk));
          res.on("end", () => {
            if (
              res.statusCode &&
              res.statusCode >= 200 &&
              res.statusCode < 300
            ) {
              try {
                resolve(JSON.parse(data) as T);
              } catch {
                reject(new Error(`Invalid JSON response from ${path}`));
              }
            } else {
              reject(
                new Error(`HTTP ${res.statusCode}: ${data.substring(0, 200)}`)
              );
            }
          });
        })
        .on("error", (err) => reject(err))
        .on("timeout", () => reject(new Error(`Request timeout: ${path}`)));
    });
  }

  /**
   * Generic HTTP request supporting POST, PUT, PATCH, DELETE.
   */
  private _request<T>(
    method: string,
    path: string,
    body?: Record<string, unknown>
  ): Promise<T> {
    return new Promise((resolve, reject) => {
      const url = new URL(`${this._baseUrl}${path}`);
      const payload = body ? JSON.stringify(body) : "";
      const options: http.RequestOptions = {
        hostname: url.hostname,
        port: url.port,
        path: url.pathname + url.search,
        method: method,
        timeout: 15000,
        headers: {
          "Content-Type": "application/json",
          "Content-Length": Buffer.byteLength(payload),
        },
      };

      const req = http.request(options, (res) => {
        let data = "";
        res.on("data", (chunk) => (data += chunk));
        res.on("end", () => {
          if (
            res.statusCode &&
            res.statusCode >= 200 &&
            res.statusCode < 300
          ) {
            try {
              resolve(data ? (JSON.parse(data) as T) : ({} as T));
            } catch {
              resolve({} as T);
            }
          } else {
            reject(
              new Error(`HTTP ${res.statusCode}: ${data.substring(0, 200)}`)
            );
          }
        });
      });

      req.on("error", (err) => reject(err));
      req.on("timeout", () => {
        req.destroy();
        reject(new Error(`Request timeout: ${path}`));
      });

      if (payload) {
        req.write(payload);
      }
      req.end();
    });
  }
}
