package dm

import (
	"context"

	"go.opentelemetry.io/otel"
	"google.golang.org/protobuf/types/known/emptypb"

	globalserverlessv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/serverless/v1"
	"github.com/tidbcloud/tidb-management-service/serverless-global-service/domain/repository"
	dmdomain "github.com/tidbcloud/tidb-management-service/serverless-global-service/domain/service/dm"
	"github.com/tidbcloud/tidb-management-service/serverless-global-service/domain/service/region"
)

var Tracer = otel.Tracer("app.dm")

type service struct {
	globalserverlessv1.UnimplementedServerlessDMServiceServer

	dmService   dmdomain.Service
	regionSvc   region.Service
	clusterRepo repository.ClusterRepo
}

func NewService(
	dmService dmdomain.Service,
	regionSvc region.Service,
	clusterRepo repository.ClusterRepo,
) globalserverlessv1.ServerlessDMServiceServer {
	return &service{
		dmService:   dmService,
		regionSvc:   regionSvc,
		clusterRepo: clusterRepo,
	}
}

func (s *service) Precheck(
	ctx context.Context,
	req *globalserverlessv1.ServerlessDMServicePrecheckRequest,
) (*globalserverlessv1.ServerlessDMServicePrecheckResponse, error) {
	ctx, span := Tracer.Start(ctx, "Precheck")
	defer span.End()
	cluster, err := s.clusterRepo.Get(ctx, req.GetClusterId(), false)
	if err != nil {
		return nil, err
	}
	res, err := s.dmService.Precheck(ctx, req, cluster.Region)
	if err != nil {
		return nil, err
	}
	return res, nil
}

func (s *service) CreateVpcEndpoint(
	ctx context.Context,
	req *globalserverlessv1.CreateVpcEndpointRequest,
) (*globalserverlessv1.CreateVpcEndpointResponse, error) {
	ctx, span := Tracer.Start(ctx, "CreateVpcEndpoint")
	defer span.End()
	cluster, err := s.clusterRepo.Get(ctx, req.GetClusterId(), false)
	if err != nil {
		return nil, err
	}
	res, err := s.dmService.CreateVpcEndpoint(ctx, req, cluster.ProjectId, cluster.ClusterId, cluster.Region)
	if err != nil {
		return nil, err
	}
	return res, nil
}

func (s *service) DescribeVpcEndpoint(
	ctx context.Context,
	req *globalserverlessv1.DescribeVpcEndpointRequest,
) (*globalserverlessv1.DescribeVpcEndpointResponse, error) {
	ctx, span := Tracer.Start(ctx, "DescribeVpcEndpoint")
	defer span.End()
	cluster, err := s.clusterRepo.Get(ctx, req.GetClusterId(), false)
	if err != nil {
		return nil, err
	}
	res, err := s.dmService.DescribeVpcEndpoint(ctx, req, cluster.ProjectId, cluster.ClusterId, cluster.Region)
	if err != nil {
		return nil, err
	}
	return res, nil
}

func (s *service) DeleteVpcEndpoint(
	ctx context.Context,
	req *globalserverlessv1.DeleteVpcEndpointRequest,
) (*emptypb.Empty, error) {
	ctx, span := Tracer.Start(ctx, "DeleteVpcEndpoint")
	defer span.End()
	cluster, err := s.clusterRepo.Get(ctx, req.GetClusterId(), false)
	if err != nil {
		return nil, err
	}
	err = s.dmService.DeleteVpcEndpoint(ctx, req, cluster.ProjectId, cluster.ClusterId, cluster.Region)
	if err != nil {
		return nil, err
	}
	return &emptypb.Empty{}, nil
}

// ListVpcEndpointZones lists zones for a VPC endpoint
func (s *service) ListVpcEndpointZones(
	ctx context.Context,
	req *globalserverlessv1.ListVpcEndpointZonesRequest,
) (*globalserverlessv1.ListVpcEndpointZonesResponse, error) {
	ctx, span := Tracer.Start(ctx, "ListVpcEndpointZones")
	defer span.End()
	cluster, err := s.clusterRepo.Get(ctx, req.GetClusterId(), false)
	if err != nil {
		return nil, err
	}
	res, err := s.dmService.ListVpcEndpointZones(ctx, req, cluster.ProjectId, cluster.ClusterId, cluster.Region)
	if err != nil {
		return nil, err
	}
	return res, nil
}

// RemoveZoneFromVpcEndpoint removes a zone from a VPC endpoint
func (s *service) RemoveZoneFromVpcEndpoint(
	ctx context.Context,
	req *globalserverlessv1.RemoveZoneFromVpcEndpointRequest,
) (*emptypb.Empty, error) {
	ctx, span := Tracer.Start(ctx, "RemoveZoneFromVpcEndpoint")
	defer span.End()
	cluster, err := s.clusterRepo.Get(ctx, req.GetClusterId(), false)
	if err != nil {
		return nil, err
	}
	err = s.dmService.RemoveZoneFromVpcEndpoint(ctx, req, cluster.ProjectId, cluster.ClusterId, cluster.Region)
	if err != nil {
		return nil, err
	}
	return &emptypb.Empty{}, nil
}
