package aws

import (
	"context"
	"errors"

	"github.com/tidbcloud/observability/pkg/common/metrics"

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

const (
	vpcEndpointServiceClient = "vpc-endpoint-service-client"

	ErrNotFoundEndpointService                   = "not found aws endpoint service with given service id"
	errCreateVpcEndpointServiceFailed            = "create new vpc endpoint service failed"
	errAddVpcEndpointServicePermissionsFailed    = "add vpc endpoint service permission failed"
	errDeleteVpcEndpointServicePermissionsFailed = "delete vpc endpoint service permission failed"
	errGetVpcEndpointServiceFailed               = "get vpc endpoint service failed"
	errDeleteVpcEndpointServiceFailed            = "delete vpc endpoint service failed"
)

type EndpointServiceClient struct {
	cli endpointServiceClient
	cfg aws.Config
}

func NewEndpointService(cfg aws.Config) EndpointServiceClient {
	c := ec2api.NewFromConfig(cfg)
	return EndpointServiceClient{
		cli: c,
		cfg: cfg,
	}
}

// CreateEndpointService used to create a new EndpointService in the VPC
func (edpc EndpointServiceClient) CreateEndpointService(ctx context.Context, name string, lbarns []string) (*ec2api.CreateVpcEndpointServiceConfigurationOutput, error) {
	output, err := edpc.cli.CreateVpcEndpointServiceConfiguration(ctx, &ec2api.CreateVpcEndpointServiceConfigurationInput{
		ClientToken:             aws.String(name),
		AcceptanceRequired:      aws.Bool(false),
		NetworkLoadBalancerArns: lbarns,
	})
	if err != nil {
		metrics.HandleAWSAPIRequestMetrics(edpc.cfg.Region, vpcEndpointServiceClient, "CreateVpcEndpointServiceConfiguration", name, errCreateVpcEndpointServiceFailed)
	}
	return output, err
}

// AddUserPermissions used to modify the permission who can access endpoint service
func (edpc EndpointServiceClient) AddUserPermissions(ctx context.Context, serviceID string, userARNs []string) error {
	_, err := edpc.cli.ModifyVpcEndpointServicePermissions(ctx, &ec2api.ModifyVpcEndpointServicePermissionsInput{
		ServiceId:            aws.String(serviceID),
		AddAllowedPrincipals: userARNs,
	})
	if err != nil {
		metrics.HandleAWSAPIRequestMetrics(edpc.cfg.Region, vpcEndpointServiceClient, "ModifyVpcEndpointServicePermissions", serviceID, errAddVpcEndpointServicePermissionsFailed)
	}
	return err
}

// DeleteUserPermissions used to modify the permission who can access endpoint service
func (edpc EndpointServiceClient) DeleteUserPermissions(ctx context.Context, serviceID string, userARNs []string) error {
	_, err := edpc.cli.ModifyVpcEndpointServicePermissions(ctx, &ec2api.ModifyVpcEndpointServicePermissionsInput{
		ServiceId:               aws.String(serviceID),
		RemoveAllowedPrincipals: userARNs,
	})
	if err != nil {
		metrics.HandleAWSAPIRequestMetrics(edpc.cfg.Region, vpcEndpointServiceClient, "ModifyVpcEndpointServicePermissions", serviceID, errDeleteVpcEndpointServicePermissionsFailed)
	}
	return err
}

// GetEndpointService used to get endpoint service config
func (edpc EndpointServiceClient) GetEndpointService(ctx context.Context, serviceID string) (*types.ServiceConfiguration, error) {
	output, err := edpc.cli.DescribeVpcEndpointServiceConfigurations(ctx, &ec2api.DescribeVpcEndpointServiceConfigurationsInput{
		Filters: []types.Filter{
			{
				Name:   aws.String("service-id"),
				Values: []string{serviceID},
			},
		},
	})
	if err != nil {
		metrics.HandleAWSAPIRequestMetrics(edpc.cfg.Region, vpcEndpointServiceClient, "DescribeVpcEndpointServiceConfigurations", serviceID, errGetVpcEndpointServiceFailed)
		return nil, err
	}
	if len(output.ServiceConfigurations) < 1 {
		metrics.HandleAWSAPIRequestMetrics(edpc.cfg.Region, vpcEndpointServiceClient, "DescribeVpcEndpointServiceConfigurations", serviceID, ErrNotFoundEndpointService)
		return nil, errors.New(ErrNotFoundEndpointService)
	}
	return &output.ServiceConfigurations[0], err
}

// DeleteEndpointService used to delete a  EndpointService in the VPC
func (edpc EndpointServiceClient) DeleteEndpointService(ctx context.Context, serviceID string) error {
	_, err := edpc.cli.DeleteVpcEndpointServiceConfigurations(ctx, &ec2api.DeleteVpcEndpointServiceConfigurationsInput{
		ServiceIds: []string{serviceID},
	})
	if err != nil {
		metrics.HandleAWSAPIRequestMetrics(edpc.cfg.Region, vpcEndpointServiceClient, "DeleteVpcEndpointServiceConfigurations", serviceID, errDeleteVpcEndpointServiceFailed)
	}
	return err
}
