package clustergateway

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

	"github.com/go-resty/resty/v2"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	clusteragentv1 "github.com/tidbcloud/cluster-service-ng/api/tidbcloud/cluster-agent/v1"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/encoding/protojson"
)

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

	body, err := protojson.Marshal(&clusteragentv1.ListPlanReplayerFilesResp{
		NextPageToken: "next",
	})
	require.NoError(t, err)

	var receivedPath string
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		receivedPath = r.URL.Path
		w.Header().Set("Content-Type", "application/json")
		_, _ = w.Write(body)
	}))
	defer server.Close()

	client := newTestClient()
	sharedPool := newTestSharedPool("pool-123", server.URL)
	resp, err := client.ListPlanReplayerFiles(
		context.Background(),
		sharedPool,
		&clusteragentv1.ListPlanReplayerFilesReq{
			ClusterId:   "987",
			StorageRoot: "s3://bucket/global-sort",
		},
	)
	require.NoError(t, err)
	assert.Equal(t, "next", resp.GetNextPageToken())
	assert.Equal(
		t,
		"/cluster-agent/"+sharedPool.GetDataPlaneNamespace()+"/api/v1/clusters/987:list_plan_replayer_files",
		receivedPath,
	)
}

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

	body, err := protojson.Marshal(&clusteragentv1.GeneratePlanReplayerFileDownloadURLResp{
		DownloadUrl: "https://download.example/file",
	})
	require.NoError(t, err)

	var receivedPath string
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		receivedPath = r.URL.Path
		w.Header().Set("Content-Type", "application/json")
		_, _ = w.Write(body)
	}))
	defer server.Close()

	client := newTestClient()
	sharedPool := newTestSharedPool("pool-123", server.URL)
	resp, err := client.GeneratePlanReplayerFileDownloadURL(
		context.Background(),
		sharedPool,
		&clusteragentv1.GeneratePlanReplayerFileDownloadURLReq{
			ClusterId:   "987",
			StorageRoot: "s3://bucket/global-sort",
			FileName:    "replayer_token_1.zip",
		},
	)
	require.NoError(t, err)
	assert.Equal(t, "https://download.example/file", resp.GetDownloadUrl())
	assert.Equal(
		t,
		"/cluster-agent/"+sharedPool.GetDataPlaneNamespace()+"/api/v1/clusters/987:generate_plan_replayer_file_download_url",
		receivedPath,
	)
}

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

	t.Run("grpc status", func(t *testing.T) {
		body, err := protojson.Marshal(status.New(codes.NotFound, "missing").Proto())
		require.NoError(t, err)

		got := planReplayerResponseError("operation", newRestyResponse(http.StatusNotFound, body))
		assert.Equal(t, codes.NotFound, status.Code(got))
	})

	t.Run("old agent plain 404", func(t *testing.T) {
		got := planReplayerResponseError(
			"operation",
			newRestyResponse(http.StatusNotFound, []byte("404 page not found")),
		)
		assert.Equal(t, codes.Unimplemented, status.Code(got))
	})

	t.Run("old agent grpc gateway route miss", func(t *testing.T) {
		body, err := protojson.Marshal(
			status.New(codes.NotFound, http.StatusText(http.StatusNotFound)).Proto(),
		)
		require.NoError(t, err)

		got := planReplayerResponseError("operation", newRestyResponse(http.StatusNotFound, body))
		assert.Equal(t, codes.Unimplemented, status.Code(got))
	})
}

func newRestyResponse(statusCode int, body []byte) *resty.Response {
	return (&resty.Response{
		RawResponse: &http.Response{StatusCode: statusCode},
	}).SetBody(body)
}
