package gateway

import (
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"sync/atomic"
	"testing"
	"time"

	"github.com/stretchr/testify/require"
	"github.com/tidbcloud/pov-service/internal/client/rms"
	"github.com/tidbcloud/pov-service/internal/client/tcms"
	"github.com/tidbcloud/pov-service/internal/client/tiworkload"
	"github.com/tidbcloud/pov-service/internal/model"
)

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

	createTask := tcms.WorkflowTask{
		Status:   "SUCCEEDED",
		NodeName: "TESTBED_CREATE",
		Action: struct {
			Kind string         `json:"kind"`
			Spec map[string]any `json:"spec"`
		}{
			Kind: "TESTBED_CREATE",
			Spec: map[string]any{},
		},
		Outputs: map[string]any{
			"testbed_items": map[string]any{
				"object": []any{
					map[string]any{"type": "TIDBCLOUD_SERVERLESS"},
				},
			},
			"testbed_name": map[string]any{"string": "essential-testbed"},
			"testbed_id":   map[string]any{"int": float64(101)},
			"tc": map[string]any{
				"object": map[string]any{
					"private_endpoint": "root:@tcp(127.0.0.1:4000)",
					"cluster_id":       "cluster-123",
					"tenant_id":        "tenant-1",
					"cluster_ru": map[string]any{
						"min": float64(100),
						"max": float64(200),
					},
				},
			},
		},
		StateOutput: map[string]any{
			"planExecutionID": float64(2024),
		},
	}

	workflowExec := tcms.WorkflowExec{
		WorkloadTaskBase: model.WorkloadTaskBase{
			ID:  123,
			Typ: model.WorkloadTaskTypeCreateCluster,
			ExecBase: model.ExecBase{
				Status: model.ExecStatusRunning,
			},
		},
		Status: "SUCCESS",
		Spec: tcms.WorkflowSpec{
			Nodes: []tcms.WorkflowNode{
				{Type: "dummy"},
			},
		},
		Graph: tcms.WorkflowGraph{
			CompletedTasks: []tcms.WorkflowTask{
				{
					Status: "SUCCEEDED",
					Action: struct {
						Kind string         `json:"kind"`
						Spec map[string]any `json:"spec"`
					}{
						Kind: "START",
						Spec: map[string]any{},
					},
				},
			},
			FinalizeTasks: []tcms.WorkflowTask{createTask},
			OngoingTasks:  []tcms.WorkflowTask{},
		},
	}

	tcmsServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		require.Equal(t, http.MethodGet, r.Method)
		require.Equal(t, "/api/v1/workflow-executions/123", r.URL.Path)
		w.Header().Set("Content-Type", "application/json")
		require.NoError(t, json.NewEncoder(w).Encode(workflowExec))
	}))
	defer tcmsServer.Close()

	var registerCalls int32
	var listCalls int32

	tiMux := http.NewServeMux()
	tiMux.HandleFunc("/api/v1/service/clusters", func(w http.ResponseWriter, r *http.Request) {
		switch r.Method {
		case http.MethodPost:
			atomic.AddInt32(&registerCalls, 1)
			require.Equal(t, "99902", r.Header.Get("X-User-Id"))
			w.Header().Set("Content-Type", "application/json")
			_, _ = w.Write([]byte(`{"id":"1","agent":{"id":"agent-123"}}`))
		case http.MethodGet:
			atomic.AddInt32(&listCalls, 1)
			require.Equal(t, "agent-123", r.URL.Query().Get("agentId"))
			w.Header().Set("Content-Type", "application/json")
			_, _ = w.Write([]byte(`{"total":1,"clusters":[{"id":"cluster-123","handle":"ready","agent":{"id":"agent-123"}}]}`))
		default:
			http.NotFound(w, r)
		}
	})

	tiServer := httptest.NewServer(tiMux)
	defer tiServer.Close()

	tcmsClient := tcms.NewClient(tcmsServer.URL, "", "", 0)
	tiClient := tiworkload.NewClient(tiServer.URL, "ak", "sk")
	rmsClient := rms.NewClient("http://rms", "")

	gw := workloadImpl{
		tcmsClient:       tcmsClient,
		rmsClient:        rmsClient,
		tiworkloadClient: tiClient,
		paramHelper:      newParamHelper(tiClient, false),
	}

	ctx := context.Background()
	task := model.WorkloadTaskBase{RelatedServiceID: "123"}

	pollTask, result, err := gw.PollCreateClusterTask(ctx, nil, task)
	require.NoError(t, err)
	require.NotNil(t, result.Cluster)
	require.Equal(t, model.ClusterTypeTiDBCloudEssential, result.Cluster.Type)
	require.Equal(t, "agent-123", result.Cluster.TiWorkloadAgentID)
	require.Equal(t, int32(1), atomic.LoadInt32(&registerCalls))
	require.Equal(t, int32(0), atomic.LoadInt32(&listCalls))
	require.Empty(t, result.Cluster.TiWorkloadClusterID)
	require.Equal(t, task, pollTask)

	// Simulate subsequent poll with existing cluster state.
	clusterState := result.Cluster
	require.NotNil(t, clusterState)

	time.Sleep(10 * time.Millisecond)

	pollTask, result, err = gw.PollCreateClusterTask(ctx, clusterState, pollTask)
	require.NoError(t, err)
	require.Equal(t, model.ExecStatusCompleted, pollTask.Status)
	require.Equal(t, "cluster-123", result.Cluster.TiWorkloadClusterID)
	require.Same(t, clusterState, result.Cluster)
	require.Equal(t, int32(1), atomic.LoadInt32(&registerCalls))
	require.Equal(t, int32(1), atomic.LoadInt32(&listCalls))
}
