package gke

import (
	"context"
	"fmt"
	"time"

	"github.com/crossplane-contrib/provider-gcp/apis/compute/v1beta1"
	xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
	"github.com/crossplane/crossplane-runtime/pkg/meta"
	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/tidbcloud/infra-provider/pkg/deploy"
	"github.com/tidbcloud/infra-provider/pkg/k8s"
	"github.com/tidbcloud/infra-provider/pkg/util"
	"github.com/tidbcloud/infra-provider/regional-server/api/v1alpha1"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers/vpc/task"
	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/utils/pointer"
)

type VPC struct {
	*task.BaseTask
}

func NewVPC(ctx *task.Context) *VPC {
	return &VPC{&task.BaseTask{Context: ctx}}
}

var _ deploy.Task = &VPC{}

// Name implements Task interface.
func (t *VPC) Name() string {
	return "VPC"
}

// Create implements Task interface.
// Note: return deploy.Panic once VPC is not set up correctly.
func (t *VPC) Create(ctx context.Context) deploy.Result {
	tags := cloudv1.TagsFromLabels(t.VPC.Labels)
	t.BeginAt = util.Pointer(time.Now())
	// no need to create network for migrated resource.
	if t.VPC.Labels[v1alpha1.IsLegacyKey] == "true" {
		return deploy.Done()
	}

	vpc := v1beta1.Network{
		ObjectMeta: metav1.ObjectMeta{
			Name: t.GetNetworkName(),
		},
	}

	_, err := controllerutil.CreateOrUpdate(ctx, t.Local, &vpc, func() error {
		vpc.Labels = t.Namer.Labels()

		vpc.Spec = v1beta1.NetworkSpec{
			ResourceSpec: xpv1.ResourceSpec{
				ProviderConfigReference: &xpv1.Reference{
					Name: t.ProviderConfigName(),
				},
			},
			ForProvider: v1beta1.NetworkParameters{
				AutoCreateSubnetworks: pointer.Bool(false),
				Description:           pointer.String(fmt.Sprintf("Tags: %v", tags)),
				RoutingConfig: &v1beta1.NetworkRoutingConfig{
					RoutingMode: "REGIONAL",
				},
			},
		}
		// if t.IsMigrate {
		// 	taskutils.SetExternalName(&vpc, t.GKENetworkSpec.Network)
		// 	// Don't delete the actual VPC resource
		// 	// It should be managed by Gardener terraform
		// 	vpc.Spec.ResourceSpec.DeletionPolicy = xpv1.DeletionOrphan
		// 	// Updating this field is not supported by GCP
		// 	vpc.Spec.ForProvider.Description = nil
		// }

		return nil
	})

	if err != nil {
		return deploy.Panic(err).WithRequeuAfter(time.Second * 30)
	}

	return deploy.Done()
}

func (t *VPC) PostCreate(ctx context.Context) deploy.Result {
	vpc := &v1beta1.Network{}

	if err := t.Local.Get(ctx, types.NamespacedName{Name: t.GetNetworkName()}, vpc); err != nil {
		return deploy.Panic(err)
	}

	condition := vpc.Status.ConditionedStatus.GetCondition(xpv1.TypeReady)
	if condition.Status != corev1.ConditionTrue {
		return deploy.Panic(fmt.Errorf("vpc status not ready: %v", condition.Reason)).WithRequeuAfter(time.Second * 30)
	}

	if err := t.setVPCStatus(ctx, vpc); err != nil {
		return deploy.Panic(err)
	}

	return deploy.Done()
}

func (t *VPC) setVPCStatus(ctx context.Context, vpc *v1beta1.Network) error {
	vpcID := meta.GetExternalName(vpc)
	t.VPC.Status.VPC = v1alpha1.VPCRes{
		VPCID: vpcID,
		Name:  vpc.Name,
	}
	// this update won't trigger reconcile
	return t.VPCRW.UpdateStatus(ctx, t.VPC)
}

func (t *VPC) Delete(ctx context.Context) deploy.Result {
	vpc := &v1beta1.Network{
		ObjectMeta: metav1.ObjectMeta{
			Name: t.GetNetworkName(),
		},
	}
	if err := k8s.DeleteObject(ctx, t.Local, vpc); err != nil {
		return deploy.Error(err)
	}
	return deploy.Done()
}

func (t *VPC) PostDelete(ctx context.Context) deploy.Result {
	return deploy.ErrorOrDone(k8s.GetDeletedObject(ctx, t.Local, types.NamespacedName{Name: t.GetNetworkName()}, &v1beta1.Network{}))
}
