package storage

import (
	"time"

	"github.com/ngaut/log"
	. "github.com/pingcap/check"
	"github.com/pingcap/schrodinger/pkg/types"
	"github.com/pingcap/schrodinger/util"
)

var _ = Suite(&experimentProcessSuite{})

type experimentProcessSuite struct {
	store *Storage
}

func (t *experimentProcessSuite) SetUpTest(c *C) {
	t.store = setupStore("experimentProcess")
}

func (t *experimentProcessSuite) TearDownSuite(c *C) {
	t.store.Close()
}

var experimentProcess = &types.ExperimentProcess{
	Name:      "experiment-1",
	Namespace: "namespace-1",
	BoxID:     1,
	BoxName:   "box-1",
	Creator:   "cwen",
	BoxIndex:  1,
	StartTime: time.Now().Format(util.TimeFormat),
	State:     types.ExperimentStateNormal,
	Stage:     types.ExperimentStagePending,
	Author:    "cwen",
	Config: &types.ExperimentConfig{
		Misc: &types.MiscConfig{
			SlackChannelForError:   "#tid-b",
			SlackChannelForSuccess: "#tid-b",
			DestroyTidbCluster:     true,
		},
	},
	Tests: make(map[string]*types.TestProcess),
	Meta: &types.Meta{
		Labels: map[string]string{
			"node": "bench",
			"idc":  "idc",
		},
	},
	Report: &types.ExperimentReport{},
}

func (t *experimentProcessSuite) TestExperimentProcess(c *C) {
	eps, _ := t.store.ListExperimentProcess(nil)
	c.Assert(len(eps), Equals, 0)

	ep := &types.ExperimentProcess{}
	err := util.Clone(ep, experimentProcess)
	c.Assert(err, Equals, nil)

	// test for CreateExperimentProcess
	err = t.store.CreateExperimentProcess(ep)
	if err != nil {
		log.Error(err)
	}
	c.Assert(err, Equals, nil)

	ep1, ok := t.store.GetExperimentProcess(ep.ID)
	c.Assert(ok, Equals, true)
	c.Assert(ep1, DeepEquals, ep)

	ep.Creator = "cwen0"
	ep.Stage = types.ExperimentStageRunning
	ep.Destroyed = true
	err = t.store.UpdateExperimentProcess(ep)
	if err != nil {
		log.Error(err)
	}
	c.Assert(err, Equals, nil)

	ep1, ok = t.store.GetExperimentProcess(ep.ID)
	c.Assert(ok, Equals, true)
	c.Assert(ep1, DeepEquals, ep)

	eps, _ = t.store.ListExperimentProcess(nil)
	c.Assert(len(eps), Equals, 1)

	time.Sleep(2 * time.Second)
	ep2 := &types.ExperimentProcess{}
	err = util.Clone(ep2, experimentProcess)
	c.Assert(err, Equals, nil)

	ep2.Name = "experiment-2"
	err = t.store.CreateExperimentProcess(ep2)
	if err != nil {
		log.Error(err)
	}
	c.Assert(err, Equals, nil)
	eps, _ = t.store.ListExperimentProcess(nil)
	c.Assert(len(eps), Equals, 2)

	ep2.Destroyed = false
	time.Sleep(1 * time.Second)
	err = t.store.UpdateExperimentProcess(ep2)
	if err != nil {
		log.Error(err)
	}
	c.Assert(err, Equals, nil)

	eps, _ = t.store.ListExperimentProcess(nil)
	c.Assert(len(eps), Equals, 2)
	c.Assert(eps[0], DeepEquals, ep2)
	c.Assert(eps[1], DeepEquals, ep)
}

func (t *experimentProcessSuite) TestGenExperimentSelectStmt(c *C) {
	got := genExperimentSelectStmt(nil)
	expect := stmtExperimentQueryBase + `WHERE 1 = 1
ORDER BY create_time DESC
`
	c.Assert(expect, Equals, got)

	got = genExperimentSelectStmt(&ExperimentProcessConditions{})
	c.Assert(expect, Equals, got)

	got = genExperimentSelectStmt(&ExperimentProcessConditions{Box: []int64{1, 2, 3}})
	expect = stmtExperimentQueryBase + `WHERE 1 = 1 AND box IN ('1', '2', '3')
ORDER BY create_time DESC
`
	c.Assert(expect, Equals, got)

	got = genExperimentSelectStmt(&ExperimentProcessConditions{Destroyed: []string{"1", "0"}, Box: []int64{1, 2, 3}, Limit: 10, Offset: 2})
	expect = stmtExperimentQueryBase + `WHERE 1 = 1 AND box IN ('1', '2', '3') AND destroyed IN ('1', '0')
ORDER BY create_time DESC
LIMIT 10 OFFSET 2
`
	c.Assert(expect, Equals, got)

	got = genExperimentSelectStmt(&ExperimentProcessConditions{Destroyed: []string{"1", "0"}, Box: []int64{1, 2, 3}, LimitEveryBox: 10})
	expect = stmtExperimentQueryBase + `WHERE 1 = 1 AND box IN ('1', '2', '3') AND destroyed IN ('1', '0') AND (SELECT COUNT(*) FROM experiment AS f WHERE f.box = experiment.box AND f.create_time >= experiment.create_time) <= 10
ORDER BY create_time DESC
`
	c.Assert(expect, Equals, got)
}

// TestListExperimentProcess
func (t *experimentProcessSuite) TestListExperimentProcess(c *C) {
	err := t.store.CreateBox(insertBox)
	c.Assert(err, Equals, nil)

	insertExperimentProcess.BoxID = insertBox.ID
	err = t.store.CreateExperimentProcess(&insertExperimentProcess)
	c.Assert(err, Equals, nil)

	err = t.store.CreateTestTemplate(&insertTestTemplate)
	c.Assert(err, Equals, nil)

	insertTestProcess.Template = &insertTestTemplate
	insertTestProcess.ExperimentID = insertExperimentProcess.ID
	err = t.store.CreateTestProcess(&insertTestProcess)
	c.Assert(err, Equals, nil)

	insertCatProcess.ExperimentName = insertExperimentProcess.Name
	err = t.store.CreateCatProcess(&insertCatProcess)
	c.Assert(err, Equals, nil)

	eps, _ := t.store.ListExperimentProcess(&ExperimentProcessConditions{
		LimitEveryBox: 10,
		Box:           []int64{insertBox.ID},
	})
	c.Assert(len(eps), Equals, 1)
	c.Assert(eps[0].Cat, NotNil)
	c.Assert(eps[0].Tests[insertTestProcess.Name], NotNil)
}

var insertExperimentProcess = types.ExperimentProcess{
	Meta: &types.Meta{},
	Name: "test-1",
}

var insertCatProcess = types.CatProcess{
	Meta: &types.Meta{},
	Config: types.TiDBClusterConfig{
		PDConfig: types.ComponentConfig{
			Size: 10,
			Version: types.VersionInfo{
				Name: "versionInfo",
			},
			ConfigTemplateName: "configTemplateName-1",
			LogLevel:           "warn",
		},
	},
	Name: "catProcess-1",
	RunVersion: types.RunVersion{
		TiKVVersion: types.VersionInfo{
			Name:      "tikv",
			Type:      "hash",
			Value:     "8001ee0e5a5a38ed0eb3d10f2282f82f820a936e",
			BuildTime: "2019-06-16 10:50",
		},
	},
}

var insertTestTemplate = types.TestTemplate{
	Meta: &types.Meta{},
	Name: "testTemplate-1",
}

var insertTestProcess = types.TestProcess{
	Meta: &types.Meta{},
	Name: "testProcess-1",
}
