package storage

import (
	"encoding/json"
	"reflect"

	"github.com/juju/errors"
	. "github.com/pingcap/check"
	"github.com/pingcap/schrodinger/pkg/types"
	v1 "k8s.io/api/core/v1"
)

var _ = Suite(&appSuite{})

type appSuite struct {
	store *Storage
}

func (t *appSuite) SetUpTest(c *C) {
	t.store = setupStore("app")
}

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

func (t *appSuite) TestCreateAPPProcess(c *C) {
	if err := t.store.CreateAPPProcess(insertAPPProcess); err != nil {
		c.Fatalf("create app process fails: %s", errors.ErrorStack(err))
	}
	if insertAPPProcess.ID == 0 {
		c.Fatalf("expect non zeor ID, got %v", insertAPPProcess.ID)
	}

	got, exists := t.store.GetAPPProcessByID(insertAPPProcess.ID)
	c.Assert(exists, Equals, true)

	if !reflect.DeepEqual(insertAPPProcess, got) {
		d1, _ := json.Marshal(insertAPPProcess)
		d2, _ := json.Marshal(got)
		c.Fatalf("expect %v, got %v", string(d1), string(d2))
	}
}

func (t *appSuite) TestUpdateAPPProcess(c *C) {
	t.TestCreateAPPProcess(c)

	updateAPPProcess.ID = insertAPPProcess.ID
	updateAPPProcess.Ctime = insertAPPProcess.Ctime

	if err := t.store.UpdateAPPProcess(updateAPPProcess); err != nil {
		c.Fatalf("update app process fails: %s", errors.ErrorStack(err))
	}

	got, exists := t.store.GetAPPProcessByID(updateAPPProcess.ID)
	c.Assert(exists, Equals, true)

	if !reflect.DeepEqual(updateAPPProcess, got) {
		d1, _ := json.Marshal(updateAPPProcess)
		d2, _ := json.Marshal(got)
		c.Fatalf("expect %v, got %v", string(d1), string(d2))
	}
}

func (t *appSuite) TestGetAPPProcessByExperimentID(c *C) {
	t.TestCreateAPPProcess(c)

	got, exists := t.store.GetAPPProcessByExperimentID(insertAPPProcess.Config.ExperimentID)
	c.Assert(exists, Equals, true)

	if !reflect.DeepEqual(types.APPProcesses{insertAPPProcess}, got) {
		d1, _ := json.Marshal([]*types.APPProcess{insertAPPProcess})
		d2, _ := json.Marshal(got)

		c.Fatalf("expect %v, got %v", string(d1), string(d2))
	}
}

func (t *appSuite) TestGetAPPProcessByNamespaceAndName(c *C) {
	t.TestCreateAPPProcess(c)

	got, exists := t.store.GetAPPProcessByNamespaceAndName(insertAPPProcess.Namespace, insertAPPProcess.Name)
	c.Assert(exists, Equals, true)

	if !reflect.DeepEqual(insertAPPProcess, got) {
		d1, _ := json.Marshal(*insertAPPProcess)
		d2, _ := json.Marshal(*got)
		c.Fatalf("expect %v, got %v", d1, d2)
	}
}

var insertAPPProcess = &types.APPProcess{
	Name: "name",
	Config: &types.APPConfig{
		Type:         "type",
		Name:         "config name",
		Namespace:    "namespace",
		Version:      "version",
		Labels:       map[string]string{"Hello": "world"},
		TestID:       1,
		ExperimentID: 2,
		BoxID:        3,
		BinaryName:   "binary",
		URL:          "URL",
		Args:         "args",
		ResourceRequirement: &types.ResourceRequirement{
			CPU:          "2000",
			Memory:       "4G",
			StorageClass: "sas",
		},
		Host:         "host",
		ContainerIDs: nil,
		Port:         80,
		Envs: []v1.EnvVar{{Name: "envs name", Value: "envs value", ValueFrom: &v1.EnvVarSource{
			FieldRef: &v1.ObjectFieldSelector{APIVersion: "api version"},
		}}},
		Actions: []types.Action{{Type: "type", Data: []byte("hello, world")}},
		Image:   "image",
	},
	Report: &types.TestReport{
		Log: "log",
	},
	Status:    "status",
	Mark:      "mark",
	Namespace: "namespace",
}

var updateAPPProcess = &types.APPProcess{
	Name: "name 2",
	Config: &types.APPConfig{
		Type:         "type 2",
		Name:         "config name 2",
		Namespace:    "namespace 2",
		Version:      "version 2",
		Labels:       map[string]string{"Hello 2": "world 2"},
		TestID:       2,
		ExperimentID: 3,
		BoxID:        4,
		BinaryName:   "binary2 ",
		URL:          "URL 2",
		Args:         "args 2",
		ResourceRequirement: &types.ResourceRequirement{
			CPU:          "4000",
			Memory:       "8G",
			StorageClass: "sata",
		},
		Host:         "host2",
		ContainerIDs: []string{"127", "0.0.1"},
		Port:         8080,
		Envs: []v1.EnvVar{{Name: "envs name 2", Value: "envs value 2", ValueFrom: &v1.EnvVarSource{
			FieldRef: &v1.ObjectFieldSelector{APIVersion: "api version 2"},
		}}},
		Actions: []types.Action{{Type: "type 2", Data: []byte("hello, world 2")}},
		Image:   "image 2",
	},
	Report: &types.TestReport{
		Log: "log 2",
	},
	Status:    "status 2",
	Mark:      "mark 2",
	Namespace: "namespace 2",
}
