package gardener

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

	"github.com/pingcap/errors"
)

func getSubCIDR(cidr string, subBits int, index int) (string, error) {
	_, ipNet, err := net.ParseCIDR(cidr)
	if err != nil {
		return "", errors.AddStack(err)
	}

	ipInt := binary.BigEndian.Uint32(ipNet.IP)

	ones, _ := ipNet.Mask.Size()
	if ones+subBits > 32 {
		return "", fmt.Errorf("host bits is %d, but want to split by %d bits", 32-ones, subBits)
	}

	index <<= (32 - ones - subBits)

	data := make([]byte, 4)
	binary.BigEndian.PutUint32(data, ipInt|uint32(index))

	sub := fmt.Sprintf("%s/%d", net.IP(data).String(), ones+subBits)
	return sub, nil
}

func getGCPCIDR(cidr string, region string) (workers []string, internalLBs []string, err error) {
	// split by one bit, halve for worker and halve for internalLB.
	worker, err := getSubCIDR(cidr, 1, 0)
	if err != nil {
		return nil, nil, err
	}

	internalLB, err := getSubCIDR(cidr, 1, 1)
	if err != nil {
		return nil, nil, err
	}

	return []string{worker}, []string{internalLB}, nil
}

func GetAWSCIDR(cidr string, region string, zones int) (workers []string, publicLBs []string, internalLBs []string, err error) {
	for i := 0; i < zones; i++ {
		zoneCIDR, err := getSubCIDR(cidr, 2, i)
		if err != nil {
			return nil, nil, nil, err
		}

		// split by one bit, worker take halve
		workerCIDR, err := getSubCIDR(zoneCIDR, 1, 0)
		if err != nil {
			return nil, nil, nil, err
		}
		workers = append(workers, workerCIDR)

		left, err := getSubCIDR(zoneCIDR, 1, 1)
		if err != nil {
			return nil, nil, nil, err
		}

		// split the left part for public and internal LB
		public, err := getSubCIDR(left, 1, 0)
		if err != nil {
			return nil, nil, nil, err
		}
		publicLBs = append(publicLBs, public)

		internal, err := getSubCIDR(left, 1, 1)
		if err != nil {
			return nil, nil, nil, err
		}
		internalLBs = append(internalLBs, internal)
	}
	// The first 3 return values are the same type, so we use named result parameters and naked return
	return // nolint:nakedret
}

func GetSubCIDR(cidr string, subBits int, index int) (string, error) {
	return getSubCIDR(cidr, subBits, index)
}
