package resource

import (
	"fmt"

	comM "github.com/pingcap/test-infra/common/model"
	reqM "github.com/pingcap/test-infra/common/model/tcms/request"
	respM "github.com/pingcap/test-infra/common/model/tcms/response"
	"github.com/pingcap/test-infra/tools/tcctl/pkg/config"
	"github.com/pingcap/test-infra/tools/tcctl/pkg/model"
	"github.com/pingcap/test-infra/tools/tcctl/pkg/util"
)

type TestCase struct {
	// apiServer config
	serverConfig config.ApiServerConfig

	doc    model.RawContent
	req    reqM.CaseCreateRequest
	id     comM.ID
	synced bool
}

func NewTestCase(serverConfig config.ApiServerConfig, doc model.Resource) (*TestCase, error) {
	t := &TestCase{serverConfig: serverConfig, doc: doc.RawContent, id: doc.ID}
	err := t.doc.UnMarshal(&t.req)
	if err != nil {
		return nil, err
	}
	return t, nil
}

func (t *TestCase) Name() string {
	return t.req.Name
}

func (t *TestCase) SyncID() error {
	if existed, _ := PullCase(t.serverConfig, t.req.Name); existed != nil {
		t.id = existed.ID
	}
	t.synced = true
	return nil
}

func (t TestCase) assertSynced() {
	if !t.synced {
		panic("resource not synced")
	}
}

func (t TestCase) IsExist() (bool, error) {
	t.assertSynced()
	return t.id > 0, nil
}

func (t TestCase) Create(dryrun bool) (interface{}, error) {
	t.assertSynced()
	return CreateCase(t.serverConfig, t.req, dryrun)
}

func (t TestCase) Update(dryrun bool) (interface{}, error) {
	t.assertSynced()
	return UpdateCase(t.serverConfig, t.id, t.req, dryrun)
}

func (t TestCase) Delete() error {
	existed, err := t.IsExist()
	if err != nil {
		return err
	}
	if existed {
		return DeleteCase(t.serverConfig, t.id)
	}
	return nil
}

func PullCase(config config.ApiServerConfig, name string) (*comM.TestCase, error) {
	path := "api/v1/cases/autocomplete"
	var rsp = &respM.CaseListResponse{}
	_, err := util.Req().Host(config.TcmsHost).Bearer(config.Token).Path(path).Param(map[string]string{"query": name}).Get(rsp)
	if err != nil {
		return nil, err
	}
	for _, each := range rsp.Data {
		if each.Name == name {
			return each, nil
		}
	}
	return nil, nil
}

func CreateCase(config config.ApiServerConfig, tc reqM.CaseCreateRequest, dryrun bool) (*comM.TestCase, error) {
	path := "api/v1/cases"
	var rsp = &comM.TestCase{}
	_, err := util.Req().Host(config.TcmsHost).Bearer(config.Token).Path(path).DryRun(dryrun).Json(tc).Post(rsp)
	if err != nil {
		return nil, err
	}
	return rsp, nil
}

func UpdateCase(config config.ApiServerConfig, id comM.ID, tc reqM.CaseCreateRequest, dryrun bool) (*comM.TestCase, error) {
	path := fmt.Sprintf("api/v1/cases/%d", id)
	var rsp = &comM.TestCase{}
	_, err := util.Req().Host(config.TcmsHost).Bearer(config.Token).Path(path).DryRun(dryrun).Json(tc).Put(rsp)
	if err != nil {
		return nil, err
	}
	return rsp, nil
}

func DeleteCase(config config.ApiServerConfig, id comM.ID) error {
	path := fmt.Sprintf("api/v1/cases/%d", id)
	rsp := new(map[string]interface{})
	_, err := util.Req().Host(config.TcmsHost).Bearer(config.Token).Path(path).Into(rsp).Delete()
	if err != nil {
		return err
	}
	return nil
}
