import type { McpdClient } from './http/mcpd-client.js';
import type { McpRouter } from './router.js';
import { McpdUpstream } from './upstream/mcpd.js';

interface McpdServer {
  id: string;
  name: string;
  description?: string;
  transport: string;
  status?: string;
}

/**
 * Discovers MCP servers from mcpd and registers them as upstreams in the router.
 * Called periodically or on demand to keep the router in sync with mcpd.
 */
export async function refreshUpstreams(router: McpRouter, mcpdClient: McpdClient): Promise<string[]> {
  const servers = await mcpdClient.get<McpdServer[]>('/api/v1/servers');
  return syncUpstreams(router, mcpdClient, servers);
}

/**
 * Discovers MCP servers scoped to a project and registers them as upstreams.
 * Uses the project-servers endpoint that returns only servers linked to the project.
 *
 * @param authToken - Optional bearer token forwarded to mcpd for RBAC checks.
 */
export async function refreshProjectUpstreams(
  router: McpRouter,
  mcpdClient: McpdClient,
  projectName: string,
  authToken?: string,
): Promise<string[]> {
  const path = `/api/v1/projects/${encodeURIComponent(projectName)}/servers`;

  let servers: McpdServer[];
  if (authToken) {
    // Forward the client's auth token to mcpd so RBAC applies
    const result = await mcpdClient.forward('GET', path, '', undefined, authToken);
    if (result.status >= 400) {
      throw new Error(`Failed to fetch project servers: ${result.status}`);
    }
    servers = result.body as McpdServer[];
  } else {
    servers = await mcpdClient.get<McpdServer[]>(path);
  }

  return syncUpstreams(router, mcpdClient, servers);
}

/**
 * Fetch a project's LLM config (llmProvider, llmModel) from mcpd.
 * These are the project-level "recommendations" — local overrides take priority.
 */
export interface ProjectLlmConfig {
  llmProvider?: string;
  llmModel?: string;
  proxyModel?: string;
  gated?: boolean;
  serverOverrides?: Record<string, { proxyModel?: string }>;
}

export async function fetchProjectLlmConfig(
  mcpdClient: McpdClient,
  projectName: string,
): Promise<ProjectLlmConfig> {
  try {
    const project = await mcpdClient.get<{
      llmProvider?: string;
      llmModel?: string;
      proxyModel?: string;
      gated?: boolean;
      serverOverrides?: Record<string, { proxyModel?: string }>;
    }>(`/api/v1/projects/${encodeURIComponent(projectName)}`);
    const config: ProjectLlmConfig = {};
    if (project.llmProvider) config.llmProvider = project.llmProvider;
    if (project.llmModel) config.llmModel = project.llmModel;
    // proxyModel is the primary field; gated is derived from it.
    // Backward compat: if proxyModel is empty, infer from gated boolean.
    if (project.proxyModel) {
      config.proxyModel = project.proxyModel;
    } else if (project.gated === false) {
      config.proxyModel = 'content-pipeline';
    } else {
      config.proxyModel = 'default';
    }
    config.gated = config.proxyModel === 'default' || config.proxyModel === 'gate';
    if (project.serverOverrides) config.serverOverrides = project.serverOverrides;
    return config;
  } catch {
    return {};
  }
}

/** Shared sync logic: reconcile a router's upstreams with a server list. */
function syncUpstreams(router: McpRouter, mcpdClient: McpdClient, servers: McpdServer[]): string[] {
  const registered: string[] = [];

  // Remove stale upstreams
  const currentNames = new Set(router.getUpstreamNames());
  const serverNames = new Set(servers.map((s) => s.name));
  for (const name of currentNames) {
    if (!serverNames.has(name)) {
      router.removeUpstream(name);
    }
  }

  // Add/update upstreams for each server
  for (const server of servers) {
    if (!currentNames.has(server.name)) {
      const upstream = new McpdUpstream(server.id, server.name, mcpdClient, server.description);
      router.addUpstream(upstream);
    }
    registered.push(server.name);
  }

  return registered;
}
