package globalapi

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"sync"
	"testing"

	"github.com/ddliu/go-httpclient"
	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"github.com/tidbcloud/observability/pkg/common/message"
	"github.com/tidbcloud/observability/pkg/common/structs"
)

var (
	mockHost = "host"
)

func TestQueryClustersGlobally(t *testing.T) {
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Set(structs.GlobalServerRequestParametersKey, requestParameters)
	req, _ := http.NewRequest("GET", "/api/v1/global/clusters/", nil)
	c.Request = req

	t.Setenv("AWS_ROLE_ARN", "arn:aws:iam::1234567890:role/test")
	t.Setenv("AWS_WEB_IDENTITY_TOKEN_FILE", "./main_test.go")
	t.Setenv("AWS_REGION", "us-west-2")
	QueryClustersGlobally(c)
	assert.Equal(t, http.StatusOK, w.Code)
}

func TestBuildRegionalClustersURLIncludesShowDeleted(t *testing.T) {
	rawURL := buildRegionalClustersURL("https://regional.example.com", "aws", "us-east-1", message.QueryClustersReq{
		TenantID:      "tenant-1",
		ProjectID:     "project-1",
		ApplicationID: "app-1",
		BizType:       "tidbcloud",
		ShowDeleted:   true,
	})

	parsed, err := url.Parse(rawURL)
	assert.NoError(t, err)
	assert.Equal(t, "/api/v1/clusters/", parsed.Path)
	assert.Equal(t, "aws", parsed.Query().Get("vendor"))
	assert.Equal(t, "us-east-1", parsed.Query().Get("region"))
	assert.Equal(t, "tenant-1", parsed.Query().Get("tenantID"))
	assert.Equal(t, "project-1", parsed.Query().Get("projectID"))
	assert.Equal(t, "app-1", parsed.Query().Get("applicationID"))
	assert.Equal(t, "tidbcloud", parsed.Query().Get("bizType"))
	assert.Equal(t, "true", parsed.Query().Get("showDeleted"))
}

func TestQueryClustersGloballyShowDeletedValidation(t *testing.T) {
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	req, _ := http.NewRequest("GET", "/api/v1/global/clusters/?showDeleted=true&vendor=aws&region=us-east-1", nil)
	c.Request = req

	QueryClustersGlobally(c)

	assert.Equal(t, http.StatusBadRequest, w.Code)
}

func TestQueryClustersInRegion(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(`
{
"code": 0,
"message": "",
"data": {
"clusters":[
	{
		"vendor":"aws",
		"region":"us-west-2",
		"tenantID":"1369847559691367432",
		"projectID":"1369847559691368131",
		"id":"085efd5f-15cb-4267-8a93-53f076fcb214"
	}
]
}
}
`))
	}))

	query := func(url string) RegionResult {
		wg := &sync.WaitGroup{}
		responseChannel := make(chan RegionResult, 1)
		wg.Add(1)
		go queryClustersInRegion(url, map[string]string{}, responseChannel, wg, true)
		wg.Wait()
		return <-responseChannel
	}

	t.Run("error", func(t *testing.T) {
		result := query(mockHost)
		assert.Error(t, result.err)
	})

	t.Run("http code error", func(t *testing.T) {
		s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.WriteHeader(http.StatusBadRequest)
		}))
		result := query(s.URL)
		assert.Error(t, result.err)
	})

	t.Run("normal", func(t *testing.T) {
		result := query(srv.URL)
		assert.True(t, len(result.resp.Clusters) == 1)
	})
}

func Test_appendRegionResult(t *testing.T) {
	t.Run("non-primary region with error sets partial flag", func(t *testing.T) {
		resp := &message.QueryClustersResp{
			Clusters: []structs.ClusterBaseInfo{},
			Parital:  false,
		}

		regionResult := RegionResult{
			resp: message.QueryClustersResp{
				Clusters: []structs.ClusterBaseInfo{
					{ID: "cluster-1"},
				},
			},
			err:     fmt.Errorf("non-primary region error"),
			primary: false, // This is a non-primary region
		}

		err := appendRegionResult(resp, regionResult)

		// Should not return error for non-primary region failures
		assert.NoError(t, err)
		// Should set partial flag to true
		assert.True(t, resp.Parital)
		// Should still append clusters from failed non-primary region (current behavior)
		assert.Len(t, resp.Clusters, 1)
		assert.Equal(t, "cluster-1", resp.Clusters[0].ID)
	})

	t.Run("primary region with error returns error", func(t *testing.T) {
		resp := &message.QueryClustersResp{
			Clusters: []structs.ClusterBaseInfo{},
			Parital:  false,
		}

		regionResult := RegionResult{
			resp: message.QueryClustersResp{
				Clusters: []structs.ClusterBaseInfo{
					{ID: "cluster-1"},
				},
			},
			err:     fmt.Errorf("primary region error"),
			primary: true, // This is a primary region
		}

		err := appendRegionResult(resp, regionResult)

		// Should return error for primary region failures
		assert.Error(t, err)
		assert.Equal(t, "primary region error", err.Error())
		// Partial flag should remain false since we return early on primary errors
		assert.False(t, resp.Parital)
		// Should not append clusters from failed primary region (returns early)
		assert.Len(t, resp.Clusters, 0)
	})

	t.Run("non-primary region without error appends clusters", func(t *testing.T) {
		resp := &message.QueryClustersResp{
			Clusters: []structs.ClusterBaseInfo{
				{ID: "existing-cluster"},
			},
			Parital: false,
		}

		regionResult := RegionResult{
			resp: message.QueryClustersResp{
				Clusters: []structs.ClusterBaseInfo{
					{ID: "cluster-1"},
					{ID: "cluster-2"},
				},
			},
			err:     nil,
			primary: false, // This is a non-primary region
		}

		err := appendRegionResult(resp, regionResult)

		// Should not return error
		assert.NoError(t, err)
		// Should not set partial flag since there's no error
		assert.False(t, resp.Parital)
		// Should append clusters from successful non-primary region
		assert.Len(t, resp.Clusters, 3)
		assert.Equal(t, "existing-cluster", resp.Clusters[0].ID)
		assert.Equal(t, "cluster-1", resp.Clusters[1].ID)
		assert.Equal(t, "cluster-2", resp.Clusters[2].ID)
	})

	t.Run("primary region without error appends clusters", func(t *testing.T) {
		resp := &message.QueryClustersResp{
			Clusters: []structs.ClusterBaseInfo{
				{ID: "existing-cluster"},
			},
			Parital: false,
		}

		regionResult := RegionResult{
			resp: message.QueryClustersResp{
				Clusters: []structs.ClusterBaseInfo{
					{ID: "primary-cluster-1"},
					{ID: "primary-cluster-2"},
				},
			},
			err:     nil,
			primary: true, // This is a primary region
		}

		err := appendRegionResult(resp, regionResult)

		// Should not return error
		assert.NoError(t, err)
		// Should not set partial flag since there's no error
		assert.False(t, resp.Parital)
		// Should append clusters from successful primary region
		assert.Len(t, resp.Clusters, 3)
		assert.Equal(t, "existing-cluster", resp.Clusters[0].ID)
		assert.Equal(t, "primary-cluster-1", resp.Clusters[1].ID)
		assert.Equal(t, "primary-cluster-2", resp.Clusters[2].ID)
	})

	t.Run("multiple non-primary region failures preserve partial flag", func(t *testing.T) {
		resp := &message.QueryClustersResp{
			Clusters: []structs.ClusterBaseInfo{},
			Parital:  false,
		}

		// First non-primary region failure
		regionResult1 := RegionResult{
			resp:    message.QueryClustersResp{Clusters: []structs.ClusterBaseInfo{}},
			err:     fmt.Errorf("first non-primary region error"),
			primary: false,
		}

		err1 := appendRegionResult(resp, regionResult1)
		assert.NoError(t, err1)
		assert.True(t, resp.Parital)

		// Second non-primary region failure - should preserve partial flag
		regionResult2 := RegionResult{
			resp:    message.QueryClustersResp{Clusters: []structs.ClusterBaseInfo{}},
			err:     fmt.Errorf("second non-primary region error"),
			primary: false,
		}

		err2 := appendRegionResult(resp, regionResult2)
		assert.NoError(t, err2)
		assert.True(t, resp.Parital) // Should still be true
	})

	t.Run("successful region after failed non-primary preserves partial flag", func(t *testing.T) {
		resp := &message.QueryClustersResp{
			Clusters: []structs.ClusterBaseInfo{},
			Parital:  false,
		}

		// First: non-primary region failure
		regionResult1 := RegionResult{
			resp:    message.QueryClustersResp{Clusters: []structs.ClusterBaseInfo{}},
			err:     fmt.Errorf("non-primary region error"),
			primary: false,
		}

		err1 := appendRegionResult(resp, regionResult1)
		assert.NoError(t, err1)
		assert.True(t, resp.Parital)

		// Second: successful region - should preserve partial flag
		regionResult2 := RegionResult{
			resp: message.QueryClustersResp{
				Clusters: []structs.ClusterBaseInfo{
					{ID: "successful-cluster"},
				},
			},
			err:     nil,
			primary: true,
		}

		err2 := appendRegionResult(resp, regionResult2)
		assert.NoError(t, err2)
		// Partial flag should be preserved from previous failure
		assert.True(t, resp.Parital)
		assert.Len(t, resp.Clusters, 1)
		assert.Equal(t, "successful-cluster", resp.Clusters[0].ID)
	})
}

func TestHttpClient(t *testing.T) {
	t.Skip("manual deadlock repro for httpclient; excluded from the default unit suite")

	type Node struct {
		Name string `json:"name"`
		Next *Node  `json:"next"`
	}
	n1 := Node{Name: "1", Next: nil}
	n2 := Node{Name: "2", Next: &n1}
	n1.Next = &n2
	// send an object that can't be marshalled.
	_, err := httpclient.Begin().PostJson("xxx", n2)
	if err != nil {
		fmt.Println(err.Error())
	}
	// block here as Begin() if dealock not fixed.
	httpclient.Begin().Get("xxx")
	fmt.Println("ok")
}
