package mgmtservicecmock

import (
	"context"
	"sync"
	"time"

	"braces.dev/errtrace"
	"github.com/google/uuid"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/types/known/timestamppb"

	openapicommonv1beta1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_cloud_open_api/common/v1beta1"
	openapidedicatedv1beta1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_cloud_open_api/dedicated/v1beta1"
)

func setupFakeDedicatedGRPCServer(s *grpc.Server) {
	svc := &fakeTiDBMgmtDedicatedService{
		clusters:        map[string]*openapidedicatedv1beta1.Cluster{},
		deletedClusters: map[string]*openapidedicatedv1beta1.Cluster{},
	}

	openapidedicatedv1beta1.RegisterClusterServiceServer(s, svc)
	openapidedicatedv1beta1.RegisterRegionServiceServer(s, svc)
	openapidedicatedv1beta1.RegisterNetworkContainerServiceServer(s, svc)
	openapidedicatedv1beta1.RegisterDatabaseAuditLogServiceServer(s, svc)
	openapidedicatedv1beta1.RegisterPrivateEndpointConnectionServiceServer(s, svc)
	openapidedicatedv1beta1.RegisterCustomerManagedEncryptionKeyServiceServer(s, svc)
	openapidedicatedv1beta1.RegisterMaintenanceServiceServer(s, svc)
	openapidedicatedv1beta1.RegisterTidbNodeGroupServiceServer(s, svc)
}

type fakeTiDBMgmtDedicatedService struct {
	openapidedicatedv1beta1.UnimplementedClusterServiceServer
	openapidedicatedv1beta1.UnimplementedRegionServiceServer
	openapidedicatedv1beta1.UnimplementedNetworkContainerServiceServer
	openapidedicatedv1beta1.UnimplementedDatabaseAuditLogServiceServer
	openapidedicatedv1beta1.UnimplementedPrivateEndpointConnectionServiceServer
	openapidedicatedv1beta1.UnimplementedCustomerManagedEncryptionKeyServiceServer
	openapidedicatedv1beta1.UnimplementedMaintenanceServiceServer
	openapidedicatedv1beta1.UnimplementedTidbNodeGroupServiceServer

	mu sync.Mutex
	// // name -> cluster
	clusters        map[string]*openapidedicatedv1beta1.Cluster
	deletedClusters map[string]*openapidedicatedv1beta1.Cluster
}

// ClusterService
func (f *fakeTiDBMgmtDedicatedService) CreateCluster(
	_ context.Context,
	r *openapidedicatedv1beta1.CreateClusterRequest,
) (*openapidedicatedv1beta1.Cluster, error) {
	f.mu.Lock()
	defer f.mu.Unlock()

	id := uuid.New().String()
	cluster := r.Cluster
	cluster.ClusterId = id
	cluster.State = openapicommonv1beta1.ClusterState_CREATING
	cluster.CreateTime = timestamppb.New(time.Now())
	cluster.UpdateTime = cluster.CreateTime

	f.clusters[cluster.ClusterId] = cluster

	return &openapidedicatedv1beta1.Cluster{}, nil
}

func (f *fakeTiDBMgmtDedicatedService) ListClusters(
	_ context.Context,
	r *openapidedicatedv1beta1.ListClustersRequest,
) (*openapidedicatedv1beta1.ListClustersResponse, error) {
	f.mu.Lock()
	defer f.mu.Unlock()

	resp := &openapidedicatedv1beta1.ListClustersResponse{}
	for _, v := range f.clusters {
		resp.Clusters = append(resp.Clusters, v)
	}

	return resp, nil
}

func (f *fakeTiDBMgmtDedicatedService) GetCluster(
	_ context.Context,
	r *openapidedicatedv1beta1.GetClusterRequest,
) (*openapidedicatedv1beta1.Cluster, error) {
	f.mu.Lock()
	defer f.mu.Unlock()

	if r.ClusterId == "" {
		return nil, errtrace.Wrap(status.Error(codes.InvalidArgument, "invalid clusterId"))
	}

	if cluster, ok := f.clusters[r.ClusterId]; ok {
		return cluster, nil
	}

	return nil, errtrace.Wrap(status.Error(codes.NotFound, "cluster not found"))
}

func (f *fakeTiDBMgmtDedicatedService) UpdateCluster(
	_ context.Context,
	r *openapidedicatedv1beta1.UpdateClusterRequest,
) (*openapidedicatedv1beta1.Cluster, error) {
	f.mu.Lock()
	defer f.mu.Unlock()

	if cluster, ok := f.clusters[r.GetCluster().GetClusterId()]; ok {
		return cluster, nil
	}

	return nil, errtrace.Wrap(status.Error(codes.NotFound, "cluster not found"))
}

func (f *fakeTiDBMgmtDedicatedService) DeleteCluster(
	_ context.Context,
	r *openapidedicatedv1beta1.DeleteClusterRequest,
) (*openapidedicatedv1beta1.Cluster, error) {
	f.mu.Lock()
	defer f.mu.Unlock()

	if cluster, ok := f.clusters[r.ClusterId]; ok {
		delete(f.clusters, r.ClusterId)
		cluster.UpdateTime = timestamppb.New(time.Now())
		f.deletedClusters[r.ClusterId] = cluster
		return cluster, nil
	}
	return nil, errtrace.Wrap(status.Error(codes.NotFound, "cluster not found"))
}

// RegionService
func (f *fakeTiDBMgmtDedicatedService) ShowCloudProviders(
	_ context.Context,
	r *openapidedicatedv1beta1.ShowCloudProvidersRequest,
) (*openapidedicatedv1beta1.ShowCloudProvidersResponse, error) {
	f.mu.Lock()
	defer f.mu.Unlock()

	providers := []openapicommonv1beta1.Region_CloudProvider{
		openapicommonv1beta1.Region_aws,
		openapicommonv1beta1.Region_gcp,
	}

	return &openapidedicatedv1beta1.ShowCloudProvidersResponse{CloudProviders: providers}, nil
}

func (f *fakeTiDBMgmtDedicatedService) ListRegions(
	_ context.Context,
	r *openapidedicatedv1beta1.ListRegionsRequest,
) (*openapidedicatedv1beta1.ListRegionsResponse, error) {
	f.mu.Lock()
	defer f.mu.Unlock()

	regions := []*openapicommonv1beta1.Region{
		{
			RegionId:      "aws-us-east-1",
			CloudProvider: openapicommonv1beta1.Region_aws,
			DisplayName:   "US East (N. Virginia)",
		},
		{
			RegionId:      "aws-us-west-2",
			CloudProvider: openapicommonv1beta1.Region_aws,
			DisplayName:   "US West (Oregon)",
		},
		{
			RegionId:      "aws-us-central1",
			CloudProvider: openapicommonv1beta1.Region_aws,
			DisplayName:   "US Central (Iowa)",
		},
	}

	return &openapidedicatedv1beta1.ListRegionsResponse{Regions: regions}, nil
}

func (f *fakeTiDBMgmtDedicatedService) GetRegion(
	_ context.Context,
	r *openapidedicatedv1beta1.GetRegionRequest,
) (*openapicommonv1beta1.Region, error) {
	f.mu.Lock()
	defer f.mu.Unlock()

	if r.RegionId == "" {
		return nil, errtrace.Wrap(status.Error(codes.InvalidArgument, "invalid regionId"))
	}

	return &openapicommonv1beta1.Region{
		RegionId:      "aws-us-west-2",
		CloudProvider: openapicommonv1beta1.Region_aws,
		DisplayName:   "US West (Oregon)",
	}, nil
}
