//nolint:gocritic // Network lifecycle boundaries deliberately receive immutable activation contracts by value.
package byoc

import (
	"context"
	"fmt"

	awssdk "github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/service/ec2"
	ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
)

func (d *AWSDriver) applyNetworkAssets(ctx context.Context, input Input) (*StageResult, error) {
	vpcID, err := d.controlPlaneVPCID(ctx, input)
	if err != nil {
		return nil, err
	}
	groupName := endpointSecurityGroupName(input.Session)
	group, err := d.findEndpointSecurityGroup(ctx, input, vpcID, groupName)
	if err != nil {
		return nil, err
	}
	if group == nil {
		clients, clientErr := d.clients(ctx, input.ControlPlaneProfile, input.Region)
		if clientErr != nil {
			return nil, clientErr
		}
		output, err := clients.ec2.CreateSecurityGroup(ctx, &ec2.CreateSecurityGroupInput{
			GroupName:   awssdk.String(groupName),
			Description: awssdk.String("CP9 BYOC interface endpoint ingress"),
			VpcId:       awssdk.String(vpcID),
			TagSpecifications: []ec2types.TagSpecification{{
				ResourceType: ec2types.ResourceTypeSecurityGroup,
				Tags: []ec2types.Tag{
					{Key: awssdk.String("Name"), Value: awssdk.String(groupName)},
					{Key: awssdk.String("cp9-owner"), Value: awssdk.String(input.Owner)},
					{Key: awssdk.String("cp9-session"), Value: awssdk.String(input.Session)},
					{Key: awssdk.String("cp9-persona"), Value: awssdk.String("byoc")},
				},
			}},
		})
		if err != nil {
			return nil, fmt.Errorf("create control-plane BYOC endpoint security group: %w", err)
		}
		groupID := awssdk.ToString(output.GroupId)
		if groupID == "" {
			return nil, fmt.Errorf("create control-plane BYOC endpoint security group returned no group id")
		}
		group = &endpointSecurityGroup{GroupID: groupID, GroupName: groupName, VPCID: vpcID, Owned: true}
	}
	if !group.Owned {
		return nil, fmt.Errorf("existing BYOC endpoint security group %s is not owned by this session", group.GroupID)
	}
	if group.IngressCount > 0 && !group.ExactIngress {
		return nil, fmt.Errorf("BYOC endpoint security group %s has unexpected ingress rules", group.GroupID)
	}
	if !group.ExactIngress {
		clients, clientErr := d.clients(ctx, input.ControlPlaneProfile, input.Region)
		if clientErr != nil {
			return nil, clientErr
		}
		if _, err := clients.ec2.AuthorizeSecurityGroupIngress(ctx, &ec2.AuthorizeSecurityGroupIngressInput{
			GroupId: awssdk.String(group.GroupID),
			IpPermissions: []ec2types.IpPermission{{
				IpProtocol: awssdk.String("tcp"), FromPort: awssdk.Int32(443), ToPort: awssdk.Int32(443),
				UserIdGroupPairs: []ec2types.UserIdGroupPair{{GroupId: awssdk.String(input.RemoteSecurityGroupID)}},
			}},
		}); err != nil {
			return nil, fmt.Errorf("restrict BYOC endpoint security group ingress: %w", err)
		}
	}
	return &StageResult{Artifacts: map[string]string{
		"security_group_id":        group.GroupID,
		"security_group_name":      groupName,
		"vpc_id":                   vpcID,
		"source_security_group_id": input.RemoteSecurityGroupID,
	}}, nil
}

type endpointSecurityGroup struct {
	GroupID      string
	GroupName    string
	VPCID        string
	Owned        bool
	IngressCount int
	ExactIngress bool
}

func endpointSecurityGroupName(session string) string {
	return session + "-byoc-endpoint"
}

func (d *AWSDriver) controlPlaneVPCID(ctx context.Context, input Input) (string, error) {
	clients, err := d.clients(ctx, input.ControlPlaneProfile, input.Region)
	if err != nil {
		return "", fmt.Errorf("read control-plane remote subnet: %w", err)
	}
	response, err := clients.ec2.DescribeSubnets(ctx, &ec2.DescribeSubnetsInput{SubnetIds: []string{input.RemoteSubnetID}})
	if err != nil {
		return "", fmt.Errorf("read control-plane remote subnet: %w", err)
	}
	if len(response.Subnets) != 1 || awssdk.ToString(response.Subnets[0].VpcId) == "" {
		return "", fmt.Errorf("control-plane remote subnet %s has no unique VPC", input.RemoteSubnetID)
	}
	return awssdk.ToString(response.Subnets[0].VpcId), nil
}

//nolint:nilnil // A nil group with no error is the read-only representation of an absent optional asset.
func (d *AWSDriver) findEndpointSecurityGroup(ctx context.Context, input Input, vpcID, groupName string) (*endpointSecurityGroup, error) {
	clients, err := d.clients(ctx, input.ControlPlaneProfile, input.Region)
	if err != nil {
		return nil, fmt.Errorf("observe BYOC endpoint security group: %w", err)
	}
	response, err := clients.ec2.DescribeSecurityGroups(ctx, &ec2.DescribeSecurityGroupsInput{
		Filters: []ec2types.Filter{
			{Name: awssdk.String("group-name"), Values: []string{groupName}},
			{Name: awssdk.String("vpc-id"), Values: []string{vpcID}},
		},
	})
	if err != nil {
		return nil, fmt.Errorf("observe BYOC endpoint security group: %w", err)
	}
	if len(response.SecurityGroups) == 0 {
		return nil, nil
	}
	if len(response.SecurityGroups) != 1 {
		return nil, fmt.Errorf("multiple BYOC endpoint security groups named %s exist in VPC %s", groupName, vpcID)
	}
	group := response.SecurityGroups[0]
	tags := make(map[string]string, len(group.Tags))
	for _, tag := range group.Tags {
		tags[awssdk.ToString(tag.Key)] = awssdk.ToString(tag.Value)
	}
	result := &endpointSecurityGroup{
		GroupID:      awssdk.ToString(group.GroupId),
		GroupName:    awssdk.ToString(group.GroupName),
		VPCID:        awssdk.ToString(group.VpcId),
		Owned:        tags["cp9-session"] == input.Session && tags["cp9-owner"] == input.Owner && tags["cp9-persona"] == "byoc",
		IngressCount: len(group.IpPermissions),
	}
	if len(group.IpPermissions) == 1 {
		permission := group.IpPermissions[0]
		result.ExactIngress = awssdk.ToString(permission.IpProtocol) == "tcp" && awssdk.ToInt32(permission.FromPort) == 443 &&
			awssdk.ToInt32(permission.ToPort) == 443 && len(permission.UserIdGroupPairs) == 1 &&
			awssdk.ToString(permission.UserIdGroupPairs[0].GroupId) == input.RemoteSecurityGroupID &&
			len(permission.IpRanges) == 0 && len(permission.Ipv6Ranges) == 0
	}
	return result, nil
}

func (d *AWSDriver) revalidateNetworkAssets(ctx context.Context, input Input, record *StageRecord) (*StageResult, error) {
	if record == nil || record.Artifacts["security_group_id"] == "" {
		return nil, ErrNeedsReconcile
	}
	vpcID, err := d.controlPlaneVPCID(ctx, input)
	if err != nil {
		return nil, err
	}
	group, err := d.findEndpointSecurityGroup(ctx, input, vpcID, endpointSecurityGroupName(input.Session))
	if err != nil {
		return nil, err
	}
	if group == nil || group.GroupID != record.Artifacts["security_group_id"] || !group.Owned || !group.ExactIngress {
		return nil, ErrNeedsReconcile
	}
	return &StageResult{Artifacts: cloneArtifacts(record.Artifacts)}, nil
}
