package privatelinkservice

import (
	"net/http"

	"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/cloud/aws"
	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"
	resapproute "github.com/tidbcloud/infra-provider/regional-server/pkg/resourcemanager/approute"
	respls "github.com/tidbcloud/infra-provider/regional-server/pkg/resourcemanager/privatelinkservice"
	resvpc "github.com/tidbcloud/infra-provider/regional-server/pkg/resourcemanager/vpc"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/router/interceptor"
	serpls "github.com/tidbcloud/infra-provider/regional-server/pkg/service/privatelinkservice"
	"go.uber.org/zap"
	"google.golang.org/protobuf/types/known/emptypb"
	"sigs.k8s.io/controller-runtime/pkg/client"
)

type PrivateLinkServiceHandler struct {
	plssvc *serpls.PrivateLinkServiceService
}

func NewPrivateLinkServiceHandler(cli client.Client, config rsconfig.RootConfig) *PrivateLinkServiceHandler {
	return &PrivateLinkServiceHandler{
		plssvc: serpls.NewPrivateLinkServiceService(
			cli,
			respls.NewPrivateLinkServiceReaderWriter(cli),
			resapproute.NewAPPRouteReader(cli),
			resvpc.NewVPCReader(cli),
			config,
		),
	}
}

func (r *PrivateLinkServiceHandler) WithAWSClient(cli aws.Client) *PrivateLinkServiceHandler {
	r.plssvc.WithAWSClient(cli)
	return r
}

func (r *PrivateLinkServiceHandler) CreatePrivateLinkService(c *gin.Context) {
	req := &regionalapiv1.CreatePrivateLinkServiceRequest{
		PrivateLinkService: &regionalapiv1.PrivateLinkService{},
	}
	if ok := interceptor.HandleJsonPbRequestFromBody(c, req.PrivateLinkService); !ok {
		log.Error("failed to handle json pb request from body")
		return
	}
	if err := (&message.CreatePrivateLinkServiceReqValidatable{CreatePrivateLinkServiceRequest: req}).Validate(); err != nil {
		log.Error("invalid request", zap.Any("req", req), zap.Error(err))
		interceptor.Fail(c, bizerr.New(bizerr.ParameterInvalid, err.Error()))
		return
	}
	resp, err := r.plssvc.CreatePrivateLinkService(c, req)
	if err != nil {
		log.Error("failed to create private link service", zap.Any("req", req), zap.Error(err))
		interceptor.Fail(c, err)
		return
	} else {
		interceptor.SucceedPb(c, http.StatusAccepted, resp)
	}
}

func (r *PrivateLinkServiceHandler) GetPrivateLinkIPAddressType(c *gin.Context) {
	req := &regionalapiv1.GetPrivateLinkIPAddressTypeRequest{
		ClusterId:   c.Param("clusterId"),
		TidbGroupId: c.Param("tidbGroupId"),
	}
	resp, err := r.plssvc.GetPrivateLinkIPAddressType(c, req)
	if err != nil {
		log.Error("failed to get TiDB Group PrivateLink address type", zap.Any("req", req), zap.Error(err))
		interceptor.Fail(c, err)
		return
	}
	interceptor.SucceedPb(c, http.StatusOK, resp)
}

func (r *PrivateLinkServiceHandler) SetPrivateLinkIPAddressType(c *gin.Context) {
	req := &regionalapiv1.SetPrivateLinkIPAddressTypeRequest{}
	if ok := interceptor.HandleJsonPbRequestFromBody(c, req); !ok {
		log.Error("failed to handle json pb request from body")
		return
	}
	req.ClusterId = c.Param("clusterId")
	req.TidbGroupId = c.Param("tidbGroupId")
	if err := r.plssvc.SetPrivateLinkIPAddressType(c, req); err != nil {
		log.Error("failed to set TiDB Group PrivateLink address type", zap.Any("req", req), zap.Error(err))
		interceptor.Fail(c, err)
		return
	}
	interceptor.SucceedPb(c, http.StatusOK, &emptypb.Empty{})
}

func (r *PrivateLinkServiceHandler) ValidatePrivateLinkIPv4Only(c *gin.Context) {
	req := &regionalapiv1.GetPrivateLinkIPAddressTypeRequest{
		ClusterId:   c.Param("clusterId"),
		TidbGroupId: c.Param("tidbGroupId"),
	}
	if err := r.plssvc.ValidatePrivateLinkIPv4Only(c, req); err != nil {
		log.Error("TiDB Group PrivateLink cannot be changed to IPv4", zap.Any("req", req), zap.Error(err))
		interceptor.Fail(c, err)
		return
	}
	interceptor.SucceedPb(c, http.StatusOK, &emptypb.Empty{})
}

func (r *PrivateLinkServiceHandler) GetPrivateLinkService(c *gin.Context) {
	req := &regionalapiv1.GetPrivateLinkServiceRequest{
		ClusterId:            c.Param("clusterId"),
		PrivateLinkServiceId: c.Param("privateLinkServiceId"),
	}
	if err := (&message.GetPrivateLinkServiceReqValidatable{GetPrivateLinkServiceRequest: req}).Validate(); err != nil {
		log.Error("invalid request", zap.Any("req", req), zap.Error(err))
		interceptor.Fail(c, bizerr.New(bizerr.ParameterInvalid, err.Error()))
		return
	}
	resp, err := r.plssvc.GetPrivateLinkService(c, req)
	if err != nil {
		log.Error("failed to get private link service", zap.Any("req", req), zap.Error(err))
		interceptor.Fail(c, err)
		return
	} else {
		interceptor.SucceedPb(c, http.StatusOK, resp)
	}
}

func (r *PrivateLinkServiceHandler) ListPrivateLinkServices(c *gin.Context) {
	req := &regionalapiv1.ListPrivateLinkServicesRequest{
		ClusterId:   c.Param("clusterId"),
		TidbGroupId: c.Query("tidbGroupId"),
	}
	if err := (&message.ListPrivateLinkServiceReqValidatable{ListPrivateLinkServicesRequest: req}).Validate(); err != nil {
		log.Error("invalid request", zap.Any("req", req), zap.Error(err))
		interceptor.Fail(c, bizerr.New(bizerr.ParameterInvalid, err.Error()))
		return
	}
	resp, err := r.plssvc.ListPrivateLinkService(c, req)
	if err != nil {
		log.Error("failed to list private link services", zap.Any("req", req), zap.Error(err))
		interceptor.Fail(c, err)
		return
	} else {
		interceptor.SucceedPb(c, http.StatusOK, resp)
	}
}

func (r *PrivateLinkServiceHandler) DeletePrivateLinkService(c *gin.Context) {
	req := &regionalapiv1.DeletePrivateLinkServiceRequest{
		ClusterId:            c.Param("clusterId"),
		PrivateLinkServiceId: c.Param("privateLinkServiceId"),
	}
	if err := (&message.DeletePrivateLinkServiceReqValidatable{DeletePrivateLinkServiceRequest: req}).Validate(); err != nil {
		log.Error("invalid request", zap.Any("req", req), zap.Error(err))
		interceptor.Fail(c, bizerr.New(bizerr.ParameterInvalid, err.Error()))
		return
	}
	err := r.plssvc.DeletePrivateLinkService(c, req)
	if err != nil {
		log.Error("failed to delete private link service", zap.Any("req", req), zap.Error(err))
		interceptor.Fail(c, err)
		return
	} else {
		interceptor.SucceedPb(c, http.StatusOK, new(emptypb.Empty))
	}
}

func (r *PrivateLinkServiceHandler) UpdatePrivateLinkService(c *gin.Context) {
	req := &regionalapiv1.UpdatePrivateLinkServiceRequest{}
	if ok := interceptor.HandleJsonPbRequestFromBody(c, req); !ok {
		log.Error("failed to handle json pb request from body")
		return
	}
	req.ClusterId = c.Param("clusterId")
	req.PrivateLinkServiceId = c.Param("privateLinkServiceId")
	if err := (&message.UpdatePrivateLinkServiceReqValidatable{UpdatePrivateLinkServiceRequest: req}).Validate(); err != nil {
		log.Error("invalid request", zap.Any("req", req), zap.Error(err))
		interceptor.Fail(c, bizerr.New(bizerr.ParameterInvalid, err.Error()))
		return
	}
	resp, err := r.plssvc.UpdatePrivateLinkService(c, req)
	if err != nil {
		log.Error("failed to update private link service", zap.Any("req", req), zap.Error(err))
		interceptor.Fail(c, err)
		return
	} else {
		interceptor.SucceedPb(c, http.StatusAccepted, resp)
	}
}

func (r *PrivateLinkServiceHandler) RejectVpcEndpointConnections(c *gin.Context) {
	req := &regionalapiv1.RejectVpcEndpointConnectionsRequest{}
	if ok := interceptor.HandleJsonPbRequestFromBody(c, req); !ok {
		log.Error("failed to handle json pb request from body")
		return
	}
	req.ClusterId = c.Param("clusterId")
	req.PrivateLinkServiceId = c.Param("privateLinkServiceId")
	if err := (&message.RejectVpcEndpointConnectionsReqValidatable{RejectVpcEndpointConnectionsRequest: req}).Validate(); err != nil {
		log.Error("invalid request", zap.Any("req", req), zap.Error(err))
		interceptor.Fail(c, bizerr.New(bizerr.ParameterInvalid, err.Error()))
		return
	}
	err := r.plssvc.RejectVpcEndpointConnections(c, req)
	if err != nil {
		log.Error("failed to reject vpc endpoint connections", zap.Any("req", req), zap.Error(err))
		interceptor.Fail(c, err)
		return
	} else {
		interceptor.SucceedPb(c, http.StatusOK, new(emptypb.Empty))
	}
}

func (r *PrivateLinkServiceHandler) PausePrivateLinkService(c *gin.Context) {
	req := &regionalapiv1.PausePrivateLinkServiceRequest{
		ClusterId:            c.Param("clusterId"),
		PrivateLinkServiceId: c.Param("privateLinkServiceId"),
	}
	if err := (&message.PausePrivateLinkServiceReqValidatable{PausePrivateLinkServiceRequest: req}).Validate(); err != nil {
		log.Error("invalid request", zap.Any("req", req), zap.Error(err))
		interceptor.Fail(c, bizerr.New(bizerr.ParameterInvalid, err.Error()))
		return
	}
	err := r.plssvc.PausePrivateLinkService(c, req)
	if err != nil {
		log.Error("failed to pause private link service", zap.Any("req", req), zap.Error(err))
		interceptor.Fail(c, err)
		return
	} else {
		interceptor.SucceedPb(c, http.StatusOK, new(emptypb.Empty))
	}
}

func (r *PrivateLinkServiceHandler) PurgePrivateLinkMetadata(c *gin.Context) {
	req := &regionalapiv1.PurgePrivateLinkMetadataRequest{
		ClusterId:            c.Param("clusterId"),
		PrivateLinkServiceId: c.Param("privateLinkServiceId"),
	}
	if err := (&message.PurgePrivateLinkMetadataReqValidatable{PurgePrivateLinkMetadataRequest: req}).Validate(); err != nil {
		log.Error("invalid request", zap.Any("req", req), zap.Error(err))
		interceptor.Fail(c, bizerr.New(bizerr.ParameterInvalid, err.Error()))
		return
	}
	err := r.plssvc.PurgePrivateLinkMetadata(c, req)
	if err != nil {
		log.Error("failed to purge private link metadata", zap.Any("req", req), zap.Error(err))
		interceptor.Fail(c, err)
		return
	} else {
		interceptor.SucceedPb(c, http.StatusOK, new(emptypb.Empty))
	}
}
