// Copyright (C) 2014-2018 Goodrain Co., Ltd.
// RAINBOND, Application Management Platform

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. For any non-GPL usage of Rainbond,
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
// must be obtained first.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

package model

import (
	//"github.com/sirupsen/logrus"
	"fmt"
	"io/ioutil"
	"net/http"
	url2 "net/url"
	"strings"

	"github.com/coreos/etcd/mvcc/mvccpb"
	"github.com/goodrain/rainbond/node/utils"
	"github.com/pquerna/ffjson/ffjson"
	v1 "k8s.io/api/core/v1" //"github.com/sirupsen/logrus"
)

//Resource 资源
type Resource struct {
	CPU  int `json:"cpu"`
	MemR int `json:"mem"`
}

// NodePodResource -
type NodePodResource struct {
	AllocatedResources `json:"allocatedresources"`
	Resource           `json:"allocatable"`
}

// AllocatedResources -
type AllocatedResources struct {
	CPURequests     int64
	CPULimits       int64
	MemoryRequests  int64
	MemoryLimits    int64
	MemoryRequestsR string
	MemoryLimitsR   string
	CPURequestsR    string
	CPULimitsR      string
}

// InitStatus -
type InitStatus struct {
	Status   int    `json:"status"`
	StatusCN string `json:"cn"`
	HostID   string `json:"uuid"`
}

// InstallStatus -
type InstallStatus struct {
	Status   int           `json:"status"`
	StatusCN string        `json:"cn"`
	Tasks    []*ExecedTask `json:"tasks"`
}

// ExecedTask -
type ExecedTask struct {
	ID             string   `json:"id"`
	Seq            int      `json:"seq"`
	Desc           string   `json:"desc"`
	Status         string   `json:"status"`
	CompleteStatus string   `json:"complete_status"`
	ErrorMsg       string   `json:"err_msg"`
	Depends        []string `json:"dep"`
	Next           []string `json:"next"`
}

// Prome -
type Prome struct {
	Status string    `json:"status"`
	Data   PromeData `json:"data"`
}

// PromeData -
type PromeData struct {
	ResultType string             `json:"resultType"`
	Result     []*PromeResultCore `json:"result"`
}

// PromeResultCore -
type PromeResultCore struct {
	Metric map[string]string `json:"metric"`
	Value  []interface{}     `json:"value"`
	Values []interface{}     `json:"values"`
}

// Expr swagger:parameters createToken
type Expr struct {
	Body struct {
		// expr
		// in: body
		// required: true
		Expr string `json:"expr" validate:"expr|required"`
	}
}

// LabelsResp -
type LabelsResp struct {
	SysLabels    map[string]string `json:"sys_labels"`
	CustomLabels map[string]string `json:"custom_labels"`
}

// PrometheusInterface -
type PrometheusInterface interface {
	Query(query string) *Prome
	QueryRange(query string, start, end, step string) *Prome
}

// PrometheusAPI -
type PrometheusAPI struct {
	API string
}

//Query Get
func (s *PrometheusAPI) Query(query string) (*Prome, *utils.APIHandleError) {
	resp, code, err := DoRequest(s.API, query, "query", "GET", nil)
	if err != nil {
		return nil, utils.CreateAPIHandleError(400, err)
	}
	if code == 422 {
		return nil, utils.CreateAPIHandleError(422, fmt.Errorf("unprocessable entity,expression %s can't be executed", query))
	}
	if code == 400 {
		return nil, utils.CreateAPIHandleError(400, fmt.Errorf("bad request,error to request query %s", query))
	}
	if code == 503 {
		return nil, utils.CreateAPIHandleError(503, fmt.Errorf("service unavailable"))
	}
	var prome Prome
	err = ffjson.Unmarshal(resp, &prome)
	if err != nil {
		return nil, utils.CreateAPIHandleError(500, err)
	}
	return &prome, nil
}

//QueryRange Get
func (s *PrometheusAPI) QueryRange(query string, start, end, step string) (*Prome, *utils.APIHandleError) {
	//logrus.Infof("prometheus api is %s",s.API)
	uri := fmt.Sprintf("%v&start=%v&end=%v&step=%v", query, start, end, step)
	resp, code, err := DoRequest(s.API, uri, "query_range", "GET", nil)
	if err != nil {
		return nil, utils.CreateAPIHandleError(400, err)
	}
	if code == 422 {
		return nil, utils.CreateAPIHandleError(422, fmt.Errorf("unprocessable entity,expression %s can't be executed", query))
	}
	if code == 400 {
		return nil, utils.CreateAPIHandleError(400, fmt.Errorf("bad request,error to request query %s", query))
	}
	if code == 503 {
		return nil, utils.CreateAPIHandleError(503, fmt.Errorf("service unavailable"))
	}
	var prome Prome
	err = ffjson.Unmarshal(resp, &prome)
	if err != nil {
		return nil, utils.CreateAPIHandleError(500, err)
	}
	return &prome, nil
}

// DoRequest -
func DoRequest(baseAPI, query, queryType, method string, body []byte) ([]byte, int, error) {
	api := baseAPI + "/api/v1/" + queryType + "?"
	query = "query=" + query
	query = strings.Replace(query, "+", "%2B", -1)
	val, err := url2.ParseQuery(query)
	if err != nil {
		return nil, 0, err
	}
	encoded := val.Encode()
	//logrus.Infof("uri is %s",api+encoded)
	request, err := http.NewRequest(method, api+encoded, nil)
	if err != nil {
		return nil, 0, err
	}
	resp, err := http.DefaultClient.Do(request)
	if err != nil {
		return nil, 0, err
	}

	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, 0, err
	}
	defer resp.Body.Close()
	return data, resp.StatusCode, nil
}

//ClusterResource 资源
type ClusterResource struct {
	AllNode                          int           `json:"all_node"`
	NotReadyNode                     int           `json:"notready_node"`
	ComputeNode                      int           `json:"compute_node"`
	Tenant                           int           `json:"tenant"`
	CapCPU                           int           `json:"cap_cpu"`          //可分配CPU总额
	CapMem                           int           `json:"cap_mem"`          //可分配Mem总额
	HealthCapCPU                     int           `json:"health_cap_cpu"`   //健康可分配CPU
	HealthCapMem                     int           `json:"health_cap_mem"`   //健康可分配Mem
	UnhealthCapCPU                   int           `json:"unhealth_cap_cpu"` //不健康可分配CPU
	UnhealthCapMem                   int           `json:"unhealth_cap_mem"` //不健康可分配Mem
	ReqCPU                           float32       `json:"req_cpu"`          //已使用CPU总额
	ReqMem                           int           `json:"req_mem"`          //已使用Mem总额
	HealthReqCPU                     float32       `json:"health_req_cpu"`   //健康已使用CPU
	HealthReqMem                     int           `json:"health_req_mem"`   //健康已使用Mem
	UnhealthReqCPU                   float32       `json:"unhealth_req_cpu"` //不健康已使用CPU
	UnhealthReqMem                   int           `json:"unhealth_req_mem"` //不健康已使用Mem
	CapDisk                          uint64        `json:"cap_disk"`
	ReqDisk                          uint64        `json:"req_disk"`
	MaxAllocatableMemoryNodeResource *NodeResource `json:"max_allocatable_memory_node_resource"`
}

//NodeResourceResponse 资源
type NodeResourceResponse struct {
	CapCPU int     `json:"cap_cpu"`
	CapMem int     `json:"cap_mem"`
	ReqCPU float32 `json:"req_cpu"`
	ReqMem int     `json:"req_mem"`
}

// FirstConfig -
type FirstConfig struct {
	StorageMode     string `json:"storage_mode"`
	StorageHost     string `json:"storage_host,omitempty"`
	StorageEndPoint string `json:"storage_endpoint,omitempty"`

	NetworkMode string `json:"network_mode"`
	ZKHosts     string `json:"zk_host,omitempty"`
	CassandraIP string `json:"cassandra_ip,omitempty"`
	K8SAPIAddr  string `json:"k8s_apiserver,omitempty"`
	MasterIP    string `json:"master_ip,omitempty"`
	DNS         string `json:"dns,omitempty"`
	ZMQSub      string `json:"zmq_sub,omitempty"`
	ZMQTo       string `json:"zmq_to,omitempty"`
	EtcdIP      string `json:"etcd_ip,omitempty"`
}

// Config -
type Config struct {
	Cn    string `json:"cn_name"`
	Name  string `json:"name"`
	Value string `json:"value"`
}

//ConfigUnit 一个配置单元
type ConfigUnit struct {
	//配置名称 例如:network
	Name   string `json:"name" validate:"name|required"`
	CNName string `json:"cn_name" validate:"cn_name"`
	//类型 例如:midonet
	Value     interface{} `json:"value" validate:"value|required"`
	ValueType string      `json:"value_type"`
	//可选类型 类型名称和需要的配置项
	OptionalValue []string                `json:"optional_value,omitempty"`
	DependConfig  map[string][]ConfigUnit `json:"depend_config,omitempty"`
	//是否用户可配置
	IsConfigurable bool `json:"is_configurable"`
}

func (c ConfigUnit) String() string {
	res, _ := ffjson.Marshal(&c)
	return string(res)
}

//GlobalConfig 全局配置
type GlobalConfig struct {
	Configs map[string]*ConfigUnit `json:"configs"`
}

//String String
func (g *GlobalConfig) String() string {
	res, _ := ffjson.Marshal(g)
	return string(res)
}

//Add 添加配置
func (g *GlobalConfig) Add(c ConfigUnit) {
	//具有依赖配置
	if c.DependConfig != nil || len(c.DependConfig) > 0 {
		if c.ValueType == "string" || c.ValueType == "" {
			if value, ok := c.Value.(string); ok {
				for _, dc := range c.DependConfig[value] {
					g.Add(dc)
				}
			}
		}
	}
	g.Configs[c.Name] = &c
}

//Get 获取配置
func (g *GlobalConfig) Get(name string) *ConfigUnit {
	return g.Configs[name]
}

//Delete 删除配置
func (g *GlobalConfig) Delete(Name string) {
	if _, ok := g.Configs[Name]; ok {
		delete(g.Configs, Name)
	}
}

//Bytes Bytes
func (g GlobalConfig) Bytes() []byte {
	res, _ := ffjson.Marshal(&g)
	return res
}

//CreateDefaultGlobalConfig 生成默认配置
func CreateDefaultGlobalConfig() *GlobalConfig {
	gconfig := &GlobalConfig{
		Configs: make(map[string]*ConfigUnit),
	}
	gconfig.Add(ConfigUnit{
		Name:      "NETWORK_MODE",
		CNName:    "集群网络模式",
		Value:     "calico",
		ValueType: "string",
		DependConfig: map[string][]ConfigUnit{
			"calico": []ConfigUnit{ConfigUnit{Name: "ETCD_ADDRS", CNName: "ETCD地址", ValueType: "array"}},
			"midonet": []ConfigUnit{
				ConfigUnit{Name: "CASSANDRA_ADDRS", CNName: "CASSANDRA地址", ValueType: "array"},
				ConfigUnit{Name: "ZOOKEEPER_ADDRS", CNName: "ZOOKEEPER地址", ValueType: "array"},
				ConfigUnit{Name: "LB_CIDR", CNName: "负载均衡所在网段", ValueType: "string"},
			}},
		IsConfigurable: true,
	})
	gconfig.Add(ConfigUnit{
		Name:   "STORAGE_MODE",
		Value:  "nfs",
		CNName: "默认共享存储模式",
		DependConfig: map[string][]ConfigUnit{
			"nfs": []ConfigUnit{
				ConfigUnit{Name: "NFS_SERVERS", CNName: "NFS服务端地址列表", ValueType: "array"},
				ConfigUnit{Name: "NFS_ENDPOINT", CNName: "NFS挂载端点", ValueType: "string"},
			},
			"clusterfs": []ConfigUnit{},
		},
		IsConfigurable: true,
	})
	gconfig.Add(ConfigUnit{
		Name:          "DB_MODE",
		Value:         "mysql",
		CNName:        "管理节点数据库类型",
		OptionalValue: []string{"mysql", "cockroachdb"},
		DependConfig: map[string][]ConfigUnit{
			"mysql": []ConfigUnit{
				ConfigUnit{Name: "MYSQL_HOST", CNName: "Mysql数据库地址", ValueType: "string", Value: "127.0.0.1"},
				ConfigUnit{Name: "MYSQL_PASS", CNName: "Mysql数据库密码", ValueType: "string", Value: ""},
				ConfigUnit{Name: "MYSQL_USER", CNName: "Mysql数据库用户名", ValueType: "string", Value: ""},
			},
			"cockroachdb": []ConfigUnit{
				ConfigUnit{Name: "COCKROACH_HOST", CNName: "Mysql数据库地址", ValueType: "array"},
				ConfigUnit{Name: "COCKROACH_PASS", CNName: "Mysql数据库密码", ValueType: "string"},
				ConfigUnit{Name: "COCKROACH_USER", CNName: "Mysql数据库用户名", ValueType: "string"},
			},
		},
		IsConfigurable: true,
	})
	gconfig.Add(ConfigUnit{
		Name:           "LB_MODE",
		Value:          "nginx",
		ValueType:      "string",
		CNName:         "边缘负载均衡",
		OptionalValue:  []string{"nginx", "zeus"},
		IsConfigurable: true,
	})
	gconfig.Add(ConfigUnit{Name: "DOMAIN", CNName: "应用域名", ValueType: "string"})
	gconfig.Add(ConfigUnit{Name: "INSTALL_NODE", CNName: "安装节点", ValueType: "array"})
	gconfig.Add(ConfigUnit{
		Name:           "INSTALL_MODE",
		Value:          "online",
		ValueType:      "string",
		CNName:         "安装模式",
		OptionalValue:  []string{"online", "offine"},
		IsConfigurable: true,
	})
	gconfig.Add(ConfigUnit{
		Name:      "DNS_SERVER",
		Value:     []string{},
		CNName:    "集群DNS服务",
		ValueType: "array",
	})
	gconfig.Add(ConfigUnit{
		Name:      "KUBE_API",
		Value:     []string{},
		ValueType: "array",
		CNName:    "KubernetesAPI服务",
	})
	gconfig.Add(ConfigUnit{
		Name:      "MANAGE_NODE_ADDRESS",
		Value:     []string{},
		ValueType: "array",
		CNName:    "管理节点",
	})
	return gconfig
}

//CreateGlobalConfig 生成配置
func CreateGlobalConfig(kvs []*mvccpb.KeyValue) (*GlobalConfig, error) {
	dgc := &GlobalConfig{
		Configs: make(map[string]*ConfigUnit),
	}
	for _, kv := range kvs {
		var cn ConfigUnit
		if err := ffjson.Unmarshal(kv.Value, &cn); err == nil {
			dgc.Add(cn)
		}
	}
	return dgc, nil
}

// LoginResult -
type LoginResult struct {
	HostPort  string `json:"hostport"`
	LoginType bool   `json:"type"`
	Result    string `json:"result"`
}

// Login -
type Login struct {
	HostPort  string `json:"hostport"`
	LoginType bool   `json:"type"`
	HostType  string `json:"hosttype"`
	RootPwd   string `json:"pwd,omitempty"`
}

// Body -
type Body struct {
	List interface{} `json:"list"`
	Bean interface{} `json:"bean,omitempty"`
}

// ResponseBody -
type ResponseBody struct {
	Code  int    `json:"code"`
	Msg   string `json:"msg"`
	MsgCN string `json:"msgcn"`
	Body  Body   `json:"body,omitempty"`
}

// Pods -
type Pods struct {
	Namespace       string `json:"namespace"`
	Id              string `json:"id"`
	Name            string `json:"name"`
	TenantName      string `json:"tenant_name"`
	CPURequests     string `json:"cpurequest"`
	CPURequestsR    string `json:"cpurequestr"`
	CPULimits       string `json:"cpulimits"`
	CPULimitsR      string `json:"cpulimitsr"`
	MemoryRequests  string `json:"memoryrequests"`
	MemoryRequestsR string `json:"memoryrequestsr"`
	MemoryLimits    string `json:"memorylimits"`
	MemoryLimitsR   string `json:"memorylimitsr"`
	Status          string `json:"status"`
}

//NodeDetails NodeDetails
type NodeDetails struct {
	Name               string              `json:"name"`
	Role               []string            `json:"role"`
	Status             string              `json:"status"`
	Labels             map[string]string   `json:"labels"`
	Annotations        map[string]string   `json:"annotations"`
	CreationTimestamp  string              `json:"creationtimestamp"`
	Conditions         []v1.NodeCondition  `json:"conditions"`
	Addresses          map[string]string   `json:"addresses"`
	Capacity           map[string]string   `json:"capacity"`
	Allocatable        map[string]string   `json:"allocatable"`
	SystemInfo         v1.NodeSystemInfo   `json:"systeminfo"`
	NonterminatedPods  []*Pods             `json:"nonterminatedpods"`
	AllocatedResources map[string]string   `json:"allocatedresources"`
	Events             map[string][]string `json:"events"`
}

// AlertingRulesConfig -
type AlertingRulesConfig struct {
	Groups []*AlertingNameConfig `yaml:"groups" json:"groups"`
}

// AlertingNameConfig -
type AlertingNameConfig struct {
	Name  string         `yaml:"name" json:"name"`
	Rules []*RulesConfig `yaml:"rules" json:"rules"`
}

// RulesConfig -
type RulesConfig struct {
	Alert       string            `yaml:"alert" json:"alert"`
	Expr        string            `yaml:"expr" json:"expr"`
	For         string            `yaml:"for" json:"for"`
	Labels      map[string]string `yaml:"labels" json:"labels"`
	Annotations map[string]string `yaml:"annotations" json:"annotations"`
}

//NotificationEvent NotificationEvent
type NotificationEvent struct {
	//Kind could be service, tenant, cluster, node
	Kind string `json:"Kind"`
	//KindID could be service_id,tenant_id,cluster_id,node_id
	KindID string `json:"KindID"`
	Hash   string `json:"Hash"`
	//Type could be Normal UnNormal Notification
	Type          string `json:"Type"`
	Message       string `json:"Message"`
	Reason        string `json:"Reason"`
	Count         int    `json:"Count"`
	LastTime      string `json:"LastTime"`
	FirstTime     string `json:"FirstTime"`
	IsHandle      bool   `json:"IsHandle"`
	HandleMessage string `json:"HandleMessage"`
	ServiceName   string `json:"ServiceName"`
	TenantName    string `json:"TenantName"`
}
