package dispatcher

import (
	"context"
	"testing"

	"github.com/tidbcloud/AgentTeam/desktop/internal/agent/runtimeconfig"
	"github.com/tidbcloud/AgentTeam/desktop/internal/profile"
	"github.com/tidbcloud/AgentTeam/desktop/internal/types"
)

func Test_mcpAgentTeamEndpoint(t *testing.T) {
	got := mcpAgentTeamEndpoint("http://localhost:8080", "agentteam", "agent-1")
	want := "http://localhost:8080/api/internal/mcp/servers/agentteam/agents/agent-1"
	if got != want {
		t.Errorf("mcpAgentTeamEndpoint() = %q, want %q", got, want)
	}
}

func Test_mcpAgentTeamEndpoint_SpecialChars(t *testing.T) {
	got := mcpAgentTeamEndpoint("http://localhost:8080", "ch/space", "agent id")
	want := "http://localhost:8080/api/internal/mcp/servers/ch%2Fspace/agents/agent%20id"
	if got != want {
		t.Errorf("mcpAgentTeamEndpoint() = %q, want %q", got, want)
	}
}

func TestResolveMCPConfigs_UsesServerEndpointWithoutChannelManager(t *testing.T) {
	isolateProfileForDispatcherTest(t)
	d := mustNewDispatcher("http://localhost:8080")

	got := d.resolveMCPConfigs(" agentteam-main ", "agent id", "server-agent", "session-1", "token", runtimeconfig.TypeCodex)
	if len(got) != 1 {
		t.Fatalf("resolveMCPConfigs() returned %d configs, want 1", len(got))
	}
	if got[0].Name != "agentteam" {
		t.Fatalf("resolveMCPConfigs()[0].Name = %q, want agentteam", got[0].Name)
	}
	if got[0].URL != "http://localhost:8080/api/internal/mcp/servers/agentteam-main/agents/agent%20id" {
		t.Fatalf("resolveMCPConfigs()[0].URL = %q", got[0].URL)
	}
	if got[0].Headers["Authorization"] != "Bearer token" {
		t.Fatalf("resolveMCPConfigs()[0].Headers[Authorization] = %q", got[0].Headers["Authorization"])
	}
}

func TestResolveMCPConfigs_UsesProvidedServerIDWhenManagerHasNoMatch(t *testing.T) {
	isolateProfileForDispatcherTest(t)
	d := mustNewDispatcher("http://localhost:8080")

	got := d.resolveMCPConfigs("agentteam-main", "agent-1", "server-agent", "session-1", "token", runtimeconfig.TypeCodex)
	if len(got) != 1 {
		t.Fatalf("resolveMCPConfigs() returned %d configs, want 1", len(got))
	}
	if got[0].Name != "agentteam" {
		t.Fatalf("resolveMCPConfigs()[0].Name = %q, want agentteam", got[0].Name)
	}
	if got[0].URL != "http://localhost:8080/api/internal/mcp/servers/agentteam-main/agents/agent-1" {
		t.Fatalf("resolveMCPConfigs()[0].URL = %q", got[0].URL)
	}
}

func TestResolveMCPConfigs_UsesAgentTeamEndpointForClaudeRuntime(t *testing.T) {
	isolateProfileForDispatcherTest(t)
	d := mustNewDispatcher("http://localhost:8080")

	got := d.resolveMCPConfigs("agentteam-main", "agent-1", "server-agent", "session-1", "token", runtimeconfig.TypeClaudeCode)
	if len(got) != 1 {
		t.Fatalf("resolveMCPConfigs() returned %d configs, want 1", len(got))
	}
	if got[0].Name != "agentteam" {
		t.Fatalf("resolveMCPConfigs()[0].Name = %q, want agentteam", got[0].Name)
	}
	if got[0].URL != "http://localhost:8080/api/internal/mcp/servers/agentteam-main/agents/agent-1" {
		t.Fatalf("resolveMCPConfigs()[0].URL = %q", got[0].URL)
	}
}

func TestResolveMCPConfigs_AddsExternalProfileServers(t *testing.T) {
	isolateProfileForDispatcherTest(t)
	t.Setenv("ZAP_TOKEN", "Bearer env-token")
	t.Setenv("STDIO_SECRET", "secret-value")
	_, err := profile.Default().MCPServers().Add(profile.MCPServer{
		ID:          "zap_http",
		DisplayName: "Zap HTTP",
		Enabled:     true,
		Transport:   profile.MCPServerTransportHTTP,
		HTTP:        &profile.MCPHTTPConfig{URL: "https://mcp.example.com/rpc", Headers: []string{"Authorization: $ZAP_TOKEN"}},
	})
	if err != nil {
		t.Fatal(err)
	}
	_, err = profile.Default().MCPServers().Add(profile.MCPServer{
		ID:          "zap_stdio",
		DisplayName: "Zap Stdio",
		Enabled:     true,
		Transport:   profile.MCPServerTransportStdio,
		Stdio:       &profile.MCPStdioConfig{Command: "/bin/echo", Args: []string{"ok"}, Env: []string{"STDIO_SECRET=$STDIO_SECRET"}},
	})
	if err != nil {
		t.Fatal(err)
	}

	d := mustNewDispatcher("http://localhost:8080")
	got := d.resolveMCPConfigs("agentteam-main", "agent-1", "server-agent", "session-1", "token", runtimeconfig.TypeCodex)
	if len(got) != 3 {
		t.Fatalf("resolveMCPConfigs() returned %d configs, want 3", len(got))
	}
	if got[0].Name != "agentteam" {
		t.Fatalf("resolveMCPConfigs()[0].Name = %q, want agentteam", got[0].Name)
	}
	httpConfig := got[1]
	if httpConfig.Name != "zap_http" || httpConfig.EffectiveTransport() != types.MCPTransportHTTP {
		t.Fatalf("HTTP external config = %#v", httpConfig)
	}
	if httpConfig.Headers["Authorization"] != "Bearer env-token" {
		t.Fatalf("external HTTP Authorization = %q", httpConfig.Headers["Authorization"])
	}
	stdioConfig := got[2]
	if stdioConfig.Name != "zap_stdio" || stdioConfig.EffectiveTransport() != types.MCPTransportStdio {
		t.Fatalf("stdio external config = %#v", stdioConfig)
	}
	if stdioConfig.Command != "/bin/echo" || len(stdioConfig.Args) != 1 || stdioConfig.Args[0] != "ok" {
		t.Fatalf("stdio command/args = %#v", stdioConfig)
	}
	if stdioConfig.Env["STDIO_SECRET"] != "secret-value" {
		t.Fatalf("stdio env = %#v", stdioConfig.Env)
	}
}

func Test_mcpAuthHeaders(t *testing.T) {
	tests := []struct {
		name      string
		authToken string
		wantNil   bool
		wantKey   string
		wantValue string
	}{
		{"valid token", "my-token", false, "Authorization", "Bearer my-token"},
		{"empty token", "", true, "", ""},
		{"whitespace only", "  ", true, "", ""},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := mcpAuthHeaders(tt.authToken)
			if tt.wantNil {
				if got != nil {
					t.Error("expected nil for empty token")
				}
				return
			}
			if got[tt.wantKey] != tt.wantValue {
				t.Errorf("mcpAuthHeaders()[%q] = %q, want %q", tt.wantKey, got[tt.wantKey], tt.wantValue)
			}
		})
	}
}

func isolateProfileForDispatcherTest(t *testing.T) string {
	t.Helper()
	dir := t.TempDir()
	t.Setenv(profile.ConfigDirEnvKey, dir)
	t.Setenv("PATH", t.TempDir())
	return dir
}

type mcpTestServerConnection struct {
	id string
}

func (c mcpTestServerConnection) ID() string                  { return c.id }
func (c mcpTestServerConnection) Enabled() bool               { return true }
func (c mcpTestServerConnection) Start(context.Context) error { return nil }
func (c mcpTestServerConnection) Stop(context.Context) error  { return nil }
func (c mcpTestServerConnection) Send(context.Context, types.Metadata, types.OutboundMessage) error {
	return nil
}
