package regionalserver

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

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	clusterngv1beta "github.com/tidbcloud/cluster-service-ng/api/cluster-service-ng/v1beta"
	"google.golang.org/grpc/codes"

	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

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

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, request *http.Request) {
		assert.Equal(t, http.MethodGet, request.Method)
		assert.Equal(t, "/api/premium/v1/sharedPoolProvisionConfiguration", request.URL.Path)
		w.Header().Set("Content-Type", "application/json")
		_, _ = w.Write([]byte(`{"o11yClusterCidr":"10.128.0.0/16"}`))
	}))
	defer server.Close()

	response, err := newTestRegionalCli().GetSharedPoolProvisionConfiguration(
		context.Background(),
		newActiveRegion(server.URL),
		&clusterngv1beta.GetSharedPoolProvisionConfigurationRequest{},
	)
	require.Nil(t, err)
	require.Equal(t, "10.128.0.0/16", response.GetO11YClusterCidr())
}

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

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		http.Error(w, `{"code":12,"message":"not implemented"}`, http.StatusNotImplemented)
	}))
	defer server.Close()

	_, err := newTestRegionalCli().GetSharedPoolProvisionConfiguration(
		context.Background(),
		newActiveRegion(server.URL),
		&clusterngv1beta.GetSharedPoolProvisionConfigurationRequest{},
	)
	require.NotNil(t, err)
	require.Equal(t, bizerr.ErrCode(codes.Unavailable), err.ErrCode())
}

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

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, request *http.Request) {
		assert.Equal(t, http.MethodPost, request.Method)
		assert.Equal(t, "/api/premium/v1/sharedPools:evaluateNetworkCapacity", request.URL.Path)
		body := map[string]any{}
		assert.NoError(t, json.NewDecoder(request.Body).Decode(&body))
		assert.InDelta(t, 18, body["cidrPrefixLength"], 0)
		assert.Equal(t, "REGIONAL", body["highAvailabilityType"])
		w.Header().Set("Content-Type", "application/json")
		_, _ = w.Write([]byte(`{"supported":true,"supportedNodeCount":"64"}`))
	}))
	defer server.Close()

	prefix := int32(18)
	response, err := newTestRegionalCli().EvaluateSharedPoolNetworkCapacity(
		context.Background(),
		newActiveRegion(server.URL),
		&clusterngv1beta.EvaluateSharedPoolNetworkCapacityRequest{
			CidrPrefixLength:     &prefix,
			HighAvailabilityType: clusterngv1beta.CreateSharedPoolRequest_InitSharedPoolConfig_REGIONAL,
		},
	)
	require.Nil(t, err)
	require.True(t, response.GetSupported())
	require.EqualValues(t, 64, response.GetSupportedNodeCount())
}

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

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		_, _ = w.Write([]byte(`{"code":3,"message":"invalid request"}`))
	}))
	defer server.Close()

	prefix := int32(18)
	_, err := newTestRegionalCli().EvaluateSharedPoolNetworkCapacity(
		context.Background(),
		newActiveRegion(server.URL),
		&clusterngv1beta.EvaluateSharedPoolNetworkCapacityRequest{CidrPrefixLength: &prefix},
	)
	require.NotNil(t, err)
	require.Equal(t, bizerr.ErrCodeRegionalAPI, err.ErrCode())
}

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

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusBadGateway)
		_, _ = w.Write([]byte(`not-json`))
	}))
	defer server.Close()

	prefix := int32(18)
	_, err := newTestRegionalCli().EvaluateSharedPoolNetworkCapacity(
		context.Background(),
		newActiveRegion(server.URL),
		&clusterngv1beta.EvaluateSharedPoolNetworkCapacityRequest{CidrPrefixLength: &prefix},
	)
	require.NotNil(t, err)
	require.Equal(t, bizerr.ErrCodeRegionalAPI, err.ErrCode())
}

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

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusConflict)
		_, _ = w.Write([]byte(`{"code":6,"message":"conflict"}`))
	}))
	defer server.Close()

	prefix := int32(18)
	_, err := newTestRegionalCli().EvaluateSharedPoolNetworkCapacity(
		context.Background(),
		newActiveRegion(server.URL),
		&clusterngv1beta.EvaluateSharedPoolNetworkCapacityRequest{CidrPrefixLength: &prefix},
	)
	require.NotNil(t, err)
	require.Equal(t, bizerr.ErrCodeRegionalAPI, err.ErrCode())
}

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

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, request *http.Request) {
		assert.Equal(t, http.MethodPost, request.Method)
		assert.Equal(t, "/api/premium/v1/sharedPools/42:updateCloudResourceTags", request.URL.Path)

		body := map[string]any{}
		assert.NoError(t, json.NewDecoder(request.Body).Decode(&body))
		assert.Equal(t, map[string]any{"environment": "test"}, body["cloudResourceTags"])
		assert.Equal(t, []any{"deprecated"}, body["removeCloudResourceTagKeys"])

		w.Header().Set("Content-Type", "application/json")
		_, _ = w.Write([]byte(`{"id":"42","cloudResourceTags":{"environment":"test","team":"database"}}`))
	}))
	defer server.Close()

	response, err := newTestRegionalCli().UpdateSharedPoolCloudResourceTags(
		context.Background(),
		newActiveRegion(server.URL),
		&clusterngv1beta.UpdateSharedPoolCloudResourceTagsRequest{
			SharedPoolId:               "42",
			CloudResourceTags:          map[string]string{"environment": "test"},
			RemoveCloudResourceTagKeys: []string{"deprecated"},
		},
	)
	require.Nil(t, err)
	require.Equal(t, map[string]string{
		"environment": "test",
		"team":        "database",
	}, response.GetCloudResourceTags())
}
