package client

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/juju/errors"
	"github.com/ngaut/log"
	"github.com/pingcap/schrodinger/config"
	"github.com/pingcap/schrodinger/pkg/notify/slack"
	"github.com/pingcap/schrodinger/pkg/types"
	"github.com/pingcap/schrodinger/util"
)

// GetTests gets test for current experiment
// return empty slice of *TestInfo for compatible if mode is dev
func (c *Client) GetTests() []*TestInfo {
	if err := c.refreshConfig(); err != nil {
		Fatalf("init client config error %v", errors.ErrorStack(err))
	}

	var testInfos []*TestInfo
	if !c.isOnline() {
		Errorf("only mode online is permitted. the mode is %s", c.mode)
		return testInfos
	}
	return c.conf.Tests
}

// StopTest stops test
func (c *Client) StopTest(name string) error {
	if !c.isOnline() {
		Errorf("only mode online is permitted. the mode is %s", c.mode)
		return nil
	}

	if err := c.refreshConfig(); err != nil {
		Fatalf("init client config error %v", errors.ErrorStack(err))
	}

	_, err := postAndExtract(fmt.Sprintf("%s/boxes/%d/experiments/%d/tests/%s/stop", c.managerAddr, c.conf.Experiment.BoxID, c.conf.Experiment.ID, name), []byte(""))
	if err != nil {
		Errorf(errors.ErrorStack(err))
		return errors.Trace(err)
	}
	return nil
}

// StartTest starts test
func (c *Client) StartTest(name string) error {
	if !c.isOnline() {
		Errorf("only mode online is permitted. the mode is %s", c.mode)
		return nil
	}
	if err := c.refreshConfig(); err != nil {
		Fatalf("init client config error %v", errors.ErrorStack(err))
	}

	_, err := postAndExtract(fmt.Sprintf("%s/boxes/%d/experiments/%d/tests/%s/start", c.managerAddr, c.conf.Experiment.BoxID, c.conf.Experiment.ID, name), []byte(""))
	if err != nil {
		Errorf(errors.ErrorStack(err))
		return errors.Trace(err)
	}
	return nil
}

// RemoveTest removes test
func (c *Client) RemoveTest(name string) error {
	if err := c.refreshConfig(); err != nil {
		Fatalf("init client config error %v", errors.ErrorStack(err))
	}
	if !c.isOnline() {
		Errorf("only mode online is permitted. the mode is %s", c.mode)
		return nil
	}

	_, err := postAndExtract(fmt.Sprintf("%s/boxes/%d/experiments/%d/tests/%s/remove", c.managerAddr, c.conf.Experiment.BoxID, c.conf.Experiment.ID, name), []byte(""))
	if err != nil {
		Errorf(errors.ErrorStack(err))
		return errors.Trace(err)
	}
	return nil
}

// ReportTestResult sends bench result to manager.
// If the ID is specified, will update the old value.
func (c *Client) ReportTestResult(result *types.TestResult) error {
	if err := c.refreshConfig(); err != nil {
		Fatalf("init client config error %v", errors.ErrorStack(err))
	}

	if !c.isOnline() {
		Errorf("only mode online is permitted. the mode is %s", c.mode)
		return nil
	}

	if result == nil {
		return errors.Errorf("test result is nil")
	}

	if result.TestName == "" {
		Infof("test name is empty, will use %s as the test name", c.conf.Self.Name)
		result.TestName = c.conf.Self.Name
	}

	result.ExperimentID = c.conf.Experiment.ID

	Infof("send test result [%v] to schrodinger", result)

	data, err := json.Marshal(result)
	if err != nil {
		return errors.Trace(err)
	}

	respData, err := postAndExtract(fmt.Sprintf("%s/tests/results", c.managerAddr), data)
	if err != nil {
		return errors.Trace(err)
	}

	if err = json.Unmarshal(respData, result); err != nil {
		return errors.Trace(err)
	}
	return nil
}

// SendReport sends report to slack
func (c *Client) SendReport(message string) {
	if util.IsEmpty(config.GetGlobalConfig().Notifiers.Slack.Token) {
		log.Errorf("slack token is empty")
		return
	}

	conf := config.GetGlobalConfig().Notifiers

	slackNotifier, err := slack.New(&conf.Slack)
	if err != nil {
		log.Errorf("fail to create slack client, error: %v", err)
		return
	}

	notification := slack.Notification{
		Channel:    c.conf.Experiment.SlackChannelForError,
		Username:   config.GetGlobalConfig().Notifiers.Slack.Username,
		Title:      fmt.Sprintf("experiment: [%s] \n test: %s", c.conf.Experiment.Name, c.conf.Self.Name),
		Text:       message,
		Color:      slack.SuccessColor,
		MarkDownIn: []string{"text"},
	}

	if err = slackNotifier.Notify(context.Background(), notification); err != nil {
		log.Errorf("fail to send report to slack, error: %v", err)
	}
}

// GetTestReport gets test report
func (c *Client) GetTestReport(testName string) (*types.TestReport, error) {
	respData, err := getAndExtract(fmt.Sprintf("%s/boxes/%d/experiments/%d/tests/%s", c.managerAddr, c.boxID, c.experimentID, testName))
	if err != nil {
		return nil, errors.Trace(err)
	}

	var testProc types.TestProcess
	if err := json.Unmarshal(respData, &testProc); err != nil {
		return nil, errors.Trace(err)
	}
	return testProc.Report, nil
}
