package nodeconfigs

import (
	"encoding/json"
	"fmt"
	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
	"github.com/iwind/TeaGo/Tea"
	"github.com/iwind/TeaGo/logs"
	"github.com/iwind/TeaGo/maps"
	"io/ioutil"
	"strconv"
)

var sharedNodeConfig *NodeConfig = nil

// NodeConfig 边缘节点配置
type NodeConfig struct {
	Id                     int64                         `yaml:"id" json:"id"`
	NodeId                 string                        `yaml:"nodeId" json:"nodeId"`
	Secret                 string                        `yaml:"secret" json:"secret"`
	IsOn                   bool                          `yaml:"isOn" json:"isOn"`
	Servers                []*serverconfigs.ServerConfig `yaml:"servers" json:"servers"`
	SupportCNAME           bool                          `yaml:"supportCNAME" json:"supportCNAME"`
	Version                int64                         `yaml:"version" json:"version"`
	Name                   string                        `yaml:"name" json:"name"`
	MaxCPU                 int32                         `yaml:"maxCPU" json:"maxCPU"`
	RegionId               int64                         `yaml:"regionId" json:"regionId"`
	MaxCacheDiskCapacity   *shared.SizeCapacity          `yaml:"maxCacheDiskCapacity" json:"maxCacheDiskCapacity"`
	MaxCacheMemoryCapacity *shared.SizeCapacity          `yaml:"maxCacheMemoryCapacity" json:"maxCacheMemoryCapacity"`

	// 全局配置
	GlobalConfig *serverconfigs.GlobalConfig `yaml:"globalConfig" json:"globalConfig"` // 全局配置

	// 集群统一配置
	HTTPFirewallPolicies []*firewallconfigs.HTTPFirewallPolicy   `yaml:"httpFirewallPolicies" json:"httpFirewallPolicies"`
	HTTPCachePolicies    []*serverconfigs.HTTPCachePolicy        `yaml:"httpCachePolicies" json:"httpCachePolicies"`
	TOA                  *TOAConfig                              `yaml:"toa" json:"toa"`
	SystemServices       map[string]maps.Map                     `yaml:"systemServices" json:"systemServices"` // 系统服务配置 type => params
	FirewallActions      []*firewallconfigs.FirewallActionConfig `yaml:"firewallActions" json:"firewallActions"`
	TimeZone             string                                  `yaml:"timeZone" json:"timeZone"`

	MetricItems []*serverconfigs.MetricItemConfig `yaml:"metricItems" json:"metricItems"`

	paddedId string

	// firewall
	firewallPolicies []*firewallconfigs.HTTPFirewallPolicy

	// metrics
	hasHTTPConnectionMetrics bool

	// 源站集合
	originMap map[int64]*serverconfigs.OriginConfig
}

// SharedNodeConfig 取得当前节点配置单例
func SharedNodeConfig() (*NodeConfig, error) {
	shared.Locker.Lock()
	defer shared.Locker.Unlock()

	if sharedNodeConfig != nil {
		return sharedNodeConfig, nil
	}

	data, err := ioutil.ReadFile(Tea.ConfigFile("node.json"))
	if err != nil {
		return &NodeConfig{}, err
	}

	config := &NodeConfig{}
	err = json.Unmarshal(data, &config)
	if err != nil {
		return &NodeConfig{}, err
	}

	sharedNodeConfig = config
	return config, nil
}

// ResetNodeConfig 重置节点配置
func ResetNodeConfig(nodeConfig *NodeConfig) {
	shared.Locker.Lock()
	sharedNodeConfig = nodeConfig
	shared.Locker.Unlock()
}

// Init 初始化
func (this *NodeConfig) Init() error {
	this.paddedId = fmt.Sprintf("%08d", this.Id)

	// servers
	for _, server := range this.Servers {
		err := server.Init()
		if err != nil {
			// 这里不返回错误，而是继续往下，防止单个服务错误而影响其他服务
			logs.Println("[INIT]server '" + strconv.FormatInt(server.Id, 10) + "' init failed: " + err.Error())
		}
	}

	// global config
	if this.GlobalConfig != nil {
		err := this.GlobalConfig.Init()
		if err != nil {
			return err
		}
	}

	// cache policy
	if len(this.HTTPCachePolicies) > 0 {
		for _, policy := range this.HTTPCachePolicies {
			err := policy.Init()
			if err != nil {
				return err
			}
		}
	}

	// firewall policy
	if len(this.HTTPFirewallPolicies) > 0 {
		for _, policy := range this.HTTPFirewallPolicies {
			err := policy.Init()
			if err != nil {
				return err
			}
		}
	}

	// TOA
	if this.TOA != nil {
		err := this.TOA.Init()
		if err != nil {
			return err
		}
	}

	// 源站
	this.originMap = map[int64]*serverconfigs.OriginConfig{}

	// 查找FirewallPolicy
	this.firewallPolicies = []*firewallconfigs.HTTPFirewallPolicy{}
	for _, policy := range this.HTTPFirewallPolicies {
		if policy.IsOn {
			this.firewallPolicies = append(this.firewallPolicies, policy)
		}
	}
	for _, server := range this.Servers {
		if !server.IsOk() || !server.IsOn {
			continue
		}

		// WAF策略
		if server.HTTPFirewallPolicyId > 0 {
			for _, policy := range this.HTTPFirewallPolicies {
				if server.HTTPFirewallPolicyId == policy.Id {
					server.HTTPFirewallPolicy = policy
					break
				}
			}
		}

		// 缓存策略
		if server.HTTPCachePolicyId > 0 {
			for _, policy := range this.HTTPCachePolicies {
				if server.HTTPCachePolicyId == policy.Id {
					server.HTTPCachePolicy = policy
				}
			}
		}

		// 源站
		if server.ReverseProxyRef != nil && server.ReverseProxyRef.IsOn && server.ReverseProxy != nil && server.ReverseProxy.IsOn {
			for _, origin := range server.ReverseProxy.PrimaryOrigins {
				if origin.IsOn {
					this.originMap[origin.Id] = origin
				}
			}
			for _, origin := range server.ReverseProxy.BackupOrigins {
				if origin.IsOn {
					this.originMap[origin.Id] = origin
				}
			}
		}

		if server.Web != nil {
			this.lookupWeb(server, server.Web)
		}
	}

	// firewall actions
	for _, action := range this.FirewallActions {
		err := action.Init()
		if err != nil {
			return err
		}
	}

	// metric items
	this.hasHTTPConnectionMetrics = false
	for _, item := range this.MetricItems {
		err := item.Init()
		if err != nil {
			return err
		}
		if item.IsOn && item.HasHTTPConnectionValue() {
			this.hasHTTPConnectionMetrics = true
		}
	}

	return nil
}

// AvailableGroups 根据网络地址和协议分组
func (this *NodeConfig) AvailableGroups() []*serverconfigs.ServerAddressGroup {
	groupMapping := map[string]*serverconfigs.ServerAddressGroup{} // protocol://addr => Server Group
	for _, server := range this.Servers {
		if !server.IsOk() || !server.IsOn {
			continue
		}
		for _, addr := range server.FullAddresses() {
			group, ok := groupMapping[addr]
			if ok {
				group.Add(server)
			} else {
				group = serverconfigs.NewServerAddressGroup(addr)
				group.Add(server)
			}
			groupMapping[addr] = group
		}
	}
	result := []*serverconfigs.ServerAddressGroup{}
	for _, group := range groupMapping {
		result = append(result, group)
	}
	return result
}

// FindAllFirewallPolicies 获取所有的防火墙策略
func (this *NodeConfig) FindAllFirewallPolicies() []*firewallconfigs.HTTPFirewallPolicy {
	return this.firewallPolicies
}

// Save 写入到文件
func (this *NodeConfig) Save() error {
	shared.Locker.Lock()
	defer shared.Locker.Unlock()

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

	return ioutil.WriteFile(Tea.ConfigFile("node.json"), data, 0777)
}

// PaddedId 获取填充后的ID
func (this *NodeConfig) PaddedId() string {
	return this.paddedId
}

// HasHTTPConnectionMetrics 是否含有HTTP连接数的指标
func (this *NodeConfig) HasHTTPConnectionMetrics() bool {
	return this.hasHTTPConnectionMetrics
}

// FindOrigin 读取源站配置
func (this *NodeConfig) FindOrigin(originId int64) *serverconfigs.OriginConfig {
	if this.originMap == nil {
		return nil
	}
	config, ok := this.originMap[originId]
	if ok {
		return config
	}
	return nil
}

// 搜索WAF策略
func (this *NodeConfig) lookupWeb(server *serverconfigs.ServerConfig, web *serverconfigs.HTTPWebConfig) {
	if web == nil || !web.IsOn {
		return
	}
	if web.FirewallPolicy != nil && web.FirewallPolicy.IsOn {
		// 复用节点的拦截选项设置
		if web.FirewallPolicy.BlockOptions == nil && server.HTTPFirewallPolicy != nil && server.HTTPFirewallPolicy.BlockOptions != nil {
			web.FirewallPolicy.BlockOptions = server.HTTPFirewallPolicy.BlockOptions
			web.FirewallPolicy.Mode = server.HTTPFirewallPolicy.Mode
		}
		this.firewallPolicies = append(this.firewallPolicies, web.FirewallPolicy)
	}
	if len(web.Locations) > 0 {
		for _, location := range web.Locations {
			// 源站
			if location.IsOn && location.ReverseProxyRef != nil && location.ReverseProxyRef.IsOn && location.ReverseProxy != nil && location.ReverseProxy.IsOn {
				for _, origin := range location.ReverseProxy.PrimaryOrigins {
					if origin.IsOn {
						this.originMap[origin.Id] = origin
					}
				}
				for _, origin := range location.ReverseProxy.BackupOrigins {
					if origin.IsOn {
						this.originMap[origin.Id] = origin
					}
				}
			}

			// Web
			if location.Web != nil && location.Web.IsOn {
				this.lookupWeb(server, location.Web)
			}
		}
	}
}
