package handler

import (
	"encoding/json"
	"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 newGHPlan() *reqM.PlanCreateRequest {
	return &reqM.PlanCreateRequest{
		Type: model.TestPlanKindGithub,
		TestPlanBase: model.TestPlanBase{
			Name: NEW_PLAN_NAME,
			Variables: []model.PlanVarDecl{{
				Name:    "var1",
				Default: "val1",
			}},
			MetaParams: []model.PlanVarDecl{
				{
					Name:    "base_image_version",
					Default: "master",
				},
			},
			TestBed: model.TestBed{
				GenerateName: "123123",
				Items: []model.TestBedItem{{
					ResourceHandle: resource.ResourceHandle{
						Name: "tidb-1",
						Type: "TIDB_CLUSTER",
					},
					Spec: json.RawMessage(`{
					"version": "{{meta.parameters.base_image_version}}",
					"tidb": {
					  "replicas": 1,
					  "baseImage": "hub.pingcap.net/qa/tidb"
					},
					"tikv": {
					  "repliacs": 2,
					  "baseImage": "hub.pingcap.net/qa/tikv",
					  "config": "[storage]\nreserve-space = 0\n"
					},
					"pd": {
					  "repliacs": 3,
					  "baseImage": "hub.pingcap.net/qa/tipd"
					}
				  }`),
				}},
				ReclaimPolicy: resource.ReclaimPolicy{
					Lease: "24h",
					GcStrategy: resource.GcStrategy{
						OnFailure: &resource.GcAction{
							Artifact: &resource.GcArtifactAction{
								Monitor: true,
								Log:     true,
							},
							ReserveEnv: &resource.GcReserveEnvAction{
								DurationOnWorkingHour: "1h",
							},
						},
					},
				},
			},
			Pipeline: []model.PipelineStep{{
				Name: "bank-optimistic",
				CaseRef: model.CaseRef{
					CaseID: 1,
					Arguments: []model.CaseParamBind{
						{Name: "version", Value: "nightly"},
					},
				},
			}, {
				Name: "bank-optimistic-2",
				CaseRef: model.CaseRef{
					CaseName: "case1",
					Arguments: []model.CaseParamBind{
						{Name: "version", Value: "nightly"},
					},
				},
			}},
			FailFast:        false,
			InterruptAction: model.InterruptActionAbort,
		},
		Priority: model.ExecPriorityHighPlusOne,
		GitHubPlanInfo: &model.GitHubPlanInfo{
			Repo:       "pingcap/test-plan",
			Branch:     "main",
			CommitHash: "7dae1d3",
			CommitTs:   1628107612,
			Path:       "tidb/xxx.daily.yaml",
			Tags:       []string{"111"},
			Schedule:   "0 0 * * 1",
		},
	}
}

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

	plan := newGHPlan()
	_, res := htest.MustPOST[map[string]int](h.Echo, "/api/v1/gh-plans", htest.WithJSON(plan))
	assert.Greater(t, res["id"], 0)
	id := res["id"]

	// check plan
	_, tp := htest.MustGET[model.TestPlan](h.Echo, "/api/v1/plans/"+strconv.Itoa(id))
	assert.Equal(t, id, int(tp.ID))
	assert.Equal(t, plan.Tags, tp.Tags)
	assert.Equal(t, plan.Schedule, tp.Schedule)
	assert.Equal(t, plan.Variables, tp.Variables)
	assert.Equal(t, plan.InterruptAction, tp.InterruptAction)
	assert.Equal(t, plan.Pipeline[0].CaseID, tp.Pipeline[0].CaseID)
	assert.Equal(t, plan.Pipeline[1].CaseName, tp.Pipeline[1].CaseName)

	// check the corresponding trigger
	_, triggers := htest.MustGET[respM.TriggerListResponse](h.Echo, "/api/v1/triggers?plan_id="+strconv.Itoa(id)+"&type=CRON")
	assert.Equal(t, 1, triggers.Total)
	assert.Equal(t, id, int(triggers.Data[0].PlanID))
	assert.Equal(t, plan.Schedule, triggers.Data[0].Expr)
	assert.Equal(t, plan.Priority, triggers.Data[0].ExecPriority)

	// create a plan that have steps rather than pipeline
	plan = newGHPlan()
	plan.Steps = plan.Pipeline
	plan.Pipeline = nil
	_, body := htest.MustPOST[map[string]model.ID](h.Echo, "/api/v1/gh-plans", htest.WithJSON(plan))
	assert.NotZero(t, body["id"])
}

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

	_, body := htest.MustDELETE[map[string]string](h.Echo, "/api/v1/gh-plans/old?branch=main")
	assert.Equal(t, body["result"], "ok")

	_, tp := htest.MustGET[model.TestPlan](h.Echo, "/api/v1/plans/2")
	assert.NotNil(t, tp.DeletedAt)
}
