package exec

import (
	"context"
	"encoding/json"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/desktop/internal/wire/apiclient"
)

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

	var seen []string
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		seen = append(seen, r.Method+" "+r.URL.RequestURI())
		require.Equal(t, "Bearer token", r.Header.Get("Authorization"), "Bearer auth is forwarded")
		if strings.Contains(r.URL.Path, "/managed-agents/") {
			require.NotEmpty(t, r.Header.Get("Idempotency-Key"))
			require.Equal(t, r.Header.Get("Idempotency-Key"), r.Header.Get("X-Request-ID"))
		}
		switch r.URL.Path {
		case "/api/v1/workspaces/ws/agents/agent/runtime/managed-agents/start":
			var input agentLifecycleRequest
			require.NoError(t, json.NewDecoder(r.Body).Decode(&input))
			require.Equal(t, agentLifecycleRequest{RequestID: "req-1", AgentID: "agt_target"}, input)
			_, _ = io.WriteString(w, `{"status":"started"}`)
		case "/api/v1/workspaces/ws/agents/agent/runtime/managed-agents/stop":
			var input agentLifecycleRequest
			require.NoError(t, json.NewDecoder(r.Body).Decode(&input))
			require.Equal(t, agentLifecycleRequest{RequestID: "req-1", AgentID: "agt_target"}, input)
			_, _ = io.WriteString(w, `{"status":"stopping"}`)
		case "/api/v1/workspaces/ws/agents/agent/runtime/managed-agents/restart":
			var input agentLifecycleRequest
			require.NoError(t, json.NewDecoder(r.Body).Decode(&input))
			require.Equal(t, agentLifecycleRequest{RequestID: "req-1", AgentID: "agt_target"}, input)
			_, _ = io.WriteString(w, `{"status":"restarted"}`)
		case "/api/v1/workspaces/ws/agents/agent/runtime/managed-agents/create":
			var input agentCreateRequest
			require.NoError(t, json.NewDecoder(r.Body).Decode(&input))
			require.Equal(t, agentCreateRequest{
				RequestID: "req-create", Name: "researcher", MachineID: "mac-1", Runtime: "codex",
			}, input)
			_, _ = io.WriteString(w, `{"status":"pending","approvalId":"appr-create"}`)
		case "/api/v1/workspaces/ws/agents/agent/runtime/managed-agents/update":
			var input agentUpdateRequest
			require.NoError(t, json.NewDecoder(r.Body).Decode(&input))
			require.Equal(t, "req-update", input.RequestID)
			require.Equal(t, "agt_target", input.AgentID)
			require.True(t, input.Model.Set)
			require.Nil(t, input.Model.Value)
			_, _ = io.WriteString(w, `{"status":"pending","approvalId":"appr-update"}`)
		case "/api/v1/workspaces/ws/agents/agent/runtime/managed-agents/delete":
			var input agentDeleteRequest
			require.NoError(t, json.NewDecoder(r.Body).Decode(&input))
			require.Equal(t, []string{"agt_2", "agt_1"}, input.AgentIDs, "delete order is preserved")
			_, _ = io.WriteString(w, `{"status":"pending","approvalId":"appr-delete"}`)
		case "/api/v1/workspaces/ws/agents/agent/runtime/managed-agents/duplicate":
			var input agentDuplicateRequest
			require.NoError(t, json.NewDecoder(r.Body).Decode(&input))
			require.Equal(t, "req-duplicate", input.RequestID)
			require.Equal(t, "agt_source", input.SourceAgentID)
			require.Nil(t, input.Count, "omitted count stays omitted for the server default")
			_, _ = io.WriteString(w, `{"status":"pending","approvalId":"appr-duplicate"}`)
		default:
			http.NotFound(w, r)
		}
	}))
	defer server.Close()

	remote := agentRemote{client: &apiclient.Client{
		ServerURL: server.URL, AgentID: "agent", AuthToken: "token", RouteWorkspaceID: "ws", HTTPClient: server.Client(),
	}}
	body := agentLifecycleRequest{RequestID: "req-1", AgentID: "agt_target"}

	started, err := remote.lifecycle(context.Background(), "start", body)
	require.NoError(t, err)
	require.Equal(t, "started", started.Status)
	stopped, err := remote.lifecycle(context.Background(), "stop", body)
	require.NoError(t, err)
	require.Equal(t, "stopping", stopped.Status)
	restarted, err := remote.lifecycle(context.Background(), "restart", body)
	require.NoError(t, err)
	require.Equal(t, "restarted", restarted.Status)
	created, err := remote.create(context.Background(), agentCreateRequest{
		RequestID: "req-create", Name: "researcher", MachineID: "mac-1", Runtime: "codex",
	})
	require.NoError(t, err)
	require.Equal(t, agentStructuralResponse{Status: "pending", ApprovalID: "appr-create"}, created)
	updated, err := remote.update(context.Background(), agentUpdateRequest{
		RequestID: "req-update", AgentID: "agt_target", Model: agentOptionalString{Set: true},
	})
	require.NoError(t, err)
	require.Equal(t, "appr-update", updated.ApprovalID)
	deleted, err := remote.delete(context.Background(), agentDeleteRequest{
		RequestID: "req-delete", AgentIDs: []string{"agt_2", "agt_1"},
	})
	require.NoError(t, err)
	require.Equal(t, "appr-delete", deleted.ApprovalID)
	duplicated, err := remote.duplicate(context.Background(), agentDuplicateRequest{
		RequestID: "req-duplicate", SourceAgentID: "agt_source",
	})
	require.NoError(t, err)
	require.Equal(t, "appr-duplicate", duplicated.ApprovalID)

	require.Equal(t, []string{
		"POST /api/v1/workspaces/ws/agents/agent/runtime/managed-agents/start",
		"POST /api/v1/workspaces/ws/agents/agent/runtime/managed-agents/stop",
		"POST /api/v1/workspaces/ws/agents/agent/runtime/managed-agents/restart",
		"POST /api/v1/workspaces/ws/agents/agent/runtime/managed-agents/create",
		"POST /api/v1/workspaces/ws/agents/agent/runtime/managed-agents/update",
		"POST /api/v1/workspaces/ws/agents/agent/runtime/managed-agents/delete",
		"POST /api/v1/workspaces/ws/agents/agent/runtime/managed-agents/duplicate",
	}, seen)
}

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

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusConflict)
		_, _ = io.WriteString(w, `{"error":{"code":"runtime_outcome_unknown","message":"runtime outcome could not be determined"}}`)
	}))
	defer server.Close()

	handler := agentHandler{remote: agentRemote{client: &apiclient.Client{
		ServerURL: server.URL, AgentID: "agent", AuthToken: "token", RouteWorkspaceID: "ws", HTTPClient: server.Client(),
	}}}

	_, err := handler.start(context.Background(), actionScopedJSON[agentLifecycleRequest]{
		Body: agentLifecycleRequest{RequestID: "req-1", AgentID: "agt_target"},
	})
	require.Error(t, err)
	require.Equal(t, CodeConflict, classify(err).Code, "a 409 runtime_outcome_unknown maps to Exec CodeConflict")
}
