import { z } from 'zod';
import type { ToolDefinition, ToolResult, ToolContext } from '../agent/tools/types.js';
import type { MCPServerManager } from './manager.js';
import type { MCPToolDefinition } from './types.js';

/**
 * Convert JSON Schema to Zod schema
 *
 * This is a simplified converter that handles common cases.
 * For full JSON Schema support, consider using a library like zod-to-json-schema.
 */
function jsonSchemaToZod(schema: Record<string, unknown>): z.ZodType {
  const type = schema.type as string;
  const properties = schema.properties as Record<string, unknown> | undefined;
  const required = schema.required as string[] | undefined;

  switch (type) {
    case 'string':
      let strSchema = z.string();
      if (schema.description) {
        strSchema = strSchema.describe(schema.description as string);
      }
      return strSchema;

    case 'number':
    case 'integer':
      let numSchema = z.number();
      if (schema.description) {
        numSchema = numSchema.describe(schema.description as string);
      }
      return numSchema;

    case 'boolean':
      let boolSchema = z.boolean();
      if (schema.description) {
        boolSchema = boolSchema.describe(schema.description as string);
      }
      return boolSchema;

    case 'array':
      const items = schema.items as Record<string, unknown> | undefined;
      let arraySchema = z.array(items ? jsonSchemaToZod(items) : z.unknown());
      if (schema.description) {
        arraySchema = arraySchema.describe(schema.description as string);
      }
      return arraySchema;

    case 'object':
      if (!properties) {
        return z.record(z.unknown());
      }

      const shape: Record<string, z.ZodType> = {};
      for (const [key, propSchema] of Object.entries(properties)) {
        let propZod = jsonSchemaToZod(propSchema as Record<string, unknown>);

        // Make optional if not in required array
        if (!required?.includes(key)) {
          propZod = propZod.optional();
        }

        shape[key] = propZod;
      }

      let objSchema = z.object(shape);
      if (schema.description) {
        objSchema = objSchema.describe(schema.description as string);
      }
      return objSchema;

    default:
      return z.unknown();
  }
}

/**
 * Create a tool definition from an MCP tool
 */
export function createMCPToolDefinition(
  mcpTool: MCPToolDefinition & { prefixedName: string; serverName: string },
  manager: MCPServerManager
): ToolDefinition {
  // Convert JSON Schema to Zod
  const zodSchema = jsonSchemaToZod(mcpTool.inputSchema) as z.ZodObject<Record<string, z.ZodType>>;

  return {
    name: mcpTool.prefixedName,
    description: `[MCP:${mcpTool.serverName}] ${mcpTool.description}`,
    category: 'mcp' as any, // MCP tools get their own category
    schema: zodSchema,

    async execute(input: Record<string, unknown>, context: ToolContext): Promise<ToolResult> {
      context.logger.debug(
        { tool: mcpTool.prefixedName, server: mcpTool.serverName },
        'Calling MCP tool'
      );

      const result = await manager.callTool(mcpTool.prefixedName, input);

      if (!result.success) {
        return {
          success: false,
          error: result.error,
        };
      }

      return {
        success: true,
        data: result.result,
        metadata: {
          mcpServer: mcpTool.serverName,
          mcpTool: mcpTool.name,
        },
      };
    },
  };
}

/**
 * Create tool definitions for all MCP tools
 */
export function createMCPTools(manager: MCPServerManager): ToolDefinition[] {
  const mcpTools = manager.getAllTools();
  return mcpTools.map((tool) => createMCPToolDefinition(tool, manager));
}
