package client

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/juju/errors"
	"github.com/pingcap/schrodinger/manager/api"
	"github.com/pingcap/schrodinger/pkg/types"
	"github.com/pingcap/schrodinger/util"
)

// CreateTestTemplate creates a test template by schrodinger api
func (c *Client) CreateTestTemplate(tc *types.TestTemplate) (*types.TestTemplate, error) {
	t, err := json.Marshal(tc)
	if err != nil {
		return nil, errors.Trace(err)
	}

	url := fmt.Sprintf("%s/tests/templates", c.cfg.Addr)
	data, err := util.PostAndExtract(c.client, url, t)
	if err != nil {
		return nil, errors.Trace(err)
	}
	if err := json.Unmarshal(data, tc); err != nil {
		return nil, errors.Trace(err)
	}
	return tc, nil
}

// UpdateTestTemplate updates a test template by schrodinger api
func (c *Client) UpdateTestTemplate(tc *types.TestTemplate) (*types.TestTemplate, error) {
	t, err := json.Marshal(tc)
	if err != nil {
		return nil, errors.Trace(err)
	}

	url := fmt.Sprintf("%s/tests/templates/%s/%d", c.cfg.Addr, tc.Name, tc.ID)
	_, err = util.PutAndExtract(c.client, url, t)
	if err != nil {
		return nil, errors.Trace(err)
	}

	return tc, nil
}

// SetTestTemplate sets a test template by schrodinger api,
// if the test is exists, `SetTemplate` will update this test template
func (c *Client) SetTestTemplate(tc *types.TestTemplate) (*types.TestTemplate, error) {
	if tc.Source.Type == types.Latest {
		getURL := fmt.Sprintf("%s/tests/templates/%s/latest", c.cfg.Addr, tc.Name)
		data, err := util.GetAndExtract(c.client, getURL)
		if err == nil {
			var tt types.TestTemplate
			if err := json.Unmarshal(data, &tt); err != nil {
				return nil, errors.Trace(err)
			}
			tc.ID = tt.ID
			return c.UpdateTestTemplate(tc)
		}
		if !strings.Contains(err.Error(), "not found") {
			return nil, errors.Trace(err)
		}
	}
	if tc.Source.Type == types.GitSource && tc.Source.GitValue.GitType == types.GitTypePr {
		getURL := fmt.Sprintf("%s/tests/templates/%s/pr/%s", c.cfg.Addr, tc.Name, tc.Source.GitValue.Value)
		data, err := util.GetAndExtract(c.client, getURL)
		if err == nil {
			var tt types.TestTemplate
			if err := json.Unmarshal(data, &tt); err != nil {
				return nil, errors.Trace(err)
			}
			tc.ID = tt.ID
			return c.UpdateTestTemplate(tc)
		}
		if !strings.Contains(err.Error(), "not found") {
			return nil, errors.Trace(err)
		}
	}
	return c.CreateTestTemplate(tc)
}

// DeleteTestTemplate deletes a test template by schrodinger api
func (c *Client) DeleteTestTemplate(name string, id int64) error {
	deleteURL := fmt.Sprintf("%s/tests/templates/%s/%d", c.cfg.Addr, name, id)
	_, err := util.DeleteAndExtract(c.client, deleteURL)
	if err != nil {
		return errors.Trace(err)
	}

	return nil
}

// ListTestTemplates lists test templates
func (c *Client) ListTestTemplates(limit, offset int64) (types.TestTemplates, error) {
	listURL := fmt.Sprintf("%s/tests/templates?size=%d&offset=%d", c.cfg.Addr, limit, offset)
	respData, err := util.GetAndExtract(c.client, listURL)
	if err != nil {
		return nil, errors.Trace(err)
	}

	listData := api.ListData{}
	if err = json.Unmarshal(respData, &listData); err != nil {
		return nil, errors.Trace(err)
	}

	templatesJSON, _ := json.Marshal(listData.List)

	var respTests types.TestTemplates
	if err = json.Unmarshal(templatesJSON, &respTests); err != nil {
		return nil, errors.Trace(err)
	}

	return respTests, nil
}
