// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of Cilium

package types

import (
	iputil "github.com/cilium/cilium/pkg/ip"
	ipamTypes "github.com/cilium/cilium/pkg/ipam/types"
)

// SecurityGroup is the representation of an AWS Security Group
type SecurityGroup struct {
	// ID is the SecurityGroup ID
	ID string

	// VpcID is the VPC ID in which the security group resides
	VpcID string

	// Tags are the tags of the security group
	Tags ipamTypes.Tags
}

// SecurityGroupMap indexes AWS Security Groups by security group ID
type SecurityGroupMap map[string]*SecurityGroup

// ENISpec is the ENI specification of a node. This specification is considered
// by the cilium-operator to act as an IPAM operator and makes ENI IPs available
// via the IPAMSpec section.
//
// The ENI specification can either be provided explicitly by the user or the
// cilium agent running on the node can be instructed to create the CiliumNode
// custom resource along with an ENI specification when the node registers
// itself to the Kubernetes cluster.
type ENISpec struct {
	// InstanceType is the AWS EC2 instance type, e.g. "m5.large"
	//
	// +kubebuilder:validation:Optional
	InstanceType string `json:"instance-type,omitempty"`

	// FirstInterfaceIndex is the index of the first ENI to use for IP
	// allocation, e.g. if the node has eth0, eth1, eth2 and
	// FirstInterfaceIndex is set to 1, then only eth1 and eth2 will be
	// used for IP allocation, eth0 will be ignored for PodIP allocation.
	//
	// +kubebuilder:validation:Minimum=0
	// +kubebuilder:validation:Optional
	FirstInterfaceIndex *int `json:"first-interface-index,omitempty"`

	// SecurityGroups is the list of security groups to attach to any ENI
	// that is created and attached to the instance.
	//
	// +kubebuilder:validation:Optional
	SecurityGroups []string `json:"security-groups,omitempty"`

	// SecurityGroupTags is the list of tags to use when evaliating what
	// AWS security groups to use for the ENI.
	//
	// +kubebuilder:validation:Optional
	SecurityGroupTags map[string]string `json:"security-group-tags,omitempty"`

	// SubnetIDs is the list of subnet ids to use when evaluating what AWS
	// subnets to use for ENI and IP allocation.
	//
	// +kubebuilder:validation:Optional
	SubnetIDs []string `json:"subnet-ids,omitempty"`

	// SubnetTags is the list of tags to use when evaluating what AWS
	// subnets to use for ENI and IP allocation.
	//
	// +kubebuilder:validation:Optional
	SubnetTags map[string]string `json:"subnet-tags,omitempty"`

	// NodeSubnetID is the subnet of the primary ENI the instance was brought up
	// with. It is used as a sensible default subnet to create ENIs in.
	//
	// +kubebuilder:validation:Optional
	NodeSubnetID string `json:"node-subnet-id,omitempty"`

	// VpcID is the VPC ID to use when allocating ENIs.
	//
	// +kubebuilder:validation:Optional
	VpcID string `json:"vpc-id,omitempty"`

	// AvailabilityZone is the availability zone to use when allocating
	// ENIs.
	//
	// +kubebuilder:validation:Optional
	AvailabilityZone string `json:"availability-zone,omitempty"`

	// ExcludeInterfaceTags is the list of tags to use when excluding ENIs for
	// Cilium IP allocation. Any interface matching this set of tags will not
	// be managed by Cilium.
	//
	// +kubebuilder:validation:Optional
	ExcludeInterfaceTags map[string]string `json:"exclude-interface-tags,omitempty"`

	// DeleteOnTermination defines that the ENI should be deleted when the
	// associated instance is terminated. If the parameter is not set the
	// default behavior is to delete the ENI on instance termination.
	//
	// +kubebuilder:validation:Optional
	DeleteOnTermination *bool `json:"delete-on-termination,omitempty"`

	// UsePrimaryAddress determines whether an ENI's primary address
	// should be available for allocations on the node
	//
	// +kubebuilder:validation:Optional
	UsePrimaryAddress *bool `json:"use-primary-address,omitempty"`

	// DisablePrefixDelegation determines whether ENI prefix delegation should be
	// disabled on this node.
	//
	// +kubebuilder:validation:Optional
	DisablePrefixDelegation *bool `json:"disable-prefix-delegation,omitempty"`
}

// ENI represents an AWS Elastic Network Interface
//
// More details:
// https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-eni.html
type ENI struct {
	// ID is the ENI ID
	//
	// +optional
	ID string `json:"id,omitempty"`

	// IP is the primary IP of the ENI
	//
	// +optional
	IP iputil.Addr `json:"ip,omitzero"`

	// MAC is the mac address of the ENI
	//
	// +optional
	MAC string `json:"mac,omitempty"`

	// AvailabilityZone is the availability zone of the ENI
	//
	// +optional
	AvailabilityZone string `json:"availability-zone,omitempty"`

	// Description is the description field of the ENI
	//
	// +optional
	Description string `json:"description,omitempty"`

	// Number is the interface index, it used in combination with
	// FirstInterfaceIndex
	//
	// +optional
	Number int `json:"number,omitempty"`

	// Subnet is the subnet the ENI is associated with
	//
	// +optional
	Subnet AwsSubnet `json:"subnet,omitempty"`

	// VPC is the VPC information to which the ENI is attached to
	//
	// +optional
	VPC AwsVPC `json:"vpc,omitempty"`

	// Addresses is the list of all secondary IPs associated with the ENI
	//
	// +optional
	Addresses []iputil.Addr `json:"addresses,omitempty"`

	// Prefixes is the list of all /28 prefixes associated with the ENI
	//
	// +optional
	Prefixes []iputil.Prefix `json:"prefixes,omitempty"`

	// SecurityGroups are the security groups associated with the ENI
	SecurityGroups []string `json:"security-groups,omitempty"`

	// Tags is the set of tags of the ENI. Used to detect ENIs which should
	// not be managed by Cilium
	//
	// +optional
	Tags map[string]string `json:"tags,omitempty"`

	// PublicIP is the public IP associated with the ENI
	//
	// +optional
	PublicIP iputil.Addr `json:"public-ip,omitzero"`
}

func (e *ENI) DeepCopyInterface() ipamTypes.Interface {
	return e.DeepCopy()
}

// InterfaceID returns the identifier of the interface
func (e *ENI) InterfaceID() string {
	return e.ID
}

// IsExcludedBySpec returns true if the ENI is excluded by the provided spec and
// therefore should not be managed by Cilium.
func (e *ENI) IsExcludedBySpec(spec ENISpec) bool {
	if spec.FirstInterfaceIndex != nil && e.Number < *spec.FirstInterfaceIndex {
		return true
	}

	if len(spec.ExcludeInterfaceTags) > 0 {
		if ipamTypes.Tags(e.Tags).Match(spec.ExcludeInterfaceTags) {
			return true
		}
	}

	return false
}

// ENIStatus is the status of ENI addressing of the node
type ENIStatus struct {
	// ENIs is the list of ENIs on the node
	//
	// +optional
	ENIs map[string]ENI `json:"enis,omitempty"`
}

// AwsSubnet stores information regarding an AWS subnet
type AwsSubnet struct {
	// ID is the ID of the subnet
	ID string `json:"id,omitempty"`

	// CIDR is the CIDR range associated with the subnet
	//
	// +optional
	CIDR iputil.Prefix `json:"cidr,omitzero"`
}

// AwsVPC stores information regarding an AWS VPC
type AwsVPC struct {
	/// ID is the ID of a VPC
	ID string `json:"id,omitempty"`

	// PrimaryCIDR is the primary CIDR of the VPC
	//
	// +optional
	PrimaryCIDR iputil.Prefix `json:"primary-cidr,omitzero"`

	// CIDRs is the list of CIDR ranges associated with the VPC
	//
	// +optional
	CIDRs []iputil.Prefix `json:"cidrs,omitempty"`
}
