/**
 * Codex MCP Helpers
 *
 * Utility functions for working with MCP (Model Context Protocol) server
 * configurations and tool definitions in the Codex adapter.
 */

import type { McpServerDetail, McpServerConfig } from "./session-types.js";

// ─── Generic Utility ──────────────────────────────────────────────────────────

/** Safely cast unknown to Record if it is a non-array object, else null. */
export function asRecord(value: unknown): Record<string, unknown> | null {
  return value && typeof value === "object" && !Array.isArray(value)
    ? value as Record<string, unknown>
    : null;
}

// ─── MCP Server Config Conversion ─────────────────────────────────────────────

/** Convert a raw Codex MCP server config value to a typed McpServerConfig. */
export function toMcpServerConfig(value: unknown): McpServerConfig {
  const cfg = asRecord(value) || {};
  const args = Array.isArray(cfg.args)
    ? cfg.args.filter((a): a is string => typeof a === "string")
    : undefined;
  const env = asRecord(cfg.env) as Record<string, string> | null;

  let type: McpServerConfig["type"] = "sdk";
  if (cfg.type === "stdio" || cfg.type === "sse" || cfg.type === "http" || cfg.type === "sdk") {
    type = cfg.type;
  } else if (typeof cfg.command === "string") {
    type = "stdio";
  } else if (typeof cfg.url === "string") {
    type = "http";
  }

  return {
    type,
    command: typeof cfg.command === "string" ? cfg.command : undefined,
    args,
    env: env || undefined,
    url: typeof cfg.url === "string" ? cfg.url : undefined,
  };
}

/** Convert a typed McpServerConfig back to a raw config object for Codex. */
export function fromMcpServerConfig(config: McpServerConfig): Record<string, unknown> {
  const out: Record<string, unknown> = {};
  if (typeof config.command === "string") out.command = config.command;
  if (Array.isArray(config.args)) out.args = config.args;
  if (config.env) out.env = config.env;
  if (typeof config.url === "string") out.url = config.url;
  return out;
}

/**
 * Normalize a raw MCP server config, keeping only fields supported by the
 * Codex raw MCP config schema.
 */
export function normalizeRawMcpServerConfig(value: unknown): Record<string, unknown> {
  const cfg = asRecord(value) || {};
  const out: Record<string, unknown> = {};

  if (typeof cfg.command === "string") out.command = cfg.command;
  if (Array.isArray(cfg.args)) out.args = cfg.args.filter((a) => typeof a === "string");
  if (typeof cfg.cwd === "string") out.cwd = cfg.cwd;
  if (typeof cfg.url === "string") out.url = cfg.url;
  if (typeof cfg.enabled === "boolean") out.enabled = cfg.enabled;
  if (typeof cfg.required === "boolean") out.required = cfg.required;

  const env = asRecord(cfg.env);
  if (env) out.env = Object.fromEntries(
    Object.entries(env).filter(([, v]) => typeof v === "string"),
  );

  const envHttpHeaders = asRecord(cfg.env_http_headers);
  if (envHttpHeaders) out.env_http_headers = Object.fromEntries(
    Object.entries(envHttpHeaders).filter(([, v]) => typeof v === "string"),
  );

  const httpHeaders = asRecord(cfg.http_headers);
  if (httpHeaders) out.http_headers = Object.fromEntries(
    Object.entries(httpHeaders).filter(([, v]) => typeof v === "string"),
  );

  const asStringArray = (arr: unknown): string[] | undefined =>
    Array.isArray(arr)
      ? arr.filter((x): x is string => typeof x === "string")
      : undefined;

  const disabledTools = asStringArray(cfg.disabled_tools);
  if (disabledTools) out.disabled_tools = disabledTools;
  const enabledTools = asStringArray(cfg.enabled_tools);
  if (enabledTools) out.enabled_tools = enabledTools;
  const envVars = asStringArray(cfg.env_vars);
  if (envVars) out.env_vars = envVars;
  const scopes = asStringArray(cfg.scopes);
  if (scopes) out.scopes = scopes;

  if (typeof cfg.startup_timeout_ms === "number") out.startup_timeout_ms = cfg.startup_timeout_ms;
  if (typeof cfg.startup_timeout_sec === "number") out.startup_timeout_sec = cfg.startup_timeout_sec;
  if (typeof cfg.tool_timeout_sec === "number") out.tool_timeout_sec = cfg.tool_timeout_sec;
  if (typeof cfg.bearer_token === "string") out.bearer_token = cfg.bearer_token;
  if (typeof cfg.bearer_token_env_var === "string") out.bearer_token_env_var = cfg.bearer_token_env_var;

  return out;
}

// ─── MCP Status Helpers ───────────────────────────────────────────────────────

/** Check whether an MCP server config entry is enabled (defaults to true). */
export function isMcpServerEnabled(value: unknown): boolean {
  const cfg = asRecord(value);
  if (!cfg) return true;
  return cfg.enabled !== false;
}

/** Map Codex MCP tool definitions to the McpServerDetail tool format. */
export function mapMcpTools(
  tools: Record<string, { name?: string; annotations?: unknown }> | undefined,
): McpServerDetail["tools"] {
  if (!tools) return [];
  return Object.entries(tools).map(([key, tool]) => {
    const ann = asRecord(tool.annotations);
    const annotations = ann ? {
      readOnly: (ann.readOnly ?? ann.readOnlyHint) === true,
      destructive: (ann.destructive ?? ann.destructiveHint) === true,
      openWorld: (ann.openWorld ?? ann.openWorldHint) === true,
    } : undefined;

    return {
      name: typeof tool.name === "string" ? tool.name : key,
      annotations,
    };
  });
}
