package handler

import (
	"encoding/json"
	"net/url"
	"strconv"
	"testing"

	"github.com/stretchr/testify/assert"

	"github.com/pingcap/test-infra/common/model"
	"github.com/pingcap/test-infra/common/model/resource"
	reqM "github.com/pingcap/test-infra/common/model/tcms/request"
	respM "github.com/pingcap/test-infra/common/model/tcms/response"
	htest "github.com/pingcap/test-infra/services/tcms/tests/httptest"
)

func newOneshotPlan() reqM.PlanCreateRequest {
	return reqM.PlanCreateRequest{
		Type: model.TestPlanKindOneShot,
		TestPlanBase: model.TestPlanBase{
			Name: "",
			TestBed: model.TestBed{
				GenerateName: "123123",
				Items: []model.TestBedItem{{
					ResourceHandle: resource.ResourceHandle{
						Name: "tc",
						Type: "TIDB_CLUSTER",
					},
					Spec: json.RawMessage(`{
					"tidb": {
					  "replicas": 1,
					  "image": "hub.pingcap.net/qa/tidb:master"
					},
					"tikv": {
					  "repliacs": 2,
					  "image": "hub.pingcap.net/qa/tikv:master",
					  "config": "[storage]\nreserve-space = 0\n"
					},
					"pd": {
					  "repliacs": 3,
					  "image": "hub.pingcap.net/qa/tipd:master"
					}
				  }`),
				}},
				ReclaimPolicy: resource.ReclaimPolicy{
					Lease: "24h",
				},
			},
			Pipeline: []model.PipelineStep{{
				Name:    "bank-pessimistic",
				After:   []string{},
				Timeout: 0,
				CaseRef: model.CaseRef{
					CaseID: 2,
					// rewrite case image
					CaseImage: "debian",
					Arguments: []model.CaseParamBind{},
				},
			}, {
				Name: "bank-pessimistic-inline-case",
				// ref to inline case
				CaseRef: model.CaseRef{
					CaseName: "inline-case-1",
					Arguments: []model.CaseParamBind{{
						Name:  "tc",
						Value: "{{testbed.tc}}",
					}},
				},
			}},
			Variables: model.PlanVarDeclSlice{{
				Name:    "aaa",
				Default: "bbb",
			}},
			FailFast:        false,
			InterruptAction: model.InterruptActionAbort,
		},
		PlanCreateRequestOneshotSpec: reqM.PlanCreateRequestOneshotSpec{
			InlineCases: []*model.TestCaseBase{{
				Name: "inline-case-1",
				Framework: model.FrameworkUnion{
					Generic: &model.GenericFrameworkSpec{
						Command: []string{"ls", "-l"},
						Image:   "debian",
					},
				},
				Parameters: []model.CaseParamDecl{
					{Type: model.CaseParamTypeResource, Name: "tc"},
				},
			}},
		},
	}
}

func TestCreateOneshotPlan(t *testing.T) {
	h := setup(t)
	defer h.Shutdown()

	plan := newOneshotPlan()

	creatorID := model.ID(3)
	_, resp := htest.MustPOST[respM.PlanCreateResponse](h.Echo, "/api/v1/plans", htest.WithJSON(plan), htest.WithUser(creatorID))
	assert.Equal(t, len(plan.Pipeline), len(resp.Pipeline))
	assert.Equal(t, creatorID, resp.CreatedBy.ID)
	assert.Zero(t, resp.Pipeline[0].Seq)
	assert.NotZero(t, resp.Pipeline[1].Seq)
	_, err := url.Parse(resp.PlanExecutionURL)
	assert.NoError(t, err)
	assert.Equal(t, plan.InterruptAction, resp.InterruptAction)

	// check overwrited case-image
	assert.Equal(t, plan.Pipeline[0].CaseImage, resp.Pipeline[0].CaseImage)

	// check inline-case
	assert.Equal(t, plan.InlineCases[0].Name, resp.Pipeline[1].CaseName)
	assert.NotZero(t, resp.Pipeline[1].CaseID)
	_, c := htest.MustGET[model.TestCase](h.Echo, "/api/v1/cases/"+resp.Pipeline[1].CaseID.String())
	assert.NotNil(t, c)

	// check the corresponding plan-execution
	_, execs := htest.MustGET[respM.PlanExecutionSearchResponse](h.Echo, "/api/v1/plan-executions?plan_ids="+strconv.Itoa(int(resp.ID)))
	assert.Equal(t, 1, execs.Total)
	assert.Equal(t, resp.ID, execs.Data[0].PlanIDAndName.ID)
	assert.Equal(t, model.ExecPriorityUserDebug, execs.Data[0].Priority)

	// create a plan that have steps rather than pipeline
	plan = newOneshotPlan()
	plan.Steps = plan.Pipeline
	plan.Pipeline = nil
	// set all step names to empty string
	for i := range plan.Steps {
		plan.Steps[i].Name = ""
	}
	_, resp = htest.MustPOST[respM.PlanCreateResponse](h.Echo, "/api/v1/plans", htest.WithJSON(plan), htest.WithUser(creatorID))
	assert.Equal(t, len(plan.Pipeline), len(resp.Pipeline))
	assert.Equal(t, len(plan.Steps), len(resp.Steps))
	for _, i := range resp.Steps {
		assert.NotEmpty(t, i.Name)
	}
	// get the plan we created above
	_, tp := htest.MustGET[model.TestPlan](h.Echo, "/api/v1/plans/"+resp.ID.String())
	assert.NotEmpty(t, tp.Steps)
	assert.Empty(t, tp.Pipeline)
}
