package serverconfigs

import (
	"strings"
	"sync"

	"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
)

type ServerAddressGroup struct {
	fullAddr string
	servers  []*ServerConfig

	// 域名和服务映射
	strictDomainMap map[string]map[string]*ServerConfig // domain[:2] => {domain => *ServerConfig}
	fuzzyDomainMap  map[string]*ServerConfig            // special domain => *ServerConfig

	cacheLocker       sync.RWMutex
	cacheDomainMap    map[string]map[string]*ServerConfig // domain[:2] => {domain => *ServerConfig}
	countCacheDomains int

	// 支持CNAME的服务
	cnameDomainMap map[string]map[string]*ServerConfig // domain[:2] => {domain => *ServerConfig}

	// 第一个TLS Server
	firstTLSServer *ServerConfig
}

func NewServerAddressGroup(fullAddr string) *ServerAddressGroup {
	return &ServerAddressGroup{
		fullAddr:        fullAddr,
		strictDomainMap: map[string]map[string]*ServerConfig{},
		fuzzyDomainMap:  map[string]*ServerConfig{},
		cacheDomainMap:  map[string]map[string]*ServerConfig{},
		cnameDomainMap:  map[string]map[string]*ServerConfig{},
	}
}

// Add 添加服务
func (this *ServerAddressGroup) Add(server *ServerConfig) {
	for _, serverName := range server.AllStrictNames() {
		var prefix = this.domainPrefix(serverName)
		domainsMap, ok := this.strictDomainMap[prefix]
		if ok {
			domainsMap[serverName] = server
		} else {
			this.strictDomainMap[prefix] = map[string]*ServerConfig{serverName: server}
		}

		// CNAME
		if server.SupportCNAME {
			cnameDomainsMap, ok := this.cnameDomainMap[prefix]
			if ok {
				cnameDomainsMap[serverName] = server
			} else {
				this.cnameDomainMap[prefix] = map[string]*ServerConfig{serverName: server}
			}
		}
	}
	for _, serverName := range server.AllFuzzyNames() {
		this.fuzzyDomainMap[serverName] = server
	}

	this.servers = append(this.servers, server)

	// 第一个TLS Server
	if this.firstTLSServer == nil && server.SSLPolicy() != nil && server.SSLPolicy().IsOn {
		this.firstTLSServer = server
	}
}

// FullAddr 获取完整的地址
func (this *ServerAddressGroup) FullAddr() string {
	return this.fullAddr
}

// Protocol 获取当前分组的协议
func (this *ServerAddressGroup) Protocol() Protocol {
	for _, p := range AllProtocols() {
		if strings.HasPrefix(this.fullAddr, p.String()+":") {
			return p
		}
	}
	return ProtocolHTTP
}

// Addr 获取当前分组的地址
func (this *ServerAddressGroup) Addr() string {
	protocol := this.Protocol()
	return strings.TrimPrefix(this.fullAddr, protocol.String()+"://")
}

// Servers 读取所有服务
func (this *ServerAddressGroup) Servers() []*ServerConfig {
	return this.servers
}

// IsHTTP 判断当前分组是否为HTTP
func (this *ServerAddressGroup) IsHTTP() bool {
	p := this.Protocol()
	return p == ProtocolHTTP || p == ProtocolHTTP4 || p == ProtocolHTTP6
}

// IsHTTPS 判断当前分组是否为HTTPS
func (this *ServerAddressGroup) IsHTTPS() bool {
	p := this.Protocol()
	return p == ProtocolHTTPS || p == ProtocolHTTPS4 || p == ProtocolHTTPS6
}

// IsTCP 判断当前分组是否为TCP
func (this *ServerAddressGroup) IsTCP() bool {
	p := this.Protocol()
	return p == ProtocolTCP || p == ProtocolTCP4 || p == ProtocolTCP6
}

// IsTLS 判断当前分组是否为TLS
func (this *ServerAddressGroup) IsTLS() bool {
	p := this.Protocol()
	return p == ProtocolTLS || p == ProtocolTLS4 || p == ProtocolTLS6
}

// IsUDP 判断当前分组是否为UDP
func (this *ServerAddressGroup) IsUDP() bool {
	p := this.Protocol()
	return p == ProtocolUDP
}

// FirstServer 获取第一个Server
func (this *ServerAddressGroup) FirstServer() *ServerConfig {
	if len(this.servers) > 0 {
		return this.servers[0]
	}
	return nil
}

// FirstTLSServer 获取第一个TLS Server
func (this *ServerAddressGroup) FirstTLSServer() *ServerConfig {
	return this.firstTLSServer
}

// MatchServerName 使用域名查找服务
func (this *ServerAddressGroup) MatchServerName(serverName string) *ServerConfig {
	var prefix = this.domainPrefix(serverName)

	// 试图从缓存中读取
	this.cacheLocker.RLock()
	if len(this.cacheDomainMap) > 0 {
		domainMap, ok := this.cacheDomainMap[prefix]
		if ok {
			server, ok := domainMap[serverName]
			if ok {
				this.cacheLocker.RUnlock()
				return server
			}
		}
	}
	this.cacheLocker.RUnlock()

	domainMap, ok := this.strictDomainMap[prefix]
	if ok {
		server, ok := domainMap[serverName]
		if ok {
			return server
		}
	}
	for pattern, server := range this.fuzzyDomainMap {
		if configutils.MatchDomain(pattern, serverName) {
			// 加入到缓存
			this.cacheLocker.Lock()

			// 限制缓存的最大尺寸，防止内存耗尽
			// TODO 清理20%比较老的数据
			if this.countCacheDomains < 1_000_000 {
				domainMap, ok := this.cacheDomainMap[prefix]
				if ok {
					domainMap[serverName] = server
				} else {
					this.cacheDomainMap[prefix] = map[string]*ServerConfig{serverName: server}
				}
				this.countCacheDomains++
			}
			this.cacheLocker.Unlock()

			return server
		}
	}
	return nil
}

// MatchServerCNAME 使用CNAME查找服务
func (this *ServerAddressGroup) MatchServerCNAME(serverName string) *ServerConfig {
	var prefix = this.domainPrefix(serverName)

	domainMap, ok := this.cnameDomainMap[prefix]
	if ok {
		server, ok := domainMap[serverName]
		if ok {
			return server
		}
	}

	return nil
}

func (this *ServerAddressGroup) domainPrefix(domain string) string {
	if len(domain) < 2 {
		return domain
	}
	return domain[:2]
}
