package cluster

import (
	"encoding/json"
	"fmt"
	"strconv"
	"strings"

	"github.com/gin-gonic/gin"
	userapiv1 "github.com/tidbcloud/aisupport/packages/sdk-go/contracts/api/core-server/user/v1"

	"github.com/tidbcloud/devops/api"
	clusterapi "github.com/tidbcloud/devops/api/cluster"
	opent "github.com/tidbcloud/devops/entity/operation"
	ctrl "github.com/tidbcloud/devops/pkg/controller"
	"github.com/tidbcloud/devops/pkg/infra/clients/mgmthttpcli"
	"github.com/tidbcloud/devops/pkg/infra/consts"
	opsvc "github.com/tidbcloud/devops/pkg/service/operation"
	opsvcngoperation "github.com/tidbcloud/devops/pkg/service/operation/actions/ngoperation"
	opsvcins "github.com/tidbcloud/devops/pkg/service/operation/instance"
)

// ListVPCPeerings
//
//	@Id				ListVPCPeerings
//	@Description	list vpc peerings
//	@Tags			ClusterService
//	@Accept			json
//	@Produce		json
//	@Param			cluster_id	path		string	true	"cluster id"
//	@Success		200			{object}	clusterapi.ListVPCPeeringsResp
//	@Router			/api/v1/devops/clusters/{cluster_id}/vpc-peerings [get]
func (c *ClusterController) ListVPCPeerings(ctx *gin.Context) {
	clusterID, err := strconv.ParseUint(ctx.Param("cluster_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("cluster_id is not a int"), api.ErrCodeInvalidArgument)
		return
	}

	cluster, err := c.coreSvcCli.Mgmt.GetCluster(ctx, clusterID)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	ngCluster := cluster.GetNextGenCluster()
	vpcPeerings, err := c.mgmtHTTP.NextGen().ListVPCPeerings(ctx, strconv.FormatUint(clusterID, 10), ngCluster.GetDataplaneId())
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	ctrl.Success(ctx, clusterapi.BuildVPCPeerings(vpcPeerings))
}

// CreateVPCPeering
//
//	@Id				CreateVPCPeering
//	@Description	create vpc peering
//	@Tags			ClusterService
//	@Accept			json
//	@Produce		json
//	@Param			cluster_id	path		string							true	"cluster id"
//	@Param			req			body		clusterapi.CreateVPCPeeringReq	true	"CreateVPCPeeringReq"
//	@Success		200			{object}	clusterapi.CommonResp
//	@Router			/api/v1/devops/clusters/{cluster_id}/vpc-peerings [post]
func (c *ClusterController) CreateVPCPeering(ctx *gin.Context) {
	user, ok := ctx.Get(consts.CtxUserProfile)
	if !ok {
		ctrl.FailWithCode(ctx, fmt.Errorf("user profile not found"), api.ErrCodeUnauthenticated)
		return
	}
	userProfile := user.(*userapiv1.PrincipalProfile)

	clusterID, err := strconv.ParseUint(ctx.Param("cluster_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("cluster_id is not a int"), api.ErrCodeInvalidArgument)
		return
	}

	req := &clusterapi.CreateVPCPeeringReq{}
	err = ctx.ShouldBindJSON(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	opReq := mgmthttpcli.VPCPeering{
		DestinationCIDRBlock: req.CIDR,
	}
	if strings.ToLower(req.CloudProvider) == clusterapi.ProviderAWS {
		opReq.AWS = &mgmthttpcli.VPCPeeringInfo{
			DestinationAccountID: req.CloudAccountID,
			DestinationRegion:    req.Region,
			DestinationVPCID:     req.VPCID,
		}
	} else if strings.ToLower(req.CloudProvider) == clusterapi.ProviderAliCloud {
		opReq.Ali = &mgmthttpcli.VPCPeeringInfo{
			DestinationAccountID: req.CloudAccountID,
			DestinationRegion:    req.Region,
			DestinationVPCID:     req.VPCID,
		}
	}

	opReqData, err := json.Marshal(opReq)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("marshal operation request failed: %s", err), api.ErrCodeInternal)
		return
	}

	// redirect to operation service
	// NOTE: must match the operation definition.
	createOPparam := opsvc.CreateInstanceParam{
		Type:      opent.OperationTargetCluster,
		Operation: "CreateChangefeedVPCPeering",
		Reason:    req.Reason,
		Applicant: userProfile.UserEmail,
		Params: map[opent.ParamName]string{
			opent.ParamClusterID:               strconv.FormatUint(clusterID, 10),
			opent.ParamCreateVPCPeeringRequest: string(opReqData),
		},
		Actions: []opsvcins.CreateInstanceActionParam{
			{
				Name:        opsvcngoperation.ActionCreateChangefeedVPCPeering.Name,
				StartPolicy: opent.ActionStartPolicyImmediate,
			},
		},
	}
	insID, err := c.operationSvc.CreateInstance(ctx, createOPparam)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := &clusterapi.CommonResp{InstanceID: insID}
	ctrl.Success(ctx, resp)
}

// DeleteVPCPeering
//
//	@Id				DeleteVPCPeering
//	@Description	delete vpc peering
//	@Tags			ClusterService
//	@Accept			json
//	@Produce		json
//	@Param			cluster_id		path		string							true	"cluster id"
//	@Param			vpc_peering_id	path		string							true	"vpc peering id"
//	@Param			req				body		clusterapi.DeleteVPCPeeringReq	true	"DeleteVPCPeeringReq"
//	@Success		200				{object}	clusterapi.CommonResp
//	@Router			/api/v1/devops/clusters/{cluster_id}/vpc-peerings/{vpc_peering_id} [delete]
func (c *ClusterController) DeleteVPCPeering(ctx *gin.Context) {
	user, ok := ctx.Get(consts.CtxUserProfile)
	if !ok {
		ctrl.FailWithCode(ctx, fmt.Errorf("user profile not found"), api.ErrCodeUnauthenticated)
		return
	}
	userProfile := user.(*userapiv1.PrincipalProfile)

	clusterID, err := strconv.ParseUint(ctx.Param("cluster_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("cluster_id is not a int"), api.ErrCodeInvalidArgument)
		return
	}

	vpcPeeringID := ctx.Param("vpc_peering_id")

	req := &clusterapi.DeleteVPCPeeringReq{}
	err = ctx.ShouldBindJSON(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	if req.Reason == "" {
		ctrl.FailWithCode(ctx, fmt.Errorf("reason is empty"), api.ErrCodeInvalidArgument)
		return
	}

	// redirect to operation service
	// NOTE: must match the operation definition.
	createOPparam := opsvc.CreateInstanceParam{
		Type:      opent.OperationTargetCluster,
		Operation: "DeleteChangefeedVPCPeering",
		Reason:    req.Reason,
		Applicant: userProfile.UserEmail,
		Params: map[opent.ParamName]string{
			opent.ParamClusterID:    strconv.FormatUint(clusterID, 10),
			opent.ParamVPCPeeringID: vpcPeeringID,
		},
		Actions: []opsvcins.CreateInstanceActionParam{
			{
				Name:        opsvcngoperation.ActionDeleteChangefeedVPCPeering.Name,
				StartPolicy: opent.ActionStartPolicyImmediate,
			},
		},
	}
	insID, err := c.operationSvc.CreateInstance(ctx, createOPparam)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := &clusterapi.CommonResp{InstanceID: insID}
	ctrl.Success(ctx, resp)
}
