package util

import (
	"errors"
	"fmt"
	"net"
	"slices"

	"github.com/sirupsen/logrus"
	"go.podman.io/common/libnetwork/types"
)

// GetBridgeInterfaceNames returns all bridge interface names
// already used by network configs.
func GetBridgeInterfaceNames(n NetUtil) []string {
	names := make([]string, 0, n.Len())
	n.ForEach(func(net types.Network) {
		if net.Driver == types.BridgeNetworkDriver {
			names = append(names, net.NetworkInterface)
		}
	})
	return names
}

// GetUsedNetworkNames returns all network names already used
// by network configs.
func GetUsedNetworkNames(n NetUtil) []string {
	names := make([]string, 0, n.Len())
	n.ForEach(func(net types.Network) {
		names = append(names, net.Name)
	})
	return names
}

// GetFreeDeviceName returns a free device name which can
// be used for new configs as name and bridge interface name.
// The base name is suffixed by a number.
func GetFreeDeviceName(n NetUtil) (string, error) {
	bridgeNames := GetBridgeInterfaceNames(n)
	netNames := GetUsedNetworkNames(n)
	liveInterfaces, err := GetLiveNetworkNames()
	if err != nil {
		return "", err
	}
	names := make([]string, 0, len(bridgeNames)+len(netNames)+len(liveInterfaces))
	names = append(names, bridgeNames...)
	names = append(names, netNames...)
	names = append(names, liveInterfaces...)
	// FIXME: Is a limit fine?
	// Start by 1, 0 is reserved for the default network
	for i := 1; i < 1000000; i++ {
		deviceName := fmt.Sprintf("%s%d", n.DefaultInterfaceName(), i)
		if !slices.Contains(names, deviceName) {
			logrus.Debugf("found free device name %s", deviceName)
			return deviceName, nil
		}
	}
	return "", errors.New("could not find free device name, to many iterations")
}

// GetUsedSubnets returns a list of all used subnets by network
// configs and interfaces on the host.
func GetUsedSubnets(n NetUtil) ([]*net.IPNet, error) {
	// first, load all used subnets from network configs
	subnets := make([]*net.IPNet, 0, n.Len())
	n.ForEach(func(n types.Network) {
		for i := range n.Subnets {
			subnets = append(subnets, &n.Subnets[i].Subnet.IPNet)
		}
	})
	// second, load networks from the current system
	liveSubnets, err := getLiveNetworkSubnets()
	if err != nil {
		return nil, err
	}
	return append(subnets, liveSubnets...), nil
}
