package handler

import (
	"bytes"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/labstack/echo/v4"
	"github.com/stretchr/testify/assert"

	"github.com/pingcap/test-infra/common/model"
	respM "github.com/pingcap/test-infra/common/model/tcms/response"
	"github.com/pingcap/test-infra/services/tcms/utils"
)

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

	// not show released
	req := httptest.NewRequest(echo.GET, "/api/v1/testbeds/info", nil)
	rec := httptest.NewRecorder()
	ctx := utils.NewContext(h.Echo.NewContext(req, rec))
	assert.NoError(t, WithAuth(ctx, h.ListTestBedInfos, 1))
	if assert.Equal(t, http.StatusOK, rec.Code) {
		tbs := respM.TestBedInfoListResponse{}
		assert.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tbs))
		assert.Equal(t, 5, tbs.Total)
	}

	// show released
	req = httptest.NewRequest(echo.GET, "/api/v1/testbeds/info?show_released=true", nil)
	rec = httptest.NewRecorder()
	ctx = utils.NewContext(h.Echo.NewContext(req, rec))
	assert.NoError(t, WithAuth(ctx, h.ListTestBedInfos, 1))
	if assert.Equal(t, http.StatusOK, rec.Code) {
		tbs := respM.TestBedInfoListResponse{}
		assert.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tbs))
		assert.Equal(t, 9, tbs.Total)
	}

	// filter by plan-exec-id
	req = httptest.NewRequest(echo.GET, "/api/v1/testbeds/info?plan_exec_id=8", nil)
	rec = httptest.NewRecorder()
	ctx = utils.NewContext(h.Echo.NewContext(req, rec))
	assert.NoError(t, WithAuth(ctx, h.ListTestBedInfos, 1))
	if assert.Equal(t, http.StatusOK, rec.Code) {
		tbs := respM.TestBedInfoListResponse{}
		assert.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tbs))
		assert.Equal(t, 4, tbs.Total)
	}
}

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

	pid := model.ID(111)
	tb := model.TestBedInfo{
		Name:            "new-testbed",
		PlanExecutionID: pid,
		URL:             "I'm testbed url",
		ResourcePool:    model.ResourcePool{Name: "default"},
	}

	rawTb := utils.ToJSON(tb)

	req := httptest.NewRequest(echo.POST, "/api/v1/testbeds/info", bytes.NewReader(rawTb))
	req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
	rec := httptest.NewRecorder()
	ctx := utils.NewContext(h.Echo.NewContext(req, rec))
	assert.NoError(t, WithAuth(ctx, h.CreateTestBedInfo, 1))
	assert.Equal(t, http.StatusOK, rec.Code)

	// case is already exist, failed
	rec = httptest.NewRecorder()
	ctx = utils.NewContext(h.Echo.NewContext(req, rec))
	assert.Error(t, WithAuth(ctx, h.CreateTestBedInfo, 1))
}

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

	// update testbed "testbed_amy" status to READY
	req := httptest.NewRequest(echo.PUT, "/api/v1/plan-executions/:id/testbeds/:name/info?status=READY", nil)
	rec := httptest.NewRecorder()
	ctx := utils.NewContext(h.Echo.NewContext(req, rec))
	ctx.SetPath("/api/v1/plan-executions/:id/testbeds/:name/info")
	ctx.SetParamNames("id", "name")
	ctx.SetParamValues("4", "testbed_amy")
	// request RMS will cause an error
	assert.Error(t, WithAuth(ctx, h.UpdateTestBedInfoStatus, 1))

	// filter by plan-exec-id
	req = httptest.NewRequest(echo.GET, "/api/v1/testbeds/info?plan_exec_id=4", nil)
	rec = httptest.NewRecorder()
	ctx = utils.NewContext(h.Echo.NewContext(req, rec))
	assert.NoError(t, WithAuth(ctx, h.ListTestBedInfos, 1))
	if assert.Equal(t, http.StatusOK, rec.Code) {
		tbs := respM.TestBedInfoListResponse{}
		assert.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tbs))
		assert.Equal(t, model.TestBedInfoStatusReady, tbs.Data[0].Status)
	}
}

// TODO(@codingpoet): complete these uni-tests
func TestGetTestBeds(t *testing.T) {
}

func TestRenewTestBed(t *testing.T) {
}

func TestReleaseTestBed(t *testing.T) {
}

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

	req := httptest.NewRequest(echo.PUT, "/api/v1/testbeds/:name/restore-monitor", nil)
	rec := httptest.NewRecorder()
	ctx := utils.NewContext(h.Echo.NewContext(req, rec))
	ctx.SetPath("/api/v1/testbeds/:name/restore-monitor")
	ctx.SetParamNames("name")
	ctx.SetParamValues("todo")
}
