/**
 * config.ts — MCP server configuration types and readers.
 *
 * Purpose: centralize all MCP config resolution so connection-manager and
 * the extension wrapper share one authoritative source of truth.
 *
 * Consumer: McpConnectionManager (getOrConnect), mcp_servers tool (list).
 */
import { existsSync, readFileSync } from "node:fs";
import { homedir } from "node:os";
import { join } from "node:path";

export interface OAuthConfig {
	clientId: string;
	clientSecret?: string;
	scopes?: string[];
	redirectUrl?: string;
}

export interface McpServerConfig {
	name: string;
	transport: "stdio" | "http" | "unknown";
	command?: string;
	args?: string[];
	env?: Record<string, string>;
	cwd?: string;
	url?: string;
	headers?: Record<string, string>;
	oauth?: OAuthConfig;
}

/**
 * Read all MCP server configs from config files in priority order.
 * First hit for a given server name wins (project-local overrides global).
 *
 * Purpose: provide the canonical ordered list of available MCP servers from
 * all supported config locations.
 *
 * Consumer: McpConnectionManager.readConfigs(), mcp_servers tool.
 */
export function readMcpConfigs(): McpServerConfig[] {
	const servers: McpServerConfig[] = [];
	const seen = new Set<string>();
	const sfHome = process.env["SF_HOME"] ?? join(homedir(), ".sf");
	const configPaths = [
		join(process.cwd(), ".mcp.json"),
		join(process.cwd(), ".sf", "mcp.json"),
		join(sfHome, "mcp.json"),
		join(sfHome, "agent", "mcp.json"),
		join(homedir(), ".mcp.json"),
	];
	for (const configPath of configPaths) {
		try {
			if (!existsSync(configPath)) continue;
			const raw = readFileSync(configPath, "utf-8");
			const data = JSON.parse(raw) as Record<string, unknown>;
			const mcpServers = (data["mcpServers"] ?? data["servers"]) as
				| Record<string, unknown>
				| undefined;
			if (!mcpServers || typeof mcpServers !== "object") continue;
			for (const [name, config] of Object.entries(mcpServers)) {
				if (seen.has(name)) continue;
				seen.add(name);
				const cfg = config as Record<string, unknown>;
				const hasCommand = typeof cfg["command"] === "string";
				const hasUrl = typeof cfg["url"] === "string";
				const transport: McpServerConfig["transport"] = hasCommand
					? "stdio"
					: hasUrl
						? "http"
						: "unknown";
				const hasHeaders =
					hasUrl && cfg["headers"] && typeof cfg["headers"] === "object";
				const hasOAuth =
					hasUrl && cfg["oauth"] && typeof cfg["oauth"] === "object";
				servers.push({
					name,
					transport,
					...(hasCommand && {
						command: cfg["command"] as string,
						args: Array.isArray(cfg["args"])
							? (cfg["args"] as string[])
							: undefined,
						env:
							cfg["env"] && typeof cfg["env"] === "object"
								? (cfg["env"] as Record<string, string>)
								: undefined,
						cwd: typeof cfg["cwd"] === "string" ? cfg["cwd"] : undefined,
					}),
					...(hasUrl && { url: cfg["url"] as string }),
					headers: hasHeaders
						? (cfg["headers"] as Record<string, string>)
						: undefined,
					oauth: hasOAuth ? (cfg["oauth"] as OAuthConfig) : undefined,
				});
			}
		} catch {
			// Non-fatal — config file may not exist or be malformed
		}
	}
	return servers;
}

/**
 * Look up a single server config by name (case-insensitive fallback).
 *
 * Purpose: resolve a user-supplied server name to its canonical config entry,
 * allowing minor casing differences without failing the lookup.
 *
 * Consumer: McpConnectionManager.getOrConnect().
 */
export function getServerConfig(
	name: string,
	configs: McpServerConfig[],
): McpServerConfig | undefined {
	const trimmed = name.trim();
	return configs.find(
		(s) => s.name === trimmed || s.name.toLowerCase() === trimmed.toLowerCase(),
	);
}
