package ack

import (
	"context"
	"fmt"

	privatelink "github.com/alibabacloud-go/privatelink-20200415/v5/client"
	"github.com/alibabacloud-go/tea/tea"
	"github.com/tidbcloud/infra-provider/regional-server/api/v1alpha1"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/cloud/aliyun"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers/privatelinkservice/task"
)

const (
	vpcEndpointConnPending        = "Pending"
	vpcEndpointConnConnecting     = "Connecting"
	vpcEndpointConnConnected      = "Connected"
	vpcEndpointConnDisconnecting  = "Disconnecting"
	vpcEndpointConnDisconnected   = "Disconnected"
	vpcEndpointConnDeleting       = "Deleting"
	vpcEndpointConnServiceDeleted = "ServiceDeleted"
)

type Endpoint struct {
	*task.Context
	aliCli aliyun.Client
}

func NewEndpoint(c *task.Context, aliCli aliyun.Client) task.VpcEndpointProvider {
	return &Endpoint{Context: c, aliCli: aliCli}
}

func (e *Endpoint) GetAllVPCEndpointConns(ctx context.Context) ([]task.VpcEndpointConnection, error) {
	assumedRole := &aliyun.AssumedRole{
		RoleARN: fmt.Sprintf("acs:ram::%s:role/%s", e.VPC.Spec.AccountRef.AccountID, e.Config.DataPlaneRoleName),
	}
	cli, err := e.aliCli.PrivateLink(assumedRole, aliyun.WithRegion(e.VPC.Spec.Region))
	if err != nil {
		return nil, err
	}

	svcID := e.PrivateLinkService.Status.EndpointService.ServiceID
	req := &privatelink.ListVpcEndpointConnectionsRequest{
		ServiceId: tea.String(svcID),
	}
	resp, err := cli.ListVpcEndpointConnections(req)
	if err != nil {
		return nil, err
	}
	// when vpc endpoint service not exist, the ListVpcEndpointConnections API will not return error, instead return empty list
	conns := make([]task.VpcEndpointConnection, 0, len(resp.Body.Connections))
	for _, vpceConn := range resp.Body.Connections {
		conns = append(conns, *convertAlicloudVpceConnection(vpceConn))
	}
	return conns, nil
}

func (e *Endpoint) AcceptVPCEndpoints(ctx context.Context, acceptList []task.VpcEndpointConnection) error {
	assumedRole := &aliyun.AssumedRole{
		RoleARN: fmt.Sprintf("acs:ram::%s:role/%s", e.VPC.Spec.AccountRef.AccountID, e.Config.DataPlaneRoleName),
	}
	cli, err := e.aliCli.PrivateLink(assumedRole, aliyun.WithRegion(e.VPC.Spec.Region))
	if err != nil {
		return err
	}

	svcID := e.PrivateLinkService.Status.EndpointService.ServiceID
	for _, conn := range acceptList {
		req := &privatelink.EnableVpcEndpointConnectionRequest{
			ServiceId:  tea.String(svcID),
			EndpointId: tea.String(conn.EndpointID),
		}
		if _, err := cli.EnableVpcEndpointConnection(req); err != nil {
			return fmt.Errorf("accept endpoint %s error: %w", conn.EndpointID, err)
		}
	}
	return nil
}

func (e *Endpoint) RejectVPCEndpoints(ctx context.Context, rejectList []task.VpcEndpointConnection) error {
	assumedRole := &aliyun.AssumedRole{
		RoleARN: fmt.Sprintf("acs:ram::%s:role/%s", e.VPC.Spec.AccountRef.AccountID, e.Config.DataPlaneRoleName),
	}
	cli, err := e.aliCli.PrivateLink(assumedRole, aliyun.WithRegion(e.VPC.Spec.Region))
	if err != nil {
		return err
	}

	svcID := e.PrivateLinkService.Status.EndpointService.ServiceID
	for _, conn := range rejectList {
		req := &privatelink.DisableVpcEndpointConnectionRequest{
			ServiceId:  tea.String(svcID),
			EndpointId: tea.String(conn.EndpointID),
		}
		if _, err := cli.DisableVpcEndpointConnection(req); err != nil {
			return fmt.Errorf("reject endpoint %s error: %w", conn.EndpointID, err)
		}
	}
	return nil
}

func convertAlicloudVpceConnection(vpceConn *privatelink.ListVpcEndpointConnectionsResponseBodyConnections) *task.VpcEndpointConnection {
	conn := &task.VpcEndpointConnection{
		EndpointID: tea.StringValue(vpceConn.EndpointId),
	}
	state := tea.StringValue(vpceConn.ConnectionStatus)
	switch state {
	// The status of the not accepted connection is Disconnected
	case vpcEndpointConnPending, vpcEndpointConnDisconnecting, vpcEndpointConnDisconnected:
		conn.Phase = v1alpha1.ConnectionPhasePending
	case vpcEndpointConnConnecting, vpcEndpointConnConnected:
		conn.Phase = v1alpha1.ConnectionPhaseActive
	case vpcEndpointConnDeleting, vpcEndpointConnServiceDeleted:
		conn.Phase = v1alpha1.ConnectionPhaseFailed
		conn.Message = fmt.Sprintf("The VPC Endpoint '%s' is %s", conn.EndpointID, state)
	default:
		conn.Phase = v1alpha1.ConnectionPhaseFailed
		conn.Message = fmt.Sprintf("The VPC Endpoint '%s' is in unknown state %s", conn.EndpointID, state)
	}
	return conn
}
