package provider

import (
	"context"

	opv1alpha1 "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
	"sigs.k8s.io/controller-runtime/pkg/client"

	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/tidbcloud/infra-api/pkg/consts"
	"github.com/tidbcloud/infra-api/pkg/gardener"
	"github.com/tidbcloud/infra-api/pkg/gitops"
	"github.com/tidbcloud/infra-api/pkg/nodegroup"
)

var _ Backup = &gardenerBRProvider{}

type gardenerBRProvider struct {
	gardenerEnv *gardener.GardEnvironment
	infraCli    client.Client
}

func NewBRGardenerProvider(gardenerEnv *gardener.GardEnvironment, infraCli client.Client) *gardenerBRProvider {
	return &gardenerBRProvider{
		gardenerEnv: gardenerEnv,
		infraCli:    infraCli,
	}
}

func (p *gardenerBRProvider) BuildBackup(b *cloudv1.Backup, c *cloudv1.Cluster) (*opv1alpha1.Backup, error) {
	return nil, errUnimplemented
}

func (p *gardenerBRProvider) CreateBRNode(
	ctx context.Context,
	network *cloudv1.Network,
	ngSpec *cloudv1.NodegroupSpec) error {
	networkStatus, err := cloudv1.GardenerNetworkStatusFromNetwork(network)
	if err != nil {
		return err
	}
	// add required labels for gardener worker
	// see more in https://github.com/tidbcloud/extension-provider-gcp/blob/92e4fc1309fd3025ca7aee33b0d08b042cdbbb57/pkg/controller/worker/machines.go#L141
	newSpec := ngSpec.DeepCopy()
	newSpec.Labels[nodegroup.GardenerWorkerPrefixLabelName] = newSpec.Name
	newSpec.MachineSpec.MachineImage = gitops.NodeImage
	if newSpec.MachineSpec.MachineArch == consts.ARM64ArchName {
		newSpec.MachineSpec.MachineImageVersion = gitops.Aarch64ImageVersion
	} else {
		newSpec.MachineSpec.MachineImageVersion = gitops.X86ImageVersion
	}
	if network.Spec.Provider == consts.ProviderTypeAWS {
		newSpec.MachineSpec.SystemVolumeType = consts.AWSVolumeTypeGP2
	} else {
		newSpec.MachineSpec.SystemVolumeType = consts.GCPVolumeTypePDStandard
	}
	shootName := networkStatus.ShootName
	ngs := &nodegroup.Set{Name: ngSpec.Name, NodeGroups: []cloudv1.NodegroupSpec{*newSpec}}
	if _, err := p.gardenerEnv.ApplyNodeGroupSet(ctx, shootName, ngs, gardener.NodeGroupPolicyScaleOnly); err != nil {
		return err
	}
	return nil
}

func (p *gardenerBRProvider) DeleteBRNode(
	ctx context.Context,
	network *cloudv1.Network,
	ngSpecName string) error {
	networkStatus, err := cloudv1.GardenerNetworkStatusFromNetwork(network)
	if err != nil {
		return err
	}
	shootName := networkStatus.ShootName
	ngs := &nodegroup.Set{Name: ngSpecName} // empty node group means delete
	if _, err := p.gardenerEnv.ApplyNodeGroupSet(ctx, shootName, ngs, gardener.NodeGroupPolicyScaleOnly); err != nil {
		return err
	}
	return nil
}

func (p *gardenerBRProvider) GetBackupClient(ctx context.Context, cluster *cloudv1.Backup) (client.Client, error) {
	return nil, errUnimplemented
}

func (p *gardenerBRProvider) GetBackupNamespace(context.Context, *cloudv1.Backup) (string, error) {
	return "", errUnimplemented
}

func (p *gardenerBRProvider) BuildRestore(
	restore *cloudv1.Restore, cluster *cloudv1.Cluster) (*opv1alpha1.Restore, error) {
	return nil, errUnimplemented
}

func (p *gardenerBRProvider) PostRestoreFinished(ctx context.Context, restore *cloudv1.Restore) error {
	return nil
}

func (p *gardenerBRProvider) GetClient(ctx context.Context, cluster *cloudv1.Cluster) (client.Client, error) {
	return nil, errUnimplemented
}

func (p *gardenerBRProvider) GetKubernetesStatus(cluster *cloudv1.Cluster) (*cloudv1.KubernetesStatus, error) {
	return nil, errUnimplemented
}

func (p *gardenerBRProvider) IsDPCompatMode() bool {
	// In gardener, the CR will only in api server,no need get CR from new path, so we always return false
	return false
}
