package constants

import (
	"encoding/binary"
	"fmt"
	"net"
)

const (
	// The major CIDR of DBaaS used
	DBaaSCidrRange = "10.240.0.0/12"

	// Regions of AWS and GCP
	// AWS endpoints pkg contains the constants of regions, but it is not all of them
	AWSUSEAST2      = "us-east-2"
	AWSUSEAST1      = "us-east-1"
	AWSUSWEST1      = "us-west-1"
	AWSUSWEST2      = "us-west-2"
	AWSAFSOUTH1     = "af-south-1"
	AWSAPEAST1      = "ap-east-1"
	AWSAPSOUTH1     = "ap-south-1"
	AWSAPNORTHEAST3 = "ap-northeast-3"
	AWSAPNORTHEAST2 = "ap-northeast-2"
	AWSAPSOUTHEAST1 = "ap-southeast-1"
	AWSAPSOUTHEAST2 = "ap-southeast-2"
	AWSAPNORTHEAST1 = "ap-northeast-1"
	AWSCACENTRAL1   = "ca-central-1"
	AWSEUCENTRAL1   = "eu-central-1"
	AWSEUWEST1      = "eu-west-1"
	AWSEUWEST2      = "eu-west-2"
	AWSEUSOUTH1     = "eu-south-1"
	AWSEUWEST3      = "eu-west-3"
	AWSEUNORTH1     = "eu-north-1"
	AWSMESOUTH1     = "me-south-1"
	AWSSAEAST1      = "sa-east-1"

	GCPASIAEAST1              = "asia-east1"
	GCPASIAEAST2              = "asia-east2"
	GCPASIANORTHEAST1         = "asia-northeast1"
	GCPASIANORTHEAST2         = "asia-northeast2"
	GCPASIANORTHEAST3         = "asia-northeast3"
	GCPASIASOUTH1             = "asia-south1"
	GCPASIASOUTHEAST1         = "asia-southeast1"
	GCPASIASOUTHEAST2         = "asia-southeast2"
	GCPAUSTRALIASOUTHEAST1    = "australia-southeast1"
	GCPEUROPECENTRAL2         = "europe-central2"
	GCPEUROPENORTH1           = "europe-north1"
	GCPEUROPEWEST1            = "europe-west1"
	GCPEUROPEWEST2            = "europe-west2"
	GCPEUROPEWEST3            = "europe-west3"
	GCPEUROPEWEST4            = "europe-west4"
	GCPEUROPEWEST6            = "europe-west6"
	GCPNORTHAMERICANORTHEAST1 = "northamerica-northeast1"
	GCPSOUTHAMERICAEAST1      = "southamerica-east1"
	GCPUSCENTRAL1             = "us-central1"
	GCPUSEAST1                = "us-east1"
	GCPUSEAST4                = "us-east4"
	GCPUSWEST1                = "us-west1"
	GCPUSWEST2                = "us-west2"
	GCPUSWEST3                = "us-west3"
	GCPUSWEST4                = "us-west4"
	AWSNetRangeMask           = 20
	AWSWorkMask               = 21
	AWSLBMask                 = 22
	GCPWorkMask               = 19
	GCPLBMask                 = 19

	RegionCIDRShiftBits       = 5
	MinAWSProjectCIDRMaskBits = 11
	MinGCPProjectCIDRMaskBits = 8
	MaxProjectCIDRMaskBits    = 17
	DefaultProjectCIDR        = "172.30.0.0/16"
)

var (
	CIDRRegionMap = map[string]string{
		AWSUSEAST2:                "10.240.0.0/18",
		AWSUSEAST1:                "10.240.64.0/18",
		AWSUSWEST1:                "10.240.128.0/18",
		AWSUSWEST2:                "10.240.192.0/18",
		AWSAFSOUTH1:               "10.241.0.0/18",
		AWSAPEAST1:                "10.241.64.0/18",
		AWSAPSOUTH1:               "10.241.128.0/18",
		AWSAPNORTHEAST3:           "10.241.192.0/18",
		AWSAPNORTHEAST2:           "10.242.0.0/18",
		AWSAPSOUTHEAST1:           "10.242.64.0/18",
		AWSAPSOUTHEAST2:           "10.242.128.0/18",
		AWSAPNORTHEAST1:           "10.242.192.0/18",
		AWSCACENTRAL1:             "10.243.0.0/18",
		AWSEUCENTRAL1:             "10.243.64.0/18",
		AWSEUWEST1:                "10.243.128.0/18",
		AWSEUWEST2:                "10.243.192.0/18",
		AWSEUSOUTH1:               "10.244.0.0/18",
		AWSEUWEST3:                "10.244.64.0/18",
		AWSEUNORTH1:               "10.244.128.0/18",
		AWSMESOUTH1:               "10.244.192.0/18",
		AWSSAEAST1:                "10.245.0.0/18",
		GCPASIAEAST1:              "10.240.0.0/18",
		GCPASIAEAST2:              "10.240.64.0/18",
		GCPASIANORTHEAST1:         "10.240.128.0/18",
		GCPASIANORTHEAST2:         "10.240.192.0/18",
		GCPASIANORTHEAST3:         "10.241.0.0/18",
		GCPASIASOUTH1:             "10.241.64.0/18",
		GCPASIASOUTHEAST1:         "10.241.128.0/18",
		GCPASIASOUTHEAST2:         "10.241.192.0/18",
		GCPAUSTRALIASOUTHEAST1:    "10.242.0.0/18",
		GCPEUROPECENTRAL2:         "10.242.64.0/18",
		GCPEUROPENORTH1:           "10.242.128.0/18",
		GCPEUROPEWEST1:            "10.242.192.0/18",
		GCPEUROPEWEST2:            "10.243.0.0/18",
		GCPEUROPEWEST3:            "10.243.64.0/18",
		GCPEUROPEWEST4:            "10.243.128.0/18",
		GCPEUROPEWEST6:            "10.243.192.0/18",
		GCPNORTHAMERICANORTHEAST1: "10.244.0.0/18",
		GCPSOUTHAMERICAEAST1:      "10.244.64.0/18",
		GCPUSCENTRAL1:             "10.244.128.0/18",
		GCPUSEAST1:                "10.244.192.0/18",
		GCPUSEAST4:                "10.245.0.0/18",
		GCPUSWEST1:                "10.245.64.0/18",
		GCPUSWEST2:                "10.245.128.0/18",
		GCPUSWEST3:                "10.245.192.0/18",
		GCPUSWEST4:                "10.246.0.0/18",
	}
)

func nextSubnet(Ip []byte, offset uint32) []byte {
	var (
		ipInt uint32
	)
	ret := make([]byte, 4)
	ipInt = binary.BigEndian.Uint32(Ip)
	binary.BigEndian.PutUint32(ret, ipInt+offset)
	return ret
}

// the AWS at most support 6 zones, so DBaaS need to reserved for them
func GetDefaultAWSWorkerCidr(region string) (string, error) {
	var (
		ipNet *net.IPNet
		ret   string
		err   error
		count int
		Ip    []byte
	)
	_, ipNet, err = net.ParseCIDR(CIDRRegionMap[region])
	if err != nil {
		return "", err
	}
	// need 3 worker cidr for 3 zones, join them by comma
	count = 0
	Ip = ipNet.IP
	for {
		ret += fmt.Sprintf("%s/%d", net.IP(Ip).String(), AWSWorkMask)
		if count == 2 {
			break
		}
		Ip = nextSubnet(Ip, uint32(1<<(32-AWSNetRangeMask)))
		ret += ","
		count += 1
	}
	return ret, nil
}

// the AWS need a public LB CIDR in each zone
func GetDefaultAWSPublicLBCidr(region string) (string, error) {
	var (
		ipNet *net.IPNet
		ret   string
		err   error
		count int
		Ip    []byte
	)
	_, ipNet, err = net.ParseCIDR(CIDRRegionMap[region])
	if err != nil {
		return "", err
	}
	// need 3 lb cidr for 3 zones, join them by comma
	count = 0
	Ip = nextSubnet([]byte(ipNet.IP), uint32(1<<(32-AWSWorkMask)))
	for {
		ret += fmt.Sprintf("%s/%d", net.IP(Ip).String(), AWSLBMask)
		if count == 2 {
			break
		}
		Ip = nextSubnet(Ip, uint32(1<<(32-AWSNetRangeMask)))
		ret += ","
		count += 1
	}
	return ret, nil
}

func GetDefaultAWSInternalLBCidr(region string) (string, error) {
	var (
		ipNet *net.IPNet
		ret   string
		err   error
		count int
		Ip    []byte
	)
	_, ipNet, err = net.ParseCIDR(CIDRRegionMap[region])
	if err != nil {
		return "", err
	}
	// need 3 Internal LB cidr for 3 zones, join them by comma
	count = 0
	Ip = nextSubnet([]byte(ipNet.IP), uint32(1<<(32-AWSWorkMask))+uint32(1<<(32-AWSLBMask)))
	for {
		ret += fmt.Sprintf("%s/%d", net.IP(Ip).String(), AWSLBMask)
		if count == 2 {
			break
		}
		Ip = nextSubnet(Ip, uint32(1<<(32-AWSNetRangeMask)))
		ret += ","
		count += 1
	}
	return ret, nil
}

func OverlapCidrRange(cidrRangeA, cidrRangeB *net.IPNet) bool {
	return cidrRangeA.Contains(cidrRangeB.IP) || cidrRangeB.Contains(cidrRangeA.IP)
}
