package daemon

import (
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/store/agent"
)

func TestProjectMachineAgentsForDaemonProjectsAgentTeamProxyLLM(t *testing.T) {
	agents := []agent.MachineAgent{{
		ID:              "agt_1",
		Name:            "worker",
		DisplayName:     "Worker",
		Description:     "desc",
		Runtime:         "codex",
		Model:           "gpt-5",
		ReasoningEffort: "medium",
		LLMMode:         agent.AgentLLMModeAgentTeamProxy,
		APIKey:          "agentteam-token",
		Status:          "inactive",
		Activity:        "offline",
	}}

	projected, err := projectMachineAgentsForDaemon(agents, "https://agentteam.example.com/", "wsp_1", false, false, "", false)
	require.NoError(t, err)
	require.Len(t, projected, 1)
	require.Equal(t, "agt_1", projected[0].ID)
	require.Equal(t, agent.DaemonLLMModeInline, projected[0].LLMMode)
	require.Equal(t, "https://agentteam.example.com/api/v1/workspaces/wsp_1/llm/v1", projected[0].BaseURL)
	require.Equal(t, "agentteam-token", projected[0].APIKey)
	require.Equal(t, map[string]string{"X-AgentTeam-Route-Workspace-Id": "wsp_1"}, projected[0].Headers)
	require.Equal(t, "responses", projected[0].WireAPI)
	require.Equal(t, "inactive", projected[0].Status)
}

func TestProjectMachineAgentsForDaemonIgnoresLegacyHostedBridgeFlag(t *testing.T) {
	agents := []agent.MachineAgent{{
		ID:              "agt_1",
		Name:            "worker",
		DisplayName:     "Worker",
		Description:     "desc",
		Runtime:         "codex",
		Model:           "gpt-5",
		ReasoningEffort: "medium",
		LLMMode:         agent.AgentLLMModeAgentTeamProxy,
		APIKey:          "agentteam-token",
		Status:          "inactive",
		Activity:        "offline",
	}}

	projected, err := projectMachineAgentsForDaemon(agents, "https://agentteam.example.com/", "wsp_1", true, true, "http://127.0.0.1:8080/maas/v1", false)
	require.NoError(t, err)
	require.Len(t, projected, 1)
	require.Equal(t, agent.DaemonLLMModeInline, projected[0].LLMMode)
	require.Equal(t, "https://agentteam.example.com/api/v1/workspaces/wsp_1/llm/v1", projected[0].BaseURL)
	require.Equal(t, "agentteam-token", projected[0].APIKey)
	require.Equal(t, map[string]string{"X-AgentTeam-Route-Workspace-Id": "wsp_1"}, projected[0].Headers)
	require.Equal(t, "responses", projected[0].WireAPI)
}

func TestProjectMachineAgentsForDaemonRejectsAgentTeamProxyWithoutRouteWorkspace(t *testing.T) {
	agents := []agent.MachineAgent{{
		ID:      "agt_1",
		Runtime: "codex",
		LLMMode: agent.AgentLLMModeAgentTeamProxy,
		APIKey:  "agentteam-token",
	}}

	_, err := projectMachineAgentsForDaemon(agents, "https://agentteam.example.com/", "", false, false, "", false)
	require.EqualError(t, err, "project agent agt_1 llm for daemon: agentteam proxy route workspace ID is required")
}

func TestProjectMachineAgentsForDaemonProjectsDirectMaaSAgentTeamHostedLLM(t *testing.T) {
	agents := []agent.MachineAgent{{
		ID:              "agt_1",
		Name:            "worker",
		DisplayName:     "Worker",
		Description:     "desc",
		Runtime:         "claude",
		Model:           "agentteam/deepseek-v4-pro",
		ReasoningEffort: "medium",
		LLMMode:         agent.AgentLLMModeAgentTeamHosted,
		Status:          "inactive",
		Activity:        "offline",
	}}

	projected, err := projectMachineAgentsForDaemon(agents, "https://agentteam.example.com/", "", true, true, "http://127.0.0.1:8080/maas/v1/", false)
	require.NoError(t, err)
	require.Len(t, projected, 1)
	require.Equal(t, agent.DaemonLLMModeInline, projected[0].LLMMode)
	require.Equal(t, "http://127.0.0.1:8080/maas/v1", projected[0].BaseURL)
	require.Empty(t, projected[0].APIKey)
	require.Equal(t, "anthropic", projected[0].WireAPI)
	require.Equal(t, agent.DaemonLLMCredentialKindMaaSCapability, projected[0].CredentialKind)
}

func TestProjectMachineAgentsForDaemonOmitsHostedLLMWithoutMaaSDirectCapability(t *testing.T) {
	agents := []agent.MachineAgent{{
		ID:              "agt_1",
		Name:            "worker",
		DisplayName:     "Worker",
		Description:     "desc",
		Runtime:         "claude",
		Model:           "agentteam/deepseek-v4-pro",
		ReasoningEffort: "medium",
		LLMMode:         agent.AgentLLMModeAgentTeamHosted,
		Status:          "inactive",
		Activity:        "offline",
	}}

	projected, err := projectMachineAgentsForDaemon(agents, "https://agentteam.example.com/", "", true, false, "http://127.0.0.1:8080/maas/v1/", false)
	require.NoError(t, err)
	require.Len(t, projected, 1)
	require.Empty(t, projected[0].LLMMode)
	require.Empty(t, projected[0].BaseURL)
	require.Empty(t, projected[0].CredentialKind)
}

func TestProjectMachineAgentsForDaemonKeepsLocalInlineOverrides(t *testing.T) {
	agents := []agent.MachineAgent{{
		ID:       "agt_1",
		Name:     "worker",
		Runtime:  "claude",
		LLMMode:  agent.AgentLLMModeLocal,
		BaseURL:  "https://local.example.com/v1",
		APIKey:   "local-token",
		WireAPI:  "anthropic",
		Headers:  map[string]string{"x-test": "1"},
		Status:   "inactive",
		Activity: "offline",
	}}

	projected, err := projectMachineAgentsForDaemon(agents, "https://agentteam.example.com/", "", true, true, "http://127.0.0.1:8080/maas/v1", false)
	require.NoError(t, err)
	require.Len(t, projected, 1)
	require.Equal(t, agent.DaemonLLMModeInline, projected[0].LLMMode)
	require.Equal(t, "https://local.example.com/v1", projected[0].BaseURL)
	require.Equal(t, "local-token", projected[0].APIKey)
	require.Equal(t, "anthropic", projected[0].WireAPI)
	require.Equal(t, map[string]string{"x-test": "1"}, projected[0].Headers)
}

func TestProjectMachineAgentsForDaemonGatesVisibleReplyContractByCapability(t *testing.T) {
	runtimePolicy := agent.NewRuntimePolicySnapshot("codex", agent.AgentPermissionModeFullAccess, 5, true)
	agents := []agent.MachineAgent{{
		ID:                          "agt_1",
		Name:                        "worker",
		Runtime:                     "codex",
		RuntimePolicy:               runtimePolicy,
		VisibleReplyContractEnabled: true,
		Status:                      "inactive",
		Activity:                    "offline",
	}}

	legacy, err := projectMachineAgentsForDaemon(agents, "https://agentteam.example.com/", "", false, false, "", false)
	require.NoError(t, err)
	require.Len(t, legacy, 1)
	require.Nil(t, legacy[0].VisibleReplyContract)
	require.NotNil(t, legacy[0].RuntimePolicy)
	require.False(t, legacy[0].RuntimePolicy.VisibleReplyContractEnabled)
	require.Equal(t, runtimePolicy.Fingerprint, legacy[0].RuntimePolicy.Fingerprint)

	capable, err := projectMachineAgentsForDaemon(agents, "https://agentteam.example.com/", "", false, false, "", true)
	require.NoError(t, err)
	require.Len(t, capable, 1)
	require.NotNil(t, capable[0].VisibleReplyContract)
	require.True(t, capable[0].VisibleReplyContract.Enabled)
	require.NotNil(t, capable[0].RuntimePolicy)
	require.True(t, capable[0].RuntimePolicy.VisibleReplyContractEnabled)
	require.Equal(t, runtimePolicy.Fingerprint, capable[0].RuntimePolicy.Fingerprint)
}

func TestProjectMachineAgentsForDaemonEmitsAlwaysOnDespiteFalseStoreValue(t *testing.T) {
	// Historical DB rows may still carry visible_reply_contract_enabled=false.
	// The projection is always-on for clients that declare the capability, so
	// the effective value must be true regardless of the stale stored value.
	runtimePolicy := agent.NewRuntimePolicySnapshot("codex", agent.AgentPermissionModeFullAccess, 5, false)
	agents := []agent.MachineAgent{{
		ID:                          "agt_1",
		Name:                        "worker",
		Runtime:                     "codex",
		RuntimePolicy:               runtimePolicy,
		VisibleReplyContractEnabled: false,
		Status:                      "inactive",
		Activity:                    "offline",
	}}

	projected, err := projectMachineAgentsForDaemon(agents, "https://agentteam.example.com/", "", false, false, "", true)
	require.NoError(t, err)
	require.Len(t, projected, 1)
	require.NotNil(t, projected[0].VisibleReplyContract)
	require.True(t, projected[0].VisibleReplyContract.Enabled)
	require.NotNil(t, projected[0].RuntimePolicy)
	require.True(t, projected[0].RuntimePolicy.VisibleReplyContractEnabled)
}

func TestProjectMachineAgentsForDaemonIncludesRuntimeDebugOnlyWhenEnabled(t *testing.T) {
	t.Parallel()

	projected, err := projectMachineAgentsForDaemon([]agent.MachineAgent{
		{ID: "agt_off", Name: "off", Runtime: "opencode", Status: "inactive", Activity: "offline"},
		{ID: "agt_on", Name: "on", Runtime: "opencode", RuntimeDebugEnabled: true, Status: "inactive", Activity: "offline"},
	}, "https://agentteam.example.com/", "", false, false, "", false)
	require.NoError(t, err)
	require.Len(t, projected, 2)
	require.Nil(t, projected[0].RuntimeDebug)
	require.NotNil(t, projected[1].RuntimeDebug)
	require.True(t, projected[1].RuntimeDebug.Enabled)
}

func TestFilterProjectedAgentsByPermissionModeCapability(t *testing.T) {
	projected := []machineAgentForDaemon{
		{ID: "agt_full", PermissionMode: agent.AgentPermissionModeFullAccess},
		{ID: "agt_workspace", PermissionMode: agent.AgentPermissionModeWorkspaceWrite},
	}
	legacyClient := &clientState{capabilities: map[string]struct{}{}}

	filtered := legacyClient.filterProjectedAgentsByPermissionModeCapability(projected)
	require.Equal(t, projected, filtered)

	capableClient := &clientState{capabilities: map[string]struct{}{agentPermissionModeCapability: {}}}
	require.Equal(t, projected, capableClient.filterProjectedAgentsByPermissionModeCapability(projected))
}

func TestClientAgentRuntimeStatusTracksConnectionScopedTelemetry(t *testing.T) {
	client := &clientState{
		runningAgents:   map[string]bool{},
		startingAgents:  map[string]bool{},
		stoppedAgents:   map[string]bool{},
		agentStatuses:   map[string]string{},
		agentActivities: map[string]agentActivityState{},
	}

	client.replaceRunningAgents([]string{"agt_live"})
	status, activity, detail, ok := client.agentRuntimeStatus("agt_live")
	require.True(t, ok)
	require.Equal(t, "active", status)
	require.Empty(t, activity)
	require.Empty(t, detail)

	client.setAgentActivity("agt_live", "thinking", "Investigating")
	status, activity, detail, ok = client.agentRuntimeStatus("agt_live")
	require.True(t, ok)
	require.Equal(t, "active", status)
	require.Equal(t, "thinking", activity)
	require.Equal(t, "Investigating", detail)

	client.replaceRunningAgents(nil)
	_, _, _, ok = client.agentRuntimeStatus("agt_live")
	require.False(t, ok)
}
