package cluster

import (
	"context"

	"github.com/samber/lo"
	"google.golang.org/protobuf/types/known/emptypb"

	globaldedicatedv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/dedicated/v1"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/adapter/account"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/entity"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/repository/cluster"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/repository/quota"
	clustersvc "github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/service/cluster"
)

func NewServer(
	svc clustersvc.Service,
	tenantQuotaRepo quota.TenantClusterComponentQuotaRepo,
	clusterSnapshotRepo cluster.ComponentSnapshotRepo,
	tenantFacade account.Client,
	updateTenantQuotaSvc clustersvc.UpdateTenantQuotaSvc,
	createClusterSnapshotSvc clustersvc.CreateClusterSnapshotSvc,
) globaldedicatedv1.ClusterServiceServer {
	return &server{
		Service:                  svc,
		tenantQuotaRepo:          tenantQuotaRepo,
		clusterSnapshotRepo:      clusterSnapshotRepo,
		tenantFacade:             tenantFacade,
		updateTenantQuotaSvc:     updateTenantQuotaSvc,
		createClusterSnapshotSvc: createClusterSnapshotSvc,
	}
}

var _ globaldedicatedv1.ClusterServiceServer = (*server)(nil)

type server struct {
	globaldedicatedv1.UnimplementedClusterServiceServer

	clustersvc.Service

	tenantQuotaRepo          quota.TenantClusterComponentQuotaRepo
	clusterSnapshotRepo      cluster.ComponentSnapshotRepo
	tenantFacade             account.Client
	updateTenantQuotaSvc     clustersvc.UpdateTenantQuotaSvc
	createClusterSnapshotSvc clustersvc.CreateClusterSnapshotSvc
}

func (s *server) CreateCluster(
	ctx context.Context,
	request *globaldedicatedv1.CreateClusterRequest,
) (*globaldedicatedv1.Cluster, error) {
	c, err := s.Create(ctx, request)
	if err != nil {
		return nil, err
	}
	return c.Cluster, nil
}

func (s *server) ListClusters(
	ctx context.Context,
	request *globaldedicatedv1.ListClustersRequest,
) (*globaldedicatedv1.ListClustersResponse, error) {
	cs, err := s.List(ctx, request)
	if err != nil {
		return nil, err
	}
	mappedCs := lo.Map(cs, func(c *entity.Cluster, _ int) *globaldedicatedv1.Cluster {
		return c.Cluster
	})
	return &globaldedicatedv1.ListClustersResponse{
		Clusters: mappedCs,
	}, nil
}

func (s *server) GetCluster(
	ctx context.Context,
	request *globaldedicatedv1.GetClusterRequest,
) (*globaldedicatedv1.Cluster, error) {
	c, err := s.Get(ctx, &globaldedicatedv1.GetClusterRequest{ClusterId: request.ClusterId})
	if err != nil {
		return nil, err
	}
	return c.Cluster, nil
}

// DeleteCluster deletes the cluster but keeps the backup records. The cluster can be restored from the backup.
func (s *server) DeleteCluster(
	ctx context.Context,
	request *globaldedicatedv1.DeleteClusterRequest,
) (*globaldedicatedv1.Cluster, error) {
	c, err := s.Delete(ctx, request)
	if err != nil {
		return nil, err
	}
	return c.Cluster, nil
}

func (s *server) ScaleCluster(
	ctx context.Context,
	request *globaldedicatedv1.ScaleClusterRequest,
) (*globaldedicatedv1.Cluster, error) {
	c, err := s.Scale(ctx, &globaldedicatedv1.ScaleClusterWithMultiComponentRequest{
		ClusterId:         request.ClusterId,
		ValidateOnly:      request.ValidateOnly,
		ClusterComponents: []*globaldedicatedv1.Cluster_ClusterComponent{request.ClusterComponent},
		ForceScale:        request.ForceScale,
	})
	if err != nil {
		return nil, err
	}
	return c.Cluster, nil
}

func (s *server) ScaleClusterWithMultiComponent(
	ctx context.Context,
	request *globaldedicatedv1.ScaleClusterWithMultiComponentRequest,
) (*globaldedicatedv1.Cluster, error) {
	c, err := s.Scale(ctx, request)
	if err != nil {
		return nil, err
	}
	return c.Cluster, nil
}

func (s *server) PauseCluster(
	ctx context.Context,
	request *globaldedicatedv1.PauseClusterRequest,
) (*globaldedicatedv1.Cluster, error) {
	c, err := s.Pause(ctx, request)
	if err != nil {
		return nil, err
	}
	return c.Cluster, nil
}

func (s *server) ResumeCluster(
	ctx context.Context,
	request *globaldedicatedv1.ResumeClusterRequest,
) (*globaldedicatedv1.Cluster, error) {
	c, err := s.Resume(ctx, request, nil)
	if err != nil {
		return nil, err
	}
	return c.Cluster, nil
}

func (s *server) GetComponentNodeInfo(
	ctx context.Context,
	request *globaldedicatedv1.GetComponentNodeInfoRequest,
) (*globaldedicatedv1.GetComponentNodeInfoResponse, error) {
	nodeInfo, err := s.Service.GetComponentNodeInfo(ctx, request)
	if err != nil {
		return nil, err
	}

	return &globaldedicatedv1.GetComponentNodeInfoResponse{
		ComponentNodeInfo: nodeInfo,
	}, nil
}

func (s *server) ApplyGKEHeterogeneousDeploy(
	ctx context.Context,
	request *globaldedicatedv1.ApplyGKEHeterogeneousDeployRequest,
) (*globaldedicatedv1.GKEHeterogeneousDeploy, error) {
	return s.Service.ApplyGKEHeterogeneousDeploy(ctx, request)
}

func (s *server) GetGKEHeterogeneousDeploy(
	ctx context.Context,
	request *globaldedicatedv1.GetGKEHeterogeneousDeployRequest,
) (*globaldedicatedv1.GKEHeterogeneousDeploy, error) {
	return s.Service.GetGKEHeterogeneousDeploy(ctx, request)
}

func (s *server) DeleteGKEHeterogeneousDeploy(
	ctx context.Context,
	request *globaldedicatedv1.DeleteGKEHeterogeneousDeployRequest,
) (*emptypb.Empty, error) {
	if err := s.Service.DeleteGKEHeterogeneousDeploy(ctx, request); err != nil {
		return nil, err
	}
	return &emptypb.Empty{}, nil
}

func (s *server) SetClientCaCertificate(
	ctx context.Context,
	request *globaldedicatedv1.SetClientCaCertificateRequest,
) (*emptypb.Empty, error) {
	err := s.Service.SetClientCaCertificate(ctx, request)
	if err != nil {
		return nil, err
	}
	return &emptypb.Empty{}, nil
}

func (s *server) GetClientCaCertificate(
	ctx context.Context,
	request *globaldedicatedv1.GetClientCaCertificateRequest,
) (*globaldedicatedv1.GetClientCaCertificateResponse, error) {
	ca, err := s.Service.GetClientCaCertificate(ctx, request)
	if err != nil {
		return nil, err
	}
	return ca, nil
}

func (s *server) ApplyCluster(
	ctx context.Context,
	request *globaldedicatedv1.ApplyClusterRequest,
) (*globaldedicatedv1.Cluster, error) {
	c, err := s.Service.ApplyCluster(ctx, request)
	if err != nil {
		return nil, err
	}
	return c.Cluster, nil
}

func (s *server) GetBillableClustersCount(
	ctx context.Context,
	request *globaldedicatedv1.GetBillableClustersCountRequest,
) (*globaldedicatedv1.GetBillableClustersCountResponse, error) {
	resp, err := s.Service.GetBillableClustersCount(ctx, request)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func (s *server) GetClusterConfidentialInfo(
	ctx context.Context,
	request *globaldedicatedv1.GetClusterConfidentialInfoRequest,
) (*globaldedicatedv1.GetClusterConfidentialInfoResponse, error) {
	resp, err := s.Service.GetClusterConfidentialInfo(ctx, request)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func (s *server) ChangeClusterPasswd(
	ctx context.Context,
	request *globaldedicatedv1.ChangeClusterPasswdRequest,
) (*globaldedicatedv1.ChangeClusterPasswdResponse, error) {
	resp := &globaldedicatedv1.ChangeClusterPasswdResponse{}

	if err := s.ChangeRootPasswd(ctx, request.ClusterId, request.Passwd); err != nil {
		return resp, err
	}

	resp.Synced = true
	return resp, nil
}

func (s *server) GetDefaultTidbVersion(
	ctx context.Context,
	request *globaldedicatedv1.GetDefaultTidbVersionRequest,
) (*globaldedicatedv1.GetDefaultTidbVersionResponse, error) {
	resp, err := s.Service.GetDefaultTidbVersion(ctx, request)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func (s *server) GetLogRedactionPolicy(
	ctx context.Context,
	request *globaldedicatedv1.GetLogRedactionPolicyRequest,
) (*globaldedicatedv1.LogRedactionPolicy, error) {
	resp, err := s.Service.GetLogRedactionPolicy(ctx, request.GetClusterId())
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func (s *server) UpdateLogRedactionPolicy(
	ctx context.Context,
	request *globaldedicatedv1.UpdateLogRedactionPolicyRequest,
) (*globaldedicatedv1.LogRedactionPolicy, error) {
	err := s.Service.UpdateLogRedactionPolicy(ctx, request)
	if err != nil {
		return nil, err
	}
	return request.LogRedactionPolicy, nil
}

func (s *server) BuildDesiredComponents(
	ctx context.Context,
	req *globaldedicatedv1.BuildDesiredComponentsRequest,
) (*globaldedicatedv1.BuildDesiredComponentsResponse, error) {
	resp, err := s.Service.BuildDesiredComponents(ctx, req)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func (s *server) GetClusterCustomParams(
	ctx context.Context,
	req *globaldedicatedv1.GetClusterCustomParamsRequest,
) (*globaldedicatedv1.GetClusterCustomParamsResponse, error) {
	resp, err := s.Service.GetClusterCustomParams(ctx, req)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func (s *server) SyncSystemVariables(
	ctx context.Context,
	req *globaldedicatedv1.SyncSystemVariablesRequest,
) (*emptypb.Empty, error) {
	err := s.Service.SyncSystemVariables(ctx, req)
	if err != nil {
		return nil, err
	}
	return &emptypb.Empty{}, nil
}

func (s *server) SyncPdOnlineConfig(
	ctx context.Context,
	req *globaldedicatedv1.SyncPdOnlineConfigRequest,
) (*emptypb.Empty, error) {
	err := s.Service.SyncPdOnlineConfig(ctx, req)
	if err != nil {
		return nil, err
	}
	return &emptypb.Empty{}, nil
}

func (s *server) ShutdownUpgradeCluster(
	ctx context.Context,
	request *globaldedicatedv1.ShutdownUpgradeClusterRequest,
) (*emptypb.Empty, error) {
	if err := s.Service.ShutdownUpgradeCluster(ctx, request); err != nil {
		return nil, err
	}
	return &emptypb.Empty{}, nil
}

func (s *server) EnableClusterConfigBackup(
	ctx context.Context,
	req *globaldedicatedv1.EnableClusterConfigBackupRequest,
) (*globaldedicatedv1.ClusterConfigBackupPolicy, error) {
	resp, err := s.Service.EnableClusterConfigBackup(ctx, req)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func (s *server) DisableClusterConfigBackup(
	ctx context.Context,
	req *globaldedicatedv1.DisableClusterConfigBackupRequest,
) (*globaldedicatedv1.ClusterConfigBackupPolicy, error) {
	resp, err := s.Service.DisableClusterConfigBackup(ctx, req)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func (s *server) ListClusterConfigBackup(
	ctx context.Context,
	req *globaldedicatedv1.ListClusterConfigBackupRequest,
) (*globaldedicatedv1.ListClusterConfigBackupResponse, error) {
	resp, err := s.Service.ListClusterConfigBackup(ctx, req)
	if err != nil {
		return nil, err
	}
	return resp, nil
}
