import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'
import type { McpHttpConfig } from './client'
import type { CallToolResultPayload, McpTool } from './types'
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'
import { McpClient } from './client'

/**
 * MCP HTTP Client
 *
 * Responsibilities:
 * - Manage HTTP connection to a single MCP server
 * - Retrieve and call tools via MCP protocol
 * - Handle session management and authentication
 * - Implement McpClient interface for ToolRegistry
 */
export class McpHttpClient extends McpClient {
  private client?: Client
  private cachedTools: McpTool[] = []
  private _connected = false
  private lastError?: string

  constructor(public readonly config: McpHttpConfig) {
    super()
    if (this._enableLogging) {
      console.warn(`🔌 McpHttpClient created for: ${config.name}`)
    }
  }

  get connected(): boolean {
    return this._connected && !!this.client
  }

  async initialize(): Promise<void> {
    if (this._connected) {
      return
    }

    try {
      this.client = await this.createClient()
      // Preload tools list
      await this.refreshToolCache()
      this._connected = true
      this.lastError = undefined
      if (this._enableLogging) {
        console.warn(
          `✅ McpHttpClient initialized: ${this.config.name} (${this.cachedTools.length} tools)`,
        )
      }
    }
    catch (error) {
      const msg = error instanceof Error ? error.message : String(error)
      this.lastError = msg
      if (this._enableLogging) {
        console.error(
          `Failed to initialize HTTP MCP client "${this.config.name}":`,
          msg,
        )
      }
      throw error
    }
  }

  /**
   * Create MCP client with HTTP transport
   */
  private async createClient(): Promise<Client> {
    const headers: Record<string, string> = { ...this.config.headers }

    // Handle bearer token authentication
    if (this.config.auth?.type === 'bearer') {
      const token
        = typeof this.config.auth.token === 'function'
          ? this.config.auth.token()
          : this.config.auth.token
      if (token)
        headers.Authorization = `Bearer ${token}`
    }

    const requestInit: RequestInit
      = Object.keys(headers).length > 0 ? { headers } : {}

    const transport = new StreamableHTTPClientTransport(
      new URL(this.config.url),
      requestInit ? { requestInit } : undefined,
    )

    const client = new Client(
      {
        name: `mcp-http-client-${this.config.name}`,
        version: '1.0.0',
      },
      {
        capabilities: {
          roots: {},
          sampling: {},
        },
      },
    )

    await client.connect(transport)
    if (this._enableLogging) {
      console.warn(
        `🔗 Connected to HTTP MCP server: ${this.config.name} at ${this.config.url}`,
      )
    }

    return client
  }

  /**
   * Refresh tool cache
   */
  private async refreshToolCache(): Promise<void> {
    if (!this.client) {
      throw new Error('Client not initialized. Call initialize() first.')
    }

    try {
      const response = await this.client.listTools()
      const tools = response.tools || []

      this.cachedTools = tools.map(tool => ({
        name: tool.name,
        title: tool.title,
        description: tool.description,
        inputSchema: tool.inputSchema as any,
      }))

      if (this._enableLogging) {
        console.warn(
          `🔄 Refreshed tool cache for ${this.config.name}: ${this.cachedTools.length} tools`,
        )
      }
    }
    catch (error) {
      const msg = error instanceof Error ? error.message : String(error)
      if (this._enableLogging) {
        console.error(
          `Failed to refresh tool cache for "${this.config.name}":`,
          msg,
        )
      }
      throw error
    }
  }

  async listTools(): Promise<McpTool[]> {
    if (!this._connected) {
      if (this._enableLogging) {
        console.warn(
          `McpHttpClient "${this.config.name}" not initialized, returning cached tools`,
        )
      }
      return this.cachedTools
    }

    try {
      await this.refreshToolCache()
    }
    catch (error) {
      if (this._enableLogging) {
        console.warn(`Failed to refresh tools, returning cached: ${error}`)
      }
    }

    return this.cachedTools
  }

  async callTool(
    name: string,
    args: Record<string, unknown>,
  ): Promise<CallToolResultPayload> {
    if (!this.client) {
      throw new Error(
        `HTTP MCP client for "${this.config.name}" not initialized. Call initialize() first.`,
      )
    }

    if (this._enableLogging) {
      console.warn(`🔧 Invoking HTTP MCP tool: ${this.config.name}/${name}`, {
        args,
      })
    }

    try {
      const result = await this.client.callTool({
        name,
        arguments: args,
      })

      return result as CallToolResult
    }
    catch (error) {
      const msg = error instanceof Error ? error.message : String(error)
      this.lastError = msg
      if (this._enableLogging) {
        console.error(
          `Failed to invoke HTTP MCP tool "${this.config.name}/${name}": ${msg}`,
        )
      }
      throw error
    }
  }

  async disconnect(): Promise<void> {
    if (this.client) {
      try {
        await this.client.close()
        if (this._enableLogging) {
          console.warn(`🔌 Closed HTTP MCP client: ${this.config.name}`)
        }
      }
      catch (error) {
        if (this._enableLogging) {
          console.error(
            `Error closing HTTP MCP client "${this.config.name}":`,
            error instanceof Error ? error.message : error,
          )
        }
      }
      this.client = undefined
    }
    this._connected = false
    this.cachedTools = []
  }

  getStatus() {
    return {
      connected: this._connected,
      toolCount: this.cachedTools.length,
      error: this.lastError,
    }
  }
}
