package dataplane

import (
	"context"
	"encoding/base64"

	eksv1beta1 "github.com/crossplane-contrib/provider-aws/apis/eks/v1beta1"
	"helm.sh/helm/v3/pkg/action"
	"k8s.io/client-go/rest"
	"sigs.k8s.io/controller-runtime/pkg/client"

	"github.com/tidbcloud/eks-provider/api/v1alpha1"
	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/tidbcloud/infra-provider/pkg/namer"
	crossplaneutil "github.com/tidbcloud/infra-provider/pkg/util/crossplane"
)

// legacyProvider implements dataplane.Interface for legacy code
// ALL implementations are fully same as the legacy way
type legacyProvider struct {
	ctx            context.Context
	providerType   string
	local          client.Client
	account        *cloudv1.Account
	network        *cloudv1.Network
	client         client.Client
	helmCfg        *action.Configuration
	eksNetworkSpec *v1alpha1.EKSNetworkSpec
	gkeNetworkSpec *v1alpha1.GKENetworkSpec
	eks            *eksv1beta1.Cluster
}

func NewLegacy(
	ctx context.Context,
	providerType string,
	local client.Client,
	client client.Client,
	helmCfg *action.Configuration,
	account *cloudv1.Account,
	network *cloudv1.Network,
	eksNetworkSpec *v1alpha1.EKSNetworkSpec,
	gkeNetworkSpec *v1alpha1.GKENetworkSpec,
	eks *eksv1beta1.Cluster,
) EnvProvider {
	return &legacyProvider{
		ctx:            ctx,
		providerType:   providerType,
		local:          local,
		client:         client,
		helmCfg:        helmCfg,
		account:        account,
		network:        network,
		eksNetworkSpec: eksNetworkSpec,
		gkeNetworkSpec: gkeNetworkSpec,
		eks:            eks,
	}
}

func (p *legacyProvider) GetClient() client.Client {
	return p.client
}

func (p *legacyProvider) GetHelmConfig() *action.Configuration {
	return p.helmCfg
}

func (p *legacyProvider) GetRestConfig() *rest.Config {
	return nil
}

func (p *legacyProvider) GetRegion() string {
	return p.network.Spec.Region
}

func (p *legacyProvider) GetClusterName() string {
	return namer.LegacyBuildClusterNameByNetwork(p.network)
}

func (p *legacyProvider) GetClusterEndpoint() string {
	switch p.providerType {
	case cloudv1.ProviderTypeEKS:
		return p.eks.Status.AtProvider.Endpoint
	case cloudv1.ProviderTypeGKE:
		panic("it's not called in legacy code")
	}

	panic("invalid provider type")
}

func (p *legacyProvider) GetClusterCA() []byte {
	switch p.providerType {
	case cloudv1.ProviderTypeEKS:
		ca, err := base64.StdEncoding.DecodeString(p.eks.Status.AtProvider.CertificateAuthorityData)
		if err != nil {
			return nil
		}
		return ca
	case cloudv1.ProviderTypeGKE:
		return nil
	}

	panic("it's not called in legacy code")
}

func (p *legacyProvider) GetAccount() string {
	switch p.providerType {
	case cloudv1.ProviderTypeEKS:
		return p.account.Spec.AWS.AccountID
	case cloudv1.ProviderTypeGKE:
		return p.account.Spec.GCP.ProjectID
	}

	panic("invalid provider type")
}

func (p *legacyProvider) GetNetwork() string {
	switch p.providerType {
	case cloudv1.ProviderTypeEKS:
		return p.eksNetworkSpec.VpcId
	case cloudv1.ProviderTypeGKE:
		return p.gkeNetworkSpec.Network
	}
	panic("invalid provider type")
}

func (p *legacyProvider) AWS() AWSInterface {
	return p
}

func (p *legacyProvider) GCP() GCPInterface {
	return p
}

func (p *legacyProvider) GetEKSSecurityGroup() string {
	return p.eks.Status.AtProvider.ResourcesVpcConfig.ClusterSecurityGroupID
}

func (p *legacyProvider) GetNodeSubnets(zones ...string) []string {
	return crossplaneutil.LegacyGetNodeSubnetsByZonesFromCR(p.ctx, p.local, namer.UniqueIdentifier(p.network.ObjectMeta), zones)
}
