import { CacheService } from '@main/services/CacheService'
import mcpService from '@main/services/MCPService'
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
import type { ListToolsResult } from '@modelcontextprotocol/sdk/types.js'
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js'
import type { MCPServer } from '@types'

import { loggerService } from '../../services/LoggerService'
import { reduxService } from '../../services/ReduxService'

const logger = loggerService.withContext('MCPApiService')

// Cache configuration
const MCP_SERVERS_CACHE_KEY = 'api-server:mcp-servers'
const MCP_SERVERS_CACHE_TTL = 5 * 60 * 1000 // 5 minutes

async function handleListToolsRequest(request: any, extra: any): Promise<ListToolsResult> {
  logger.debug('Handling list tools request', { request: request, extra: extra })
  const serverId: string = request.params._meta.serverId
  const serverConfig = await getMcpServerConfigById(serverId)
  if (!serverConfig) {
    throw new Error(`Server not found: ${serverId}`)
  }
  const client = await mcpService.initClient(serverConfig)
  return client.listTools()
}

async function handleCallToolRequest(request: any, extra: any): Promise<any> {
  logger.debug('Handling call tool request', { request: request, extra: extra })
  const serverId: string = request.params._meta.serverId
  const serverConfig = await getMcpServerConfigById(serverId)
  if (!serverConfig) {
    throw new Error(`Server not found: ${serverId}`)
  }
  const client = await mcpService.initClient(serverConfig)
  return client.callTool(request.params)
}

async function getMcpServerConfigById(id: string): Promise<MCPServer | undefined> {
  const servers = await getMCPServersFromRedux()
  return servers.find((s) => s.id === id || s.name === id)
}

/**
 * Get servers directly from Redux store
 */
export async function getMCPServersFromRedux(): Promise<MCPServer[]> {
  try {
    logger.debug('Getting servers from Redux store')

    // Try to get from cache first (faster)
    const cachedServers = CacheService.get<MCPServer[]>(MCP_SERVERS_CACHE_KEY)
    if (cachedServers) {
      logger.debug('MCP servers resolved from cache', { count: cachedServers.length })
      return cachedServers
    }

    // If cache is not available, get fresh data from Redux
    const servers = await reduxService.select<MCPServer[]>('state.mcp.servers')
    const serverList = servers || []

    // Cache the results
    CacheService.set(MCP_SERVERS_CACHE_KEY, serverList, MCP_SERVERS_CACHE_TTL)

    logger.debug('Fetched servers from Redux store', { count: serverList.length })
    return serverList
  } catch (error: any) {
    logger.error('Failed to get servers from Redux', { error })
    return []
  }
}

/**
 * Creates a fresh MCP Server instance for a given server ID.
 *
 * A new Server is created for each transport session because the MCP SDK's
 * Protocol.connect() throws "Already connected" if the Server is already
 * bound to a transport. Since the Claude Agent SDK spawns a new CLI process
 * per query (including resumes), each process establishes a new HTTP
 * transport, so the proxy must provide a fresh Server instance every time.
 */
export async function createMcpServerForTransport(id: string): Promise<Server> {
  const servers = await getMCPServersFromRedux()
  const mcpServer = servers.find((s) => s.id === id || s.name === id)
  if (!mcpServer) {
    throw new Error(`Server not found: ${id}`)
  }

  const server = new Server({ name: mcpServer.name, version: '0.1.0' }, { capabilities: { tools: {} } })
  server.setRequestHandler(ListToolsRequestSchema, handleListToolsRequest)
  server.setRequestHandler(CallToolRequestSchema, handleCallToolRequest)
  return server
}
