package tidbmgmtsvchandler

import (
	"context"
	"sync"

	"github.com/pingcap/dbaas/billing2/billing_errors"
	"github.com/pingcap/dbaas/billing2/config"
	"github.com/pingcap/dbaas/billing2/forms"
	tigrpc "github.com/tidbcloud/tiinf/transport/tigrpc/client"

	commonv1beta1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_cloud_open_api/common/v1beta1"
	mgmt "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
)

type MgmtService interface {
	GetClusterByID(tenantID uint64, clusterID uint64) (ret *forms.Cluster, err error)
	GetNextGenClustersByTenantID(ctx context.Context, tenantID uint64) (ret []forms.Cluster, err error)
	GetAllClustersByTenantID(ctx context.Context, tenantID uint64) (ret []*forms.Cluster, err error)
	// GetEssentialV1ClustersByTenantID lists all live essential v1 serverless clusters of the tenant.
	GetEssentialV1ClustersByTenantID(ctx context.Context, tenantID uint64) (ret []*forms.Cluster, err error)
}
type MgmtServiceImpl struct {
	client mgmt.ClusterServiceClient
}

var (
	globalMgmtService MgmtService
	lock              sync.Mutex
)

func Get() (MgmtService, error) {
	if globalMgmtService != nil {
		return globalMgmtService, nil
	}
	lock.Lock()
	defer lock.Unlock()
	conn, err := tigrpc.NewClientConn(config.Get().GetString(config.TiDBMgmtSvcAddress), tigrpc.WithDefault())
	if err != nil {
		return nil, err
	}
	globalMgmtService = &MgmtServiceImpl{
		client: mgmt.NewClusterServiceClient(conn),
	}
	return globalMgmtService, nil
}
func Set(mgmt MgmtService) {
	globalMgmtService = mgmt
}

func convertServicePlan(plan commonv1beta1.ServicePlan) string {
	if plan == commonv1beta1.ServicePlan_SERVICE_PLAN_UNSPECIFIED {
		return ""
	}
	return plan.String()
}

func (m *MgmtServiceImpl) GetClusterByID(tenantID uint64, clusterID uint64) (ret *forms.Cluster, err error) {
	deleted := true
	req := &mgmt.ListClustersRequest{
		Filter: &mgmt.ClusterFilter{
			ClusterIds: []uint64{clusterID},
			View:       mgmt.ClusterFilter_CLUSTER_VIEW_LOCAL,
			Deleted:    &deleted,
			TenantIds:  []uint64{tenantID},
		},
	}
	listResp, err := m.client.ListClusters(context.Background(), req)

	if err != nil {
		return nil, billing_errors.NewSystemError(err)
	}
	if len(listResp.Clusters) == 0 {
		return nil, billing_errors.ObjectNotExistsError
	}
	//
	cluster := listResp.Clusters[0]
	if convertServicePlan(cluster.ServicePlan) == "BYOC" {
		return nil, billing_errors.ObjectNotExistsError
	}
	var deleteTime *int64
	if cluster.DeleteTime != nil {
		t := cluster.DeleteTime.Seconds
		deleteTime = &t
	}
	return &forms.Cluster{
		ID:   uint(clusterID),
		Name: cluster.ClusterName,
		//TenantID:          cluster.,
		CloudProviderName: cluster.CloudProvider,
		ProjectID:         uint(cluster.ProjectId),
		//InfraProviderType: cluster.,
		RegionName: cluster.Region,
		//RegionDisplayName: cluster.,
		//IsDevTier:         cluster.IsDevTier,
		//Status:            cluster.,
		DeletedAt:   deleteTime,
		DeployType:  int32(cluster.DeployType),
		ServicePlan: convertServicePlan(cluster.ServicePlan),
	}, nil
}

func (m *MgmtServiceImpl) GetNextGenClustersByTenantID(ctx context.Context, tenantID uint64) (ret []forms.Cluster, err error) {
	req := &mgmt.ListClustersRequest{
		Filter: &mgmt.ClusterFilter{
			ClusterType: mgmt.DeployType_DEPLOY_TYPE_NEXTGEN.Enum(),
			TenantIds:   []uint64{tenantID},
			View:        mgmt.ClusterFilter_CLUSTER_VIEW_LOCAL,
		},
	}
	listResp, err := m.client.ListClusters(ctx, req)

	if err != nil {
		return nil, billing_errors.NewSystemError(err)
	}
	//
	var clusters []forms.Cluster
	for _, c := range listResp.Clusters {
		if convertServicePlan(c.ServicePlan) == "BYOC" {
			continue
		}
		var deleteTime *int64
		if c.DeleteTime != nil {
			t := c.DeleteTime.Seconds
			deleteTime = &t
		}
		clusters = append(clusters, forms.Cluster{
			ID:                uint(c.ClusterId),
			Name:              c.ClusterName,
			CloudProviderName: c.CloudProvider,
			ProjectID:         uint(c.ProjectId),
			RegionName:        c.Region,
			DeletedAt:         deleteTime,
			ServicePlan:       convertServicePlan(c.ServicePlan),
		})
	}

	return clusters, nil
}
func (m *MgmtServiceImpl) GetAllClustersByTenantID(ctx context.Context, tenantID uint64) (ret []*forms.Cluster, err error) {
	deleted := true
	req := &mgmt.ListClustersRequest{
		Filter: &mgmt.ClusterFilter{
			TenantIds: []uint64{tenantID},
			View:      mgmt.ClusterFilter_CLUSTER_VIEW_LOCAL,
			Deleted:   &deleted,
		},
	}
	listResp, err := m.client.ListClusters(ctx, req)

	if err != nil {
		return nil, billing_errors.NewSystemError(err)
	}
	//
	var clusters []*forms.Cluster
	for _, c := range listResp.Clusters {
		if convertServicePlan(c.ServicePlan) == "BYOC" {
			continue
		}
		var deleteTime *int64
		if c.DeleteTime != nil {
			t := c.DeleteTime.Seconds
			deleteTime = &t
		}
		clusters = append(clusters, &forms.Cluster{
			ID:                uint(c.ClusterId),
			Name:              c.ClusterName,
			CloudProviderName: c.CloudProvider,
			ProjectID:         uint(c.ProjectId),
			RegionName:        c.Region,
			DeletedAt:         deleteTime,
			ServicePlan:       convertServicePlan(c.ServicePlan),
		})
	}

	return clusters, nil
}

func (m *MgmtServiceImpl) GetEssentialV1ClustersByTenantID(ctx context.Context, tenantID uint64) (ret []*forms.Cluster, err error) {
	// List live essential v1 serverless clusters only: the tidb-mgmt-service
	// internal API filters on the mgmt clusters table's service_plan column,
	// where 'Essential' means the legacy essential v1 plan (Essential_V2 is a
	// nextgen product and never stored in this table as 'Essential').
	deleted := false
	const pageSize uint32 = 1000 // tidb-mgmt-service internal ListClusters caps page size at 1000
	var clusters []*forms.Cluster
	for page := uint32(1); ; page++ {
		req := &mgmt.ListClustersRequest{
			Page:     page,
			PageSize: pageSize,
			Filter: &mgmt.ClusterFilter{
				TenantIds:   []uint64{tenantID},
				ClusterType: mgmt.DeployType_DEPLOY_TYPE_SERVERLESS.Enum(),
				ServicePlan: commonv1beta1.ServicePlan_Essential,
				View:        mgmt.ClusterFilter_CLUSTER_VIEW_LOCAL,
				Deleted:     &deleted,
			},
		}
		listResp, err := m.client.ListClusters(ctx, req)
		if err != nil {
			return nil, billing_errors.NewSystemError(err)
		}
		for _, c := range listResp.Clusters {
			var deleteTime *int64
			if c.DeleteTime != nil {
				t := c.DeleteTime.Seconds
				deleteTime = &t
			}
			clusters = append(clusters, &forms.Cluster{
				ID:                uint(c.ClusterId),
				Name:              c.ClusterName,
				CloudProviderName: c.CloudProvider,
				ProjectID:         uint(c.ProjectId),
				RegionName:        c.Region,
				DeletedAt:         deleteTime,
				ServicePlan:       convertServicePlan(c.ServicePlan),
			})
		}
		if uint32(len(listResp.Clusters)) < pageSize {
			break
		}
	}

	return clusters, nil
}
