package handler

import (
	"encoding/json"
	"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"
)

const NEW_PLAN_NAME = "plan100"

func newPlan() reqM.PlanCreateRequest {
	return reqM.PlanCreateRequest{
		Type: model.TestPlanKindGeneral,
		TestPlanBase: model.TestPlanBase{
			Name: NEW_PLAN_NAME,
			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",
				},
			},
			Pipeline: []model.PipelineStep{{
				Name:  "bank-pessimistic",
				After: []string{},
				CaseRef: model.CaseRef{
					CaseID:    2,
					Arguments: []model.CaseParamBind{},
				},
			}, {
				Name:  "bank-optimistic",
				After: []string{"bank-pessimistic"},
				CaseRef: model.CaseRef{
					CaseID: 1,
					Arguments: []model.CaseParamBind{
						{Name: "version", Value: "nightly"},
					},
				},
			}, {
				Name:  "bank-optimistic-2",
				After: []string{"bank-optimistic"},
				CaseRef: model.CaseRef{
					CaseName: "case2",
				},
			}},
			MetaParams: []model.PlanVarDecl{
				{
					Name:    "base_image_version",
					Default: "master",
				},
			},
			InterruptAction: model.InterruptActionAbort,
		},
	}
}

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

	_, plans := htest.MustGET[respM.PlanListResponse](h.Echo, "/api/v1/plans?type=GENERAL&offset=0&count=2")
	assert.Equal(t, 2, plans.Total)

	// list by ids
	_, plans = htest.MustGET[respM.PlanListResponse](h.Echo, "/api/v1/plans?ids=2&offset=0&count=2")
	assert.Equal(t, 2, int(plans.Data[0].ID))
}

// FIXME: this test is not working
//
// func TestQueryPlans(t *testing.T) {
// 	h := setup()
//  defer h.Shutdown()

// 	rec := htest.Serve(e, echo.GET, "/api/v1/plans?type=GITHUB&query=plan&branch=release-5.0&offset=0&count=2", nil)
// 	rec := httptest.NewRecorder()
// 	c := utils.NewContext(h.Echo.NewContext(req, rec))
// 	assert.NoError(t, h.ListPlans(c))
// 	// 		plans := respM.PlanSearchResponse{}
//
// 		assert.Equal(t, 2, plans.Total)
// 	}
// }

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

	_, plans := htest.MustGET[respM.PlanListResponse](h.Echo, "/api/v1/plans/autocomplete?type=GENERAL&query=pla")
	assert.Equal(t, 2, plans.Total)
}

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

	_, tp := htest.MustGET[model.TestPlan](h.Echo, "/api/v1/plans/1")
	assert.Equal(t, 1, int(tp.ID))
	assert.Equal(t, 1, int(tp.CreatedBy.ID))
	assert.Equal(t, 2, len(tp.Pipeline))
	assert.NotNil(t, tp.UpdatedAt)
	assert.NotEmpty(t, tp.Pipeline[0].CaseName)
	assert.NotZero(t, tp.Pipeline[0].CaseID)
}

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

	creatorID := model.ID(3)
	p := newPlan()

	_, tp := htest.MustPOST[model.TestPlan](h.Echo, "/api/v1/plans", htest.WithJSON(p), htest.WithUser(creatorID))
	assert.Equal(t, p.Name, tp.Name)
	assert.Equal(t, len(p.Pipeline), len(tp.Pipeline))
	assert.Equal(t, creatorID, tp.CreatedBy.ID)
	assert.Equal(t, "base_image_version", tp.MetaParams[0].Name)
	assert.Equal(t, p.InterruptAction, tp.InterruptAction)
	assert.NotEqual(t, 240, len(tp.TestBed.Items[0].Spec))

	tpb, err := tp.PatchParameters(nil, false)
	tp.TestPlanBase = *tpb
	assert.NoError(t, err)
	assert.Equal(t, "base_image_version", tp.MetaParams[0].Name)
	assert.Equal(t, 240, len(tp.TestBed.Items[0].Spec)) // make sure patched
}

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

	_, resp := htest.MustGET[respM.ListReleasePlansResponse](h.Echo, "/api/v1/plans/release?triggerID=3&branch=release-5.0")
	assert.Equal(t, 2, resp.Total)
	assert.Equal(t, false, resp.Data[0].Triggerred)
	assert.Equal(t, true, resp.Data[1].Triggerred)
	assert.Equal(t, 5, int(resp.Data[0].ID))
	assert.Equal(t, "release-5.0", resp.Data[0].Branch)
	assert.Equal(t, 4, int(resp.Data[1].ID))
}

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

	// delete plan 1
	_, result := htest.MustDELETE[map[string]string](h.Echo, "/api/v1/plans/1", htest.WithUser(1))
	assert.Equal(t, map[string]string{"result": "ok"}, result)

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