package client

import (
	"encoding/json"
	"fmt"

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

const (
	// PD is the kind pd nodes
	PD = "pd"

	// TiKV is the kind tikv nodes
	TiKV = "tikv"

	// TiDB is the kind tidb nodes
	TiDB = "tidb"
)

// Config defines the config of the client
type Config struct {
	Manager    *ManagerConfig  `json:"manager"`
	Self       *TestInfo       `json:"self"`
	Experiment *ExperimentInfo `json:"experiment"`
	Cat        *CatInfo        `json:"cat"`
	Tests      []*TestInfo     `json:"tests"`
	LogService *config.Log     `json:"log"`
}

// ManagerConfig is manager defines config
type ManagerConfig struct {
	PublicNode      string `json:"public-node"`
	AgentPort       int    `json:"agent-port"`
	TidbServicePort int    `json:"tidb-service-port"`
	PdServicePort   int    `json:"pd-service-port"`
	TikvServicePort int    `json:"tikv-service-port"`
	DataDir         string `json:"data-dr"`
	PdDataDir       string `json:"pd-data-dir"`
	TikvDataDir     string `json:"tikv-data-dir"`
}

// ExperimentInfo defines experiment info
type ExperimentInfo struct {
	ID                     int64              `json:"id"`
	BoxID                  int64              `json:"box_id"`
	Timeout                util.Duration      `json:"timeout"`
	Failpoints             []*types.Failpoint `json:"failpoints"`
	DaemonPort             int                `json:"deamon_port"`
	Name                   string             `json:"name"`
	Namespace              string             `json:"namespace"`
	SlackChannelForError   string             `json:"slack_channel_for_error"`
	SlackChannelForSuccess string             `json:"slack_channel_for_success"`
}

// CatInfo defines the information of cat
type CatInfo struct {
	ID             int64
	Namespace      string                  `json:"namespace"`
	LBService      string                  `json:"lb_service"`
	PdService      string                  `json:"pd_service"`
	PDs            []*PodInfo              `json:"pds"`
	TiKVs          []*PodInfo              `json:"tikvs"`
	TiDBs          []*PodInfo              `json:"tidbs"`
	PrometheusAddr string                  `json:"prometheus_addr"`
	Config         types.TiDBClusterConfig `json:"config"`
}

// TestInfo defines the infomation of a test
type TestInfo struct {
	Name   string          `json:"name"`
	ID     int64           `json:"id"`
	IP     string          `json:"ip"`
	HostIP string          `json:"host_ip"`
	State  types.TestState `json:"state"`
}

// PodInfo defines the information of pod
type PodInfo struct {
	Name        string `json:"name"`
	IP          string `json:"ip"`
	AgentPort   int    `json:"agent_port"`
	ContainerID string `json:"id"`
	// just compatible with old versions
	DataDir     string `json:"data_dir"`
	PdDataDir   string `json:"pd_data_dir"`
	TikvDataDir string `json:"tikv_data_dir"`
	NodeIP      string `json:"node_ip"`

	// tikv/tidb/pd port
	ServicePort int `json:"service_port"`
}

func (p *PodInfo) clone() *PodInfo {
	copy := &PodInfo{}
	util.Clone(copy, p)

	return copy
}

func getConfig(managerAddr string, testID int64) (*Config, error) {
	conf := &Config{}
	url := util.GenURL(fmt.Sprintf("%s/testConfig/%d", managerAddr, testID))
	Infof("get config url: %s", url)

	data, err := getAndExtract(url)
	if err != nil {
		return nil, errors.Trace(err)
	}

	if err := json.Unmarshal(data, conf); err != nil {
		return nil, errors.Trace(err)
	}

	return conf, nil
}
