package vpc

import (
	"net/http"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/pingcap/log"
	regionalapiv1 "github.com/tidbcloud/infra-provider/api/tidbcloud/regional-server/v1"
	"github.com/tidbcloud/infra-provider/pkg/consts"
	bizerr "github.com/tidbcloud/infra-provider/regional-server/pkg/common/errors"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/common/message"
	rsconfig "github.com/tidbcloud/infra-provider/regional-server/pkg/config"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/resourcemanager/account"
	resappbox "github.com/tidbcloud/infra-provider/regional-server/pkg/resourcemanager/appbox"
	resapproute "github.com/tidbcloud/infra-provider/regional-server/pkg/resourcemanager/approute"
	resinternet "github.com/tidbcloud/infra-provider/regional-server/pkg/resourcemanager/internet"
	resnamespace "github.com/tidbcloud/infra-provider/regional-server/pkg/resourcemanager/namespace"
	resvpc "github.com/tidbcloud/infra-provider/regional-server/pkg/resourcemanager/vpc"
	resvpcpeering "github.com/tidbcloud/infra-provider/regional-server/pkg/resourcemanager/vpcpeering"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/router/interceptor"
	svcaccount "github.com/tidbcloud/infra-provider/regional-server/pkg/service/account"
	svcvpc "github.com/tidbcloud/infra-provider/regional-server/pkg/service/vpc"
	"google.golang.org/protobuf/types/known/emptypb"
	"sigs.k8s.io/controller-runtime/pkg/client"
)

type VPCHandler struct {
	vpcsvc *svcvpc.VPCService
	config rsconfig.RootConfig
}

func NewVPCHandler(cli client.Client, config rsconfig.RootConfig) *VPCHandler {
	return &VPCHandler{
		vpcsvc: svcvpc.NewVPCService(
			resvpc.NewVPCReaderWriter(cli),
			resapproute.NewAPPRouteReader(cli),
			resappbox.NewAPPBoxReaderWriter(cli),
			resvpcpeering.NewVPCPeeringReader(cli),
			resinternet.NewInternetReaderWriter(cli),
			svcaccount.NewService(account.NewAccountReaderWriter(cli, config), resvpc.NewVPCReader(cli), config),
			resnamespace.NewNamespaceReaderWriter(cli),
			config,
		),
		config: config,
	}
}

func (r *VPCHandler) CreateVPC(c *gin.Context) {
	req := &regionalapiv1.CreateVpcRequest{
		Vpc: new(regionalapiv1.Vpc),
	}
	if ok := interceptor.HandleJsonPbRequestFromBody(c, req); !ok {
		return
	}
	if err := (&message.CreateVPCReqValidatable{CreateVpcRequest: req}).Validate(); err != nil {
		interceptor.Fail(c, bizerr.New(bizerr.ParameterInvalid, err.Error()))
		return
	}
	resp, err := r.vpcsvc.CreateVPC(c, req)
	if err != nil {
		interceptor.Fail(c, err)
	} else {
		interceptor.SucceedPb(c, http.StatusAccepted, resp)
	}
}

func (r *VPCHandler) ListVPC(c *gin.Context) {
	tenantId := c.Query("tenantId")
	projectId := c.Query("projectId")
	clusterId := c.Query("clusterId")
	statuses := c.QueryArray("statuses")
	filters := make([]regionalapiv1.Vpc_Status, 0, len(statuses))
	for _, v := range statuses {
		filters = append(filters,
			regionalapiv1.Vpc_Status(regionalapiv1.Vpc_Status_value[strings.ToUpper(v)]))
	}
	req := &regionalapiv1.ListVpcsRequest{
		TenantId:  tenantId,
		ProjectId: projectId,
		ClusterId: clusterId,
		Statuses:  filters,
	}
	if err := (&message.ListVPCReqValidatable{ListVpcsRequest: req}).Validate(); err != nil {
		interceptor.Fail(c, bizerr.New(bizerr.ParameterInvalid, err.Error()))
		return
	}

	resp, err := r.vpcsvc.ListVPC(c, req)
	if err != nil {
		interceptor.Fail(c, err)
	} else {
		interceptor.SucceedPb(c, http.StatusOK, resp)
	}
}

func (r *VPCHandler) GetVPCClusterIds(c *gin.Context) {
	req := new(regionalapiv1.QueryVpcClusterIdsRequest)
	switch r.config.Provider {
	case consts.ProviderTypeEKS:
		req.Info = &regionalapiv1.QueryVpcClusterIdsRequest_Aws{
			Aws: &regionalapiv1.Vpc_AwsVpcInfo{
				VpcId:     c.Query("aws.vpcId"),
				AccountId: c.Query("aws.accountId"),
			},
		}
	case consts.ProviderTypeGKE:
		req.Info = &regionalapiv1.QueryVpcClusterIdsRequest_Gcp{
			Gcp: &regionalapiv1.Vpc_GcpVpcInfo{
				NetworkName: c.Query("gcp.networkName"),
				ProjectId:   c.Query("gcp.projectId"),
			},
		}
	default:
		log.Error("provider config invalid")
		interceptor.Fail(c, bizerr.New(bizerr.InternalServerError, "Server config invalid"))
		return
	}
	req.TenantId = c.Query("tenantId")
	req.ProjectId = c.Query("projectId")

	if err := (&message.QueryVpcClusterIdsRequestValidatable{QueryVpcClusterIdsRequest: req}).Validate(); err != nil {
		interceptor.Fail(c, bizerr.New(bizerr.ParameterInvalid, err.Error()))
		return
	}

	resp, err := r.vpcsvc.QueryClusterIds(c, req)
	if err != nil {
		interceptor.Fail(c, err)
	} else {
		interceptor.SucceedPb(c, http.StatusAccepted, resp)
	}
}

// DeleteVPC delete a vpc, just used for ops at present
func (r *VPCHandler) DeleteVPC(c *gin.Context) {
	req := &regionalapiv1.DeleteVpcRequest{
		VpcId: c.Param("vpcId"),
	}

	if err := (&message.DeleteVPCValidatable{DeleteVpcRequest: req}).Validate(); err != nil {
		interceptor.Fail(c, bizerr.New(bizerr.ParameterInvalid, "vpcId is empty"))
		return
	}
	err := r.vpcsvc.DeleteVPC(c, req)
	if err != nil {
		interceptor.Fail(c, err)
	} else {
		interceptor.SucceedPb(c, http.StatusOK, new(emptypb.Empty))
	}
}

func (r *VPCHandler) GetVPC(c *gin.Context) {
	req := &regionalapiv1.GetVpcRequest{
		VpcId: c.Param("vpcId"),
	}

	if err := (&message.GetVPCReqValidatable{GetVpcRequest: req}).Validate(); err != nil {
		interceptor.Fail(c, bizerr.New(bizerr.ParameterInvalid, err.Error()))
		return
	}

	resp, err := r.vpcsvc.GetVPC(c, req)
	if err != nil {
		interceptor.Fail(c, err)
	} else {
		interceptor.SucceedPb(c, http.StatusOK, resp)
	}
}

func (r *VPCHandler) AddSecondaryCidr(c *gin.Context) {
	vpcId := c.Param("vpcId")
	if vpcId == "" {
		interceptor.Fail(c, bizerr.New(bizerr.ParameterInvalid, "vpcId is empty"))
		return
	}
	req := &regionalapiv1.AddSecondaryCidrRequest{
		VpcId: vpcId,
	}

	if ok := interceptor.HandleJsonPbRequestFromBody(c, req); !ok {
		log.Error("failed to handle json pb request from body")
		return
	}

	if req.VpcId != vpcId {
		interceptor.Fail(c, bizerr.New(bizerr.ParameterInvalid, "vpcId mismatch"))
		return
	}

	if err := (&message.AddSecondaryCidrRequestValidatable{AddSecondaryCidrRequest: req}).Validate(); err != nil {
		interceptor.Fail(c, bizerr.New(bizerr.ParameterInvalid, err.Error()))
		return
	}

	resp, err := r.vpcsvc.AddSecondaryCidr(c, req)
	if err != nil {
		interceptor.Fail(c, err)
	} else {
		interceptor.SucceedPb(c, http.StatusOK, resp)
	}
}
