package entity

import (
	"errors"
	"time"

	"braces.dev/errtrace"

	globaldedicatedv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/dedicated/v1"
	globalngv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
	globalserverlessv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/serverless/v1"
	openapicommonv1beta1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_cloud_open_api/common/v1beta1"
	tidbmgmtservicev1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
)

type DeployType string

const (
	DeployTypeDedicated  DeployType = "DEPLOY_TYPE_DEDICATED"
	DeployTypeServerless DeployType = "DEPLOY_TYPE_SERVERLESS"
	DeployTypeNextGen    DeployType = "DEPLOY_TYPE_NEXTGEN"
)

type ShadowState int8

const (
	ShadowStateNone       ShadowState = iota
	ShadowStatePreparing              // under preparation
	ShadowStatePrepared               // prepared and ready to be rented
	ShadowStateRenting                // being rented
	ShadowStateRented                 // rented
	ShadowStateFreeing                // being freed
	ShadowStateFreed                  // freed and can be prepared again
	ShadowStateClaiming               // being claimed
	ShadowStateClaimed                // claimed
	ShadowStateTakingOver             // being taken over as a real cluster
	ShadowStateTakenOver              // taken over as a real cluster
)

type (
	ClusterView = tidbmgmtservicev1.ClusterFilter_ClusterView

	ClusterState = openapicommonv1beta1.ClusterState

	Cluster struct {
		ClusterId         uint64
		ProjectId         uint64
		OriginalProjectId uint64
		OrganizationId    uint64
		DisplayName       string
		DeployType        DeployType
		CloudProvider     openapicommonv1beta1.Region_CloudProvider
		Region            string
		UserId            string
		CreatedBy         string
		CreatedAt         time.Time
		UpdatedAt         time.Time
		DeletedAt         *time.Time
		Labels            map[string]string
		Annotations       map[string]string
		GlobalCluster     any
		ServicePlan       openapicommonv1beta1.ServicePlan
		EngineType        openapicommonv1beta1.EngineType
		// ResourcePoolID is a transient create/restore selector. The authoritative
		// association is persisted as shared_pool_id by nextgen-global-service.
		ResourcePoolID uint64
		// only used for restore, not used for normal create
		SourceClusterId uint64

		ShadowState  ShadowState
		ShadowPoolId uint64
		// InitSQLs holds SQL statements to be executed on the cluster during initialization.
		// Used when creating shadow pool clusters that have init SQL configured on the pool.
		InitSQLs []string

		RuVersion int32
		Tags      []Tag
	}
)

const (
	ClusterViewClusterViewLocal ClusterView = tidbmgmtservicev1.ClusterFilter_CLUSTER_VIEW_LOCAL
	ClusterViewClusterViewBasic ClusterView = tidbmgmtservicev1.ClusterFilter_CLUSTER_VIEW_BASIC
	ClusterViewClusterViewFull  ClusterView = tidbmgmtservicev1.ClusterFilter_CLUSTER_VIEW_FULL
)

func (c *Cluster) GetServerlessCluster() (*globalserverlessv1.ServerlessServiceCluster, error) {
	if c.DeployType != DeployTypeServerless {
		return nil, errtrace.Wrap(errors.New("cluster type is not serverless"))
	}

	if c.GlobalCluster == nil {
		return nil, errtrace.Wrap(errors.New("global serverless cluster is nil"))
	}

	switch cluster := c.GlobalCluster.(type) {
	case *globalserverlessv1.ServerlessServiceCluster:
		return cluster, nil
	case *globaldedicatedv1.Cluster:
		return nil, errtrace.Wrap(errors.New("cluster is dedicated cluster"))
	default:
		return nil, errtrace.Wrap(errors.New("global cluster cannot be converted into globalserverlessv1.ServerlessServiceCluster"))
	}
}

func (c *Cluster) GetDedicatedCluster() (*globaldedicatedv1.Cluster, error) {
	if c.DeployType != DeployTypeDedicated {
		return nil, errtrace.Wrap(errors.New("cluster type is not dedicated"))
	}

	if c.GlobalCluster == nil {
		return nil, errtrace.Wrap(errors.New("global dedicated cluster is nil"))
	}

	switch cluster := c.GlobalCluster.(type) {
	case *globaldedicatedv1.Cluster:
		return cluster, nil
	case *globalserverlessv1.ServerlessServiceCluster:
		return nil, errtrace.Wrap(errors.New("cluster is dedicated cluster"))
	default:
		return nil, errtrace.Wrap(errors.New("global cluster cannot be converted into globaldedicatedv1.Cluster"))
	}
}

func (c *Cluster) GetNextGenCluster() (*globalngv1.Cluster, error) {
	if c.DeployType != DeployTypeNextGen {
		return nil, errtrace.Wrap(errors.New("cluster type is not nextgen"))
	}

	if c.GlobalCluster == nil {
		return nil, errtrace.Wrap(errors.New("global nextgen cluster is nil"))
	}

	switch cluster := c.GlobalCluster.(type) {
	case *globalngv1.Cluster:
		return cluster, nil
	default:
		return nil, errtrace.Wrap(errors.New("global cluster cannot be converted into globalnextgenv1.Cluster"))
	}
}
