import { readFileSync } from "node:fs";
import { resolve } from "node:path";
import { parse } from "yaml";
import { envString } from "../env.js";

export type McpToolParameterSchema = {
  type: "object";
  required?: string[];
  properties?: Record<string, unknown>;
};

export type McpToolDefinition = {
  id: string;
  name: string;
  description: string;
  server: string | null;
  parameters: McpToolParameterSchema;
  tags: string[];
  enabled: boolean;
};

export type McpServerDefinition = {
  id: string;
  name: string;
  description: string;
  endpoint: string;
};

export type PublicMcpToolDefinition = Omit<McpToolDefinition, "server"> & {
  server: string | null;
};

export type PublicMcpServerDefinition = McpServerDefinition;

type McpFile = {
  mcp_servers?: McpServerDefinition[];
  mcp_tools?: McpToolDefinition[];
};

const defaultPath = (): string =>
  envString(process.env.MCP_CONFIG_PATH, resolve(process.cwd(), "../../config/mcp.yml"));

const isToolParameterSchema = (value: unknown): value is McpToolParameterSchema => {
  if (!value || typeof value !== "object") return false;
  const v = value as McpToolParameterSchema;
  return v.type === "object";
};

export const loadMcpTools = (
  configPath: string = defaultPath(),
): McpToolDefinition[] => {
  let raw: string;
  try {
    raw = readFileSync(configPath, "utf8");
  } catch (error) {
    const code = (error as NodeJS.ErrnoException).code;
    if (code === "ENOENT") return [];
    throw error;
  }
  const parsed = parse(raw) as McpFile | null;
  if (!parsed || !Array.isArray(parsed.mcp_tools)) return [];
  return parsed.mcp_tools
    .filter((tool) => tool && typeof tool === "object" && typeof tool.id === "string")
    .map((tool) => ({
      id: String(tool.id).trim(),
      name: String(tool.name ?? tool.id).trim(),
      description: String(tool.description ?? "").trim(),
      server: typeof tool.server === "string" ? tool.server : null,
      parameters: isToolParameterSchema(tool.parameters)
        ? tool.parameters
        : ({ type: "object", properties: {}, required: [] } satisfies McpToolParameterSchema),
      tags: Array.isArray(tool.tags) ? tool.tags.map(String) : [],
      enabled: tool.enabled !== false,
    }))
    .filter((tool) => tool.id.length > 0);
};

export const loadMcpServers = (
  configPath: string = defaultPath(),
): McpServerDefinition[] => {
  let raw: string;
  try {
    raw = readFileSync(configPath, "utf8");
  } catch (error) {
    const code = (error as NodeJS.ErrnoException).code;
    if (code === "ENOENT") return [];
    throw error;
  }
  const parsed = parse(raw) as McpFile | null;
  if (!parsed || !Array.isArray(parsed.mcp_servers)) return [];
  return parsed.mcp_servers
    .filter((s) => s && typeof s === "object" && typeof s.id === "string")
    .map((s) => ({
      id: String(s.id).trim(),
      name: String(s.name ?? s.id).trim(),
      description: String(s.description ?? "").trim(),
      endpoint: String(s.endpoint ?? "").trim(),
    }))
    .filter((s) => s.id.length > 0);
};

export const enabledMcpTools = (tools: McpToolDefinition[] = loadMcpTools()): McpToolDefinition[] =>
  tools.filter((tool) => tool.enabled);

export const toPublicMcpTool = (tool: McpToolDefinition): PublicMcpToolDefinition => ({ ...tool });
export const toPublicMcpServer = (server: McpServerDefinition): PublicMcpServerDefinition => ({ ...server });
