package api

import (
	"context"
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"

	internalRuntime "github.com/smart-mcp-proxy/mcpproxy-go/internal/runtime"
	"github.com/smart-mcp-proxy/mcpproxy-go/internal/tray"
)

// ClientInterface defines the methods required by ServerAdapter from the API client.
// This interface allows for testing with mock implementations.
type ClientInterface interface {
	GetServers() ([]Server, error)
	GetInfo() (map[string]interface{}, error)
	EnableServer(serverName string, enabled bool) error
	QuarantineServer(serverName string) error
	UnquarantineServer(serverName string) error
	TriggerOAuthLogin(serverName string) error
	StatusChannel() <-chan StatusUpdate
	GetProfiles() ([]tray.ProfileInfo, error)
	GetActiveProfile() (string, error)
	SetActiveProfile(name string) error
}

// isServerHealthy returns true if the server is considered healthy.
// It uses health.level as the source of truth, with a fallback to the legacy
// connected field for backward compatibility when health is nil.
func isServerHealthy(health *HealthStatus, legacyConnected bool) bool {
	if health != nil {
		return health.Level == "healthy"
	}
	// Fallback to legacy connected field if health is not available
	return legacyConnected
}

// ServerAdapter adapts the API client to the ServerInterface expected by the tray
type ServerAdapter struct {
	client ClientInterface
}

// NewServerAdapter creates a new server adapter
func NewServerAdapter(client ClientInterface) *ServerAdapter {
	return &ServerAdapter{
		client: client,
	}
}

// IsRunning checks if the server is running via API
func (a *ServerAdapter) IsRunning() bool {
	if _, err := a.client.GetServers(); err != nil {
		return false
	}

	// If we can fetch servers, the API is responsive regardless of count
	return true
}

// GetListenAddress returns the listen address from the core's /api/v1/info endpoint
func (a *ServerAdapter) GetListenAddress() string {
	// Get the actual listen address from the core
	info, err := a.client.GetInfo()
	if err == nil && info != nil {
		if data, ok := info["data"].(map[string]interface{}); ok {
			if addr, ok := data["listen_addr"].(string); ok {
				return addr
			}
		}
	}
	// Return empty string if we couldn't get the address (don't return hardcoded value)
	return ""
}

// GetUpstreamStats returns upstream server statistics
func (a *ServerAdapter) GetUpstreamStats() map[string]interface{} {
	servers, err := a.client.GetServers()
	if err != nil {
		return map[string]interface{}{
			"connected_servers": 0,
			"total_servers":     0,
			"total_tools":       0,
		}
	}

	connectedCount := 0
	totalTools := 0
	for _, server := range servers {
		if isServerHealthy(server.Health, server.Connected) {
			connectedCount++
		}
		totalTools += server.ToolCount
	}

	return map[string]interface{}{
		"connected_servers": connectedCount,
		"total_servers":     len(servers),
		"total_tools":       totalTools,
	}
}

// StartServer is not supported via API (server is already running)
func (a *ServerAdapter) StartServer(_ context.Context) error {
	return fmt.Errorf("StartServer not supported via API - server is already running")
}

// StopServer is not supported via API (would break tray communication)
func (a *ServerAdapter) StopServer() error {
	return fmt.Errorf("StopServer not supported via API - would break tray communication")
}

// GetStatus returns the current server status
func (a *ServerAdapter) GetStatus() interface{} {
	// Get the actual listen address from the core's /api/v1/info endpoint
	info, err := a.client.GetInfo()
	listenAddr := ""
	if err == nil && info != nil {
		if data, ok := info["data"].(map[string]interface{}); ok {
			if addr, ok := data["listen_addr"].(string); ok {
				listenAddr = addr
			}
		}
	}

	// Fallback to empty if we couldn't get it
	if listenAddr == "" {
		listenAddr = "" // Empty means tray will show "Status: Running" without address
	}

	servers, serverErr := a.client.GetServers()
	if serverErr != nil {
		return map[string]interface{}{
			"phase":       "Error",
			"message":     fmt.Sprintf("API error: %v", serverErr),
			"running":     false,
			"listen_addr": listenAddr,
		}
	}

	connectedCount := 0
	for _, server := range servers {
		if isServerHealthy(server.Health, server.Connected) {
			connectedCount++
		}
	}

	return map[string]interface{}{
		"phase":             "Running",
		"message":           fmt.Sprintf("API connected - %d servers", len(servers)),
		"running":           true,
		"listen_addr":       listenAddr,
		"connected_servers": connectedCount,
		"total_servers":     len(servers),
	}
}

// StatusChannel returns the channel for status updates from SSE
func (a *ServerAdapter) StatusChannel() <-chan interface{} {
	// Convert the typed channel to interface{} channel
	ch := make(chan interface{}, 10)

	go func() {
		defer close(ch)
		for update := range a.client.StatusChannel() {
			// Convert StatusUpdate to the format expected by tray
			status := map[string]interface{}{
				"phase":          "Running",
				"message":        "Connected via API",
				"running":        update.Running,
				"listen_addr":    update.ListenAddr,
				"upstream_stats": update.UpstreamStats,
				"timestamp":      update.Timestamp,
			}

			select {
			case ch <- status:
			default:
				// Channel full, skip this update
			}
		}
	}()

	return ch
}

// EventsChannel returns nil as the remote API does not yet proxy runtime events.
func (a *ServerAdapter) EventsChannel() <-chan internalRuntime.Event {
	return nil
}

// GetProfiles returns the configured profiles for the tray switcher (Profiles v2 T5).
func (a *ServerAdapter) GetProfiles() ([]tray.ProfileInfo, error) {
	return a.client.GetProfiles()
}

// GetActiveProfile returns the server-level default active profile (empty = all servers).
func (a *ServerAdapter) GetActiveProfile() (string, error) {
	return a.client.GetActiveProfile()
}

// SetActiveProfile sets the server-level default active profile (empty clears it).
func (a *ServerAdapter) SetActiveProfile(name string) error {
	return a.client.SetActiveProfile(name)
}

// GetQuarantinedServers returns quarantined servers
func (a *ServerAdapter) GetQuarantinedServers() ([]map[string]interface{}, error) {
	servers, err := a.client.GetServers()
	if err != nil {
		return nil, err
	}

	var quarantined []map[string]interface{}
	for _, server := range servers {
		if server.Quarantined {
			quarantined = append(quarantined, map[string]interface{}{
				"name":        server.Name,
				"url":         server.URL,
				"command":     server.Command,
				"protocol":    server.Protocol,
				"enabled":     server.Enabled,
				"quarantined": server.Quarantined,
			})
		}
	}

	return quarantined, nil
}

// UnquarantineServer removes a server from quarantine
func (a *ServerAdapter) UnquarantineServer(serverName string) error {
	return a.client.UnquarantineServer(serverName)
}

// EnableServer enables or disables a server
func (a *ServerAdapter) EnableServer(serverName string, enabled bool) error {
	return a.client.EnableServer(serverName, enabled)
}

// QuarantineServer sets quarantine status for a server
func (a *ServerAdapter) QuarantineServer(serverName string, quarantined bool) error {
	if quarantined {
		return a.client.QuarantineServer(serverName)
	}
	return a.client.UnquarantineServer(serverName)
}

// GetAllServers returns all servers
func (a *ServerAdapter) GetAllServers() ([]map[string]interface{}, error) {
	servers, err := a.client.GetServers()
	if err != nil {
		return nil, err
	}

	var result []map[string]interface{}
	for _, server := range servers {
		serverMap := map[string]interface{}{
			"name":            server.Name,
			"url":             server.URL,
			"command":         server.Command,
			"protocol":        server.Protocol,
			"enabled":         server.Enabled,
			"quarantined":     server.Quarantined,
			"connected":       server.Connected,
			"connecting":      server.Connecting,
			"tool_count":      server.ToolCount,
			"last_error":      server.LastError,
			"status":          server.Status,
			"should_retry":    server.ShouldRetry,
			"retry_count":     server.RetryCount,
			"last_retry_time": server.LastRetry,
		}

		// Spec 013: Include health status as source of truth for connected count
		if server.Health != nil {
			serverMap["health"] = map[string]interface{}{
				"level":       server.Health.Level,
				"admin_state": server.Health.AdminState,
				"summary":     server.Health.Summary,
				"detail":      server.Health.Detail,
				"action":      server.Health.Action,
			}
		}

		result = append(result, serverMap)
	}

	return result, nil
}

// SetListenAddress is not supported via API control surfaces.
func (a *ServerAdapter) SetListenAddress(_ string, _ bool) error {
	return fmt.Errorf("SetListenAddress not supported via API")
}

// SuggestAlternateListen cannot operate through the remote API adapter.
func (a *ServerAdapter) SuggestAlternateListen(baseAddr string) (string, error) {
	return baseAddr, fmt.Errorf("SuggestAlternateListen not supported via API")
}

// ReloadConfiguration reloads the configuration
func (a *ServerAdapter) ReloadConfiguration() error {
	// This functionality is not available in the current API
	// Would need to be added to the API first
	return fmt.Errorf("ReloadConfiguration not yet supported via API")
}

// GetConfigPath returns the configuration file path core is running with.
// The tray passes MCPPROXY_TRAY_CONFIG_PATH to core as --config (see
// buildCoreArgs in main.go), so tray-side config consumers — e.g. the Spec 079
// update_check gate — must resolve to that same override, not a hardcoded
// default. Falls back to the default ~/.mcpproxy path when unset.
func (a *ServerAdapter) GetConfigPath() string {
	if cfg := strings.TrimSpace(os.Getenv("MCPPROXY_TRAY_CONFIG_PATH")); cfg != "" {
		return cfg
	}
	homeDir, err := os.UserHomeDir()
	if err != nil {
		return "~/.mcpproxy/mcp_config.json" // fallback
	}
	return filepath.Join(homeDir, ".mcpproxy", "mcp_config.json")
}

// GetLogDir returns the log directory path
func (a *ServerAdapter) GetLogDir() string {
	homeDir, err := os.UserHomeDir()
	if err != nil {
		return "~/.mcpproxy/logs" // fallback
	}

	// Use platform-specific log directory (same logic as mcpproxy-tray/main.go)
	switch runtime.GOOS {
	case "darwin":
		return filepath.Join(homeDir, "Library", "Logs", "mcpproxy")
	case "windows":
		if localAppData := os.Getenv("LOCALAPPDATA"); localAppData != "" {
			return filepath.Join(localAppData, "mcpproxy", "logs")
		}
		if userProfile := os.Getenv("USERPROFILE"); userProfile != "" {
			return filepath.Join(userProfile, "AppData", "Local", "mcpproxy", "logs")
		}
		return filepath.Join(homeDir, ".mcpproxy", "logs")
	default: // linux and others
		return filepath.Join(homeDir, ".mcpproxy", "logs")
	}
}

// TriggerOAuthLogin triggers OAuth login for a server
func (a *ServerAdapter) TriggerOAuthLogin(serverName string) error {
	return a.client.TriggerOAuthLogin(serverName)
}
