package dm

import (
	"context"

	"go.opentelemetry.io/otel"

	globalcommonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	globalserverlessv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/serverless/v1"
	regionalserverlessv1 "github.com/tidbcloud/tidb-management-service/api/spec/regional/serverless/v1"
	"github.com/tidbcloud/tidb-management-service/serverless-global-service/domain/adapter"
)

var Tracer = otel.Tracer("DomainService.DM")

type Service interface {
	Precheck(
		ctx context.Context,
		req *globalserverlessv1.ServerlessDMServicePrecheckRequest,
		region *globalcommonv1.Region,
	) (*globalserverlessv1.ServerlessDMServicePrecheckResponse, error)
	CreateVpcEndpoint(
		ctx context.Context,
		req *globalserverlessv1.CreateVpcEndpointRequest,
		projectId uint64,
		clusterId string,
		region *globalcommonv1.Region,
	) (*globalserverlessv1.CreateVpcEndpointResponse, error)
	DescribeVpcEndpoint(
		ctx context.Context,
		req *globalserverlessv1.DescribeVpcEndpointRequest,
		projectId uint64,
		clusterId string,
		region *globalcommonv1.Region,
	) (*globalserverlessv1.DescribeVpcEndpointResponse, error)
	DeleteVpcEndpoint(
		ctx context.Context,
		req *globalserverlessv1.DeleteVpcEndpointRequest,
		projectId uint64,
		clusterId string,
		region *globalcommonv1.Region,
	) error
	ListVpcEndpointZones(
		ctx context.Context,
		req *globalserverlessv1.ListVpcEndpointZonesRequest,
		projectId uint64,
		clusterId string,
		region *globalcommonv1.Region,
	) (*globalserverlessv1.ListVpcEndpointZonesResponse, error)
	RemoveZoneFromVpcEndpoint(
		ctx context.Context,
		req *globalserverlessv1.RemoveZoneFromVpcEndpointRequest,
		projectId uint64,
		clusterId string,
		region *globalcommonv1.Region,
	) error
}

type dmServiceImpl struct {
	regionalSvc adapter.RegionalService
}

func NewDMService(regionalSvc adapter.RegionalService) Service {
	return &dmServiceImpl{
		regionalSvc: regionalSvc,
	}
}

func (s *dmServiceImpl) Precheck(
	ctx context.Context,
	req *globalserverlessv1.ServerlessDMServicePrecheckRequest,
	region *globalcommonv1.Region,
) (*globalserverlessv1.ServerlessDMServicePrecheckResponse, error) {
	ctx, span := Tracer.Start(ctx, "Domain.Precheck")
	defer span.End()

	res, err := s.regionalSvc.DMPrecheck(ctx, region,
		&regionalserverlessv1.ServerlessDMRegionalServicePrecheckRequest{
			DmPrecheck: req.DmPrecheck,
		})
	if err != nil {
		return nil, err
	}
	return &globalserverlessv1.ServerlessDMServicePrecheckResponse{
		Result: res.Result,
	}, nil
}

func (s *dmServiceImpl) CreateVpcEndpoint(
	ctx context.Context,
	req *globalserverlessv1.CreateVpcEndpointRequest,
	projectId uint64,
	clusterId string,
	region *globalcommonv1.Region,
) (*globalserverlessv1.CreateVpcEndpointResponse, error) {
	ctx, span := Tracer.Start(ctx, "Domain.CreateVpcEndpoint")
	defer span.End()

	res, err := s.regionalSvc.CreateVpcEndpoint(ctx, region,
		&regionalserverlessv1.CreateVpcEndpointRequest{
			ServiceName: req.ServiceName,
			ProjectId:   projectId,
			ClusterId:   clusterId,
		})
	if err != nil {
		return nil, err
	}
	return &globalserverlessv1.CreateVpcEndpointResponse{
		EndpointDetails: res.EndpointDetails,
	}, nil
}

func (s *dmServiceImpl) DescribeVpcEndpoint(
	ctx context.Context,
	req *globalserverlessv1.DescribeVpcEndpointRequest,
	projectId uint64,
	clusterId string,
	region *globalcommonv1.Region,
) (*globalserverlessv1.DescribeVpcEndpointResponse, error) {
	ctx, span := Tracer.Start(ctx, "Domain.DescribeVpcEndpoint")
	defer span.End()

	res, err := s.regionalSvc.DescribeVpcEndpoint(ctx, region,
		&regionalserverlessv1.DescribeVpcEndpointRequest{
			EndpointId: req.EndpointId,
			ProjectId:  projectId,
			ClusterId:  clusterId,
		})
	if err != nil {
		return nil, err
	}
	return &globalserverlessv1.DescribeVpcEndpointResponse{
		EndpointDetails: res.EndpointDetails,
	}, nil
}

func (s *dmServiceImpl) DeleteVpcEndpoint(
	ctx context.Context,
	req *globalserverlessv1.DeleteVpcEndpointRequest,
	projectId uint64,
	clusterId string,
	region *globalcommonv1.Region,
) error {
	ctx, span := Tracer.Start(ctx, "Domain.DeleteVpcEndpoint")
	defer span.End()

	_, err := s.regionalSvc.DeleteVpcEndpoint(ctx, region,
		&regionalserverlessv1.DeleteVpcEndpointRequest{
			EndpointId: req.EndpointId,
			ProjectId:  projectId,
			ClusterId:  clusterId,
		})
	return err
}

func (s *dmServiceImpl) ListVpcEndpointZones(
	ctx context.Context,
	req *globalserverlessv1.ListVpcEndpointZonesRequest,
	projectId uint64,
	clusterId string,
	region *globalcommonv1.Region,
) (*globalserverlessv1.ListVpcEndpointZonesResponse, error) {
	ctx, span := Tracer.Start(ctx, "Domain.ListVpcEndpointZones")
	defer span.End()
	res, err := s.regionalSvc.ListVpcEndpointZones(ctx, region,
		&regionalserverlessv1.ListVpcEndpointZonesRequest{
			EndpointId: req.GetEndpointId(),
			ProjectId:  projectId,
			ClusterId:  clusterId,
		})
	if err != nil {
		return nil, err
	}
	return &globalserverlessv1.ListVpcEndpointZonesResponse{
		Zones: res.Zones,
	}, nil
}

func (s *dmServiceImpl) RemoveZoneFromVpcEndpoint(
	ctx context.Context,
	req *globalserverlessv1.RemoveZoneFromVpcEndpointRequest,
	projectId uint64,
	clusterId string,
	region *globalcommonv1.Region,
) error {
	ctx, span := Tracer.Start(ctx, "Domain.RemoveZoneFromVpcEndpoint")
	defer span.End()
	_, err := s.regionalSvc.RemoveZoneFromVpcEndpoint(ctx, region,
		&regionalserverlessv1.RemoveZoneFromVpcEndpointRequest{
			EndpointId: req.GetEndpointId(),
			ZoneId:     req.GetZoneId(),
			ProjectId:  projectId,
			ClusterId:  clusterId,
		})
	return err
}
