package wire

import (
	"strings"
	"sync"
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/desktop/internal/profile"
)

func TestProtocolIdentitySnapshotIsConsistentDuringBindingUpdates(t *testing.T) {
	c := &Protocol{
		id:          "agentteam",
		enabled:     true,
		serverURL:   "https://agentteam.example.test",
		apiKey:      "key",
		workspaceID: "wsp-a",
		machineID:   "mac-a",
	}
	previous := profile.AgentTeamServer{ID: "agentteam", WorkspaceID: "wsp-a", MachineID: "mac-a"}
	current := profile.AgentTeamServer{ID: "agentteam", WorkspaceID: "wsp-b", MachineID: "mac-b"}

	var wg sync.WaitGroup
	wg.Add(2)
	go func() {
		defer wg.Done()
		for range 1000 {
			snapshot := c.ServerIdentitySnapshot()
			require.True(t,
				snapshot.WorkspaceID == "wsp-a" && snapshot.MachineID == "mac-a" ||
					snapshot.WorkspaceID == "wsp-b" && snapshot.MachineID == "mac-b",
			)
		}
	}()
	go func() {
		defer wg.Done()
		for range 500 {
			require.NoError(t, c.ApplyServerBinding(previous, current))
			require.NoError(t, c.ApplyServerBinding(current, previous))
		}
	}()
	wg.Wait()
}

func TestApplyServerBindingRejectsNonCASInputsAtomically(t *testing.T) {
	protocol := &Protocol{
		id: "agentteam", enabled: true, serverURL: "https://agentteam.example.test", apiKey: "key",
		workspaceID: "wsp-old", machineID: "mac-old",
	}
	wantSnapshot := protocol.ServerIdentitySnapshot()
	wantClient := protocol.APIClient()

	validPrevious := profile.AgentTeamServer{ID: "agentteam", WorkspaceID: "wsp-old", MachineID: "mac-old"}
	validCurrent := profile.AgentTeamServer{ID: "agentteam", WorkspaceID: "wsp-new", MachineID: "mac-new"}
	for _, test := range []struct {
		name     string
		previous profile.AgentTeamServer
		current  profile.AgentTeamServer
	}{
		{name: "previous invalid", previous: profile.AgentTeamServer{ID: "agentteam", WorkspaceID: "."}, current: validCurrent},
		{name: "previous unicode", previous: profile.AgentTeamServer{ID: "agentteam", WorkspaceID: "工作区"}, current: validCurrent},
		{name: "previous control", previous: profile.AgentTeamServer{ID: "agentteam", WorkspaceID: "wsp\nold"}, current: validCurrent},
		{name: "previous mismatch", previous: profile.AgentTeamServer{ID: "agentteam", WorkspaceID: "wsp-other"}, current: validCurrent},
		{name: "current invalid", previous: validPrevious, current: profile.AgentTeamServer{ID: "agentteam", WorkspaceID: "%2F", MachineID: "mac-new"}},
		{name: "current too long", previous: validPrevious, current: profile.AgentTeamServer{ID: "agentteam", WorkspaceID: strings.Repeat("a", 129), MachineID: "mac-new"}},
	} {
		t.Run(test.name, func(t *testing.T) {
			require.Error(t, protocol.ApplyServerBinding(test.previous, test.current))
			require.Equal(t, wantSnapshot, protocol.ServerIdentitySnapshot())
			gotClient := protocol.APIClient()
			require.Equal(t, wantClient.ServerURL, gotClient.ServerURL)
			require.Equal(t, wantClient.RouteWorkspaceID, gotClient.RouteWorkspaceID)
			require.Equal(t, wantClient.MachineID, gotClient.MachineID)
		})
	}
}

func TestApplyServerBindingPreservesTrimCompatibility(t *testing.T) {
	protocol := &Protocol{id: "agentteam", workspaceID: "wsp-old", machineID: "mac-old"}
	require.NoError(t, protocol.ApplyServerBinding(
		profile.AgentTeamServer{ID: "agentteam", WorkspaceID: "  wsp-old  ", MachineID: "mac-old"},
		profile.AgentTeamServer{ID: "agentteam", WorkspaceID: "  wsp-new  ", MachineID: "mac-new"},
	))
	require.Equal(t, "wsp-new", protocol.ServerIdentitySnapshot().WorkspaceID)
	require.Equal(t, "wsp-new", protocol.APIClient().RouteWorkspaceID)
}
