package tidbcluster

import (
	"context"

	opv1alpha1 "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"sigs.k8s.io/controller-runtime/pkg/client"

	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/tidbcloud/infra-provider/pkg/k8s"
)

type v1 struct {
	tc *opv1alpha1.TidbCluster
}

var _ Getter = &v1{}

func NewV1Getter(tc *opv1alpha1.TidbCluster) Getter {
	return &v1{tc: tc}
}

func (g *v1) IsNil() bool {
	return g.tc == nil
}

func (g *v1) IsReady() bool {
	ready := false
	if g.tc != nil {
		for _, cond := range g.tc.Status.Conditions {
			if cond.Type == opv1alpha1.TidbClusterReady && cond.Status == corev1.ConditionTrue {
				ready = true
				break
			}
		}
	}
	return ready
}

func (g *v1) IsDeleted(ctx context.Context, cli client.Client, name, ns string) bool {
	return k8s.GetDeletedObject(ctx, cli, types.NamespacedName{Name: name, Namespace: ns}, &opv1alpha1.TidbCluster{}) == nil
}

func (g *v1) IsTLSEnabled() bool {
	return g.tc != nil && g.tc.Spec.TiDB != nil && g.tc.Spec.TiDB.IsTLSClientEnabled()
}

func (g *v1) IsTiProxyEnabled() bool {
	return g.tc != nil && g.tc.TiProxyStsDesiredReplicas() != 0
}

func (g *v1) Name() string {
	if g.tc != nil {
		return g.tc.Name
	}
	return ""
}

func (g *v1) Namespace() string {
	if g.tc != nil {
		return g.tc.Namespace
	}
	return ""
}

func (g *v1) Object() metav1.Object {
	return g.tc
}

func (g *v1) Version() string {
	if g.tc != nil {
		return g.tc.Spec.Version
	}
	return ""
}

func (g *v1) TiKVImage() string {
	if g.tc != nil {
		return g.tc.TiKVImage()
	}
	return ""
}

func (g *v1) PDImage() string {
	if g.tc != nil {
		return g.tc.PDImage()
	}
	return ""
}

func (g *v1) BootstrapSQLConfigMapName() string {
	if g.tc != nil && g.tc.Spec.TiDB != nil && g.tc.Spec.TiDB.BootstrapSQLConfigMapName != nil {
		return *g.tc.Spec.TiDB.BootstrapSQLConfigMapName
	}
	return ""
}

func (g *v1) ComponentReplicas(componentType cloudv1.ComponentType) int32 {
	if g.tc == nil {
		return 0
	}
	switch componentType {
	case cloudv1.ComponentPD:
		if g.tc.Spec.PD != nil {
			return g.tc.Spec.PD.Replicas
		}
	case cloudv1.ComponentTiDB:
		if g.tc.Spec.TiDB != nil {
			return g.tc.Spec.TiDB.Replicas
		}
	case cloudv1.ComponentTiKV:
		if g.tc.Spec.TiKV != nil {
			return g.tc.Spec.TiKV.Replicas
		}
	case cloudv1.ComponentTiFlash, cloudv1.ComponentTiFlashLearner:
		if g.tc.Spec.TiFlash != nil {
			return g.tc.Spec.TiFlash.Replicas
		}
	case cloudv1.ComponentTiProxy:
		if g.tc.Spec.TiProxy != nil {
			return g.tc.Spec.TiProxy.Replicas
		}
	case cloudv1.ComponentTiCDC:
		if g.tc.Spec.TiCDC != nil {
			return g.tc.Spec.TiCDC.Replicas
		}
	}
	return 0
}

func (g *v1) ComponentDesiredReplicas(componentType cloudv1.ComponentType) int32 {
	if g.tc == nil {
		return 0
	}
	switch componentType {
	case cloudv1.ComponentPD:
		if g.tc.ComponentIsSuspended(opv1alpha1.PDMemberType) {
			return 0
		}
		return g.tc.PDStsDesiredReplicas()
	case cloudv1.ComponentTiKV:
		if g.tc.ComponentIsSuspended(opv1alpha1.TiKVMemberType) {
			return 0
		}
		return g.tc.TiKVStsDesiredReplicas()
	case cloudv1.ComponentTiFlash:
		if g.tc.ComponentIsSuspended(opv1alpha1.TiFlashMemberType) {
			return 0
		}
		return g.tc.TiFlashStsDesiredReplicas()
	case cloudv1.ComponentTiDB:
		if g.tc.ComponentIsSuspended(opv1alpha1.TiDBMemberType) {
			return 0
		}
		return g.tc.TiDBStsDesiredReplicas()
	case cloudv1.ComponentTiProxy:
		if g.tc.ComponentIsSuspended(opv1alpha1.TiProxyMemberType) {
			return 0
		}
		return g.tc.TiProxyStsDesiredReplicas()
	}
	return 0
}

func (g *v1) ComponentActualReplicas(componentType cloudv1.ComponentType) int32 {
	if g.tc == nil {
		return 0
	}
	switch componentType {
	case cloudv1.ComponentPD:
		if g.tc.ComponentIsSuspended(opv1alpha1.PDMemberType) {
			return 0
		}
		return g.tc.PDStsActualReplicas()
	case cloudv1.ComponentTiKV:
		if g.tc.ComponentIsSuspended(opv1alpha1.TiKVMemberType) {
			return 0
		}
		return g.tc.TiKVStsActualReplicas()
	case cloudv1.ComponentTiFlash:
		if g.tc.ComponentIsSuspended(opv1alpha1.TiFlashMemberType) {
			return 0
		}
		return g.tc.TiFlashStsActualReplicas()
	case cloudv1.ComponentTiDB:
		if g.tc.ComponentIsSuspended(opv1alpha1.TiDBMemberType) {
			return 0
		}
		return g.tc.TiDBStsActualReplicas()
	case cloudv1.ComponentTiProxy:
		if g.tc.ComponentIsSuspended(opv1alpha1.TiProxyMemberType) {
			return 0
		}
		return g.tc.TiProxyStsActualReplicas()
	}
	return 0
}

func (g *v1) ComponentUpdatedReplicas(componentType cloudv1.ComponentType) int32 {
	if g.tc == nil {
		return 0
	}
	var sts appsv1.StatefulSetStatus
	switch componentType {
	case cloudv1.ComponentPD:
		if g.tc.ComponentIsSuspended(opv1alpha1.PDMemberType) {
			return 0
		}
		if g.tc.Status.PD.StatefulSet != nil {
			sts = *g.tc.Status.PD.StatefulSet
		}
	case cloudv1.ComponentTiKV:
		if g.tc.ComponentIsSuspended(opv1alpha1.TiKVMemberType) {
			return 0
		}
		if g.tc.Status.TiKV.StatefulSet != nil {
			sts = *g.tc.Status.TiKV.StatefulSet
		}
	case cloudv1.ComponentTiFlash:
		if g.tc.ComponentIsSuspended(opv1alpha1.TiFlashMemberType) {
			return 0
		}
		if g.tc.Status.TiFlash.StatefulSet != nil {
			sts = *g.tc.Status.TiFlash.StatefulSet
		}
	case cloudv1.ComponentTiDB:
		if g.tc.ComponentIsSuspended(opv1alpha1.TiDBMemberType) {
			return 0
		}
		if g.tc.Status.TiDB.StatefulSet != nil {
			sts = *g.tc.Status.TiDB.StatefulSet
		}
	case cloudv1.ComponentTiProxy:
		if g.tc.ComponentIsSuspended(opv1alpha1.TiProxyMemberType) {
			return 0
		}
		if g.tc.Status.TiProxy.StatefulSet != nil {
			sts = *g.tc.Status.TiProxy.StatefulSet
		}
	}
	return sts.UpdatedReplicas
}

func (g *v1) ComponentReadyReplicas(componentType cloudv1.ComponentType) int32 {
	if g.tc == nil {
		return 0
	}
	var sts appsv1.StatefulSetStatus
	switch componentType {
	case cloudv1.ComponentPD:
		if g.tc.ComponentIsSuspended(opv1alpha1.PDMemberType) {
			return 0
		}
		if g.tc.Status.PD.StatefulSet != nil {
			sts = *g.tc.Status.PD.StatefulSet
		}
	case cloudv1.ComponentTiKV:
		if g.tc.ComponentIsSuspended(opv1alpha1.TiKVMemberType) {
			return 0
		}
		if g.tc.Status.TiKV.StatefulSet != nil {
			sts = *g.tc.Status.TiKV.StatefulSet
		}
	case cloudv1.ComponentTiFlash:
		if g.tc.ComponentIsSuspended(opv1alpha1.TiFlashMemberType) {
			return 0
		}
		if g.tc.Status.TiFlash.StatefulSet != nil {
			sts = *g.tc.Status.TiFlash.StatefulSet
		}
	case cloudv1.ComponentTiDB:
		if g.tc.ComponentIsSuspended(opv1alpha1.TiDBMemberType) {
			return 0
		}
		if g.tc.Status.TiDB.StatefulSet != nil {
			sts = *g.tc.Status.TiDB.StatefulSet
		}
	case cloudv1.ComponentTiProxy:
		if g.tc.ComponentIsSuspended(opv1alpha1.TiProxyMemberType) {
			return 0
		}
		if g.tc.Status.TiProxy.StatefulSet != nil {
			sts = *g.tc.Status.TiProxy.StatefulSet
		}
	}
	return sts.ReadyReplicas
}

func (g *v1) ComponentAnnotations(componentType cloudv1.ComponentType) map[string]string {
	if g.tc == nil {
		return nil
	}
	switch componentType {
	case cloudv1.ComponentPD:
		return g.tc.Spec.PD.Annotations
	case cloudv1.ComponentTiKV:
		return g.tc.Spec.TiKV.Annotations
	case cloudv1.ComponentTiFlash:
		return g.tc.Spec.TiFlash.Annotations
	case cloudv1.ComponentTiDB:
		return g.tc.Spec.TiDB.Annotations
	case cloudv1.ComponentTiProxy:
		return g.tc.Spec.TiProxy.Annotations
	}
	return nil
}

func (g *v1) ComponentAllInstancesReady(componentType cloudv1.ComponentType) bool {
	if g.tc == nil {
		return false
	}
	switch componentType {
	case cloudv1.ComponentPD:
		return g.tc.Status.PD.Phase == opv1alpha1.NormalPhase && g.tc.PDAllMembersReady()
	case cloudv1.ComponentTiKV:
		return g.tc.Status.TiKV.Phase == opv1alpha1.NormalPhase && g.tc.TiKVAllStoresReady()
	case cloudv1.ComponentTiFlash:
		return g.tc.Status.TiFlash.Phase == opv1alpha1.NormalPhase && g.tc.TiFlashAllStoresReady()
	case cloudv1.ComponentTiDB:
		return g.tc.Status.TiDB.Phase == opv1alpha1.NormalPhase && g.tc.TiDBAllMembersReady()
	case cloudv1.ComponentTiProxy:
		return g.tc.Status.TiProxy.Phase == opv1alpha1.NormalPhase && g.tc.TiProxyAllMembersReady()
	}
	return false
}

func (g *v1) ComponentNodeSelector(componentType cloudv1.ComponentType) map[string]string {
	if g.tc == nil {
		return nil
	}
	return g.tc.ComponentSpec(opv1alpha1.MemberType(componentType)).NodeSelector()
}

func (g *v1) ComponentTerminationGracePeriodSeconds(componentType cloudv1.ComponentType) *int64 {
	if g.tc == nil {
		return nil
	}
	switch componentType {
	case cloudv1.ComponentPD:
		if g.tc.Spec.PD != nil {
			return g.tc.Spec.PD.TerminationGracePeriodSeconds
		}
	case cloudv1.ComponentTiDB:
		if g.tc.Spec.TiDB != nil {
			return g.tc.Spec.TiDB.TerminationGracePeriodSeconds
		}
	case cloudv1.ComponentTiKV:
		if g.tc.Spec.TiKV != nil {
			return g.tc.Spec.TiKV.TerminationGracePeriodSeconds
		}
	case cloudv1.ComponentTiFlash:
		if g.tc.Spec.TiFlash != nil {
			return g.tc.Spec.TiFlash.TerminationGracePeriodSeconds
		}
	case cloudv1.ComponentTiProxy:
		if g.tc.Spec.TiProxy != nil {
			return g.tc.Spec.TiProxy.TerminationGracePeriodSeconds
		}
	case cloudv1.ComponentTiCDC:
		if g.tc.Spec.TiCDC != nil {
			return g.tc.Spec.TiCDC.TerminationGracePeriodSeconds
		}
	}
	return nil
}
