package vpcpeering

import (
	"context"

	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"

	"github.com/tidbcloud/infra-provider/regional-server/api/v1alpha1"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers/vpcpeering/task"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/deploy"
)

func updateCreate(ctx context.Context, t *task.Context, results []deploy.Result) (res ctrl.Result, err error) {
	logger := ctrl.Log.WithValues("vpcpeering", client.ObjectKeyFromObject(t.VPCPeering).String())

	var retriableErr error
	res, err, retriableErr = deploy.MinRequeue(results)
	if err != nil {
		logger.Info("deploy create encounters error", "unretriable", err)
		t.VPCPeering.Status.Reason = err.Error()
	} else if retriableErr != nil {
		// FIXME(shenjun): There is bug in the deploy.MinRequeue and deploy.Panic(nil).WithRequeuAfter(0).
		// I dont want to fix it since it's a infra-provider's package which is depended everywhere.
		// for retriable error, just log, no need to change status
		logger.Info("deploy create encounters error", "retriable", retriableErr)
	}
	if err := update(ctx, t.Local, t.VPCPeering); err != nil {
		return res, err
	}
	return res, err
}

func updateDelete(ctx context.Context, t *task.Context, results []deploy.Result) (res ctrl.Result, err error) {
	logger := ctrl.Log.WithValues("vpcpeering", client.ObjectKeyFromObject(t.VPCPeering).String())

	var deleted bool
	var retriableErr error
	res, err, retriableErr = deploy.MinRequeue(results)
	if err == nil && retriableErr == nil {
		deleted = true
	} else {
		logger.Info("deploy delete enconters error",
			"retriable", retriableErr,
			"unretriable", err,
		)
	}
	if deleted {
		controllerutil.RemoveFinalizer(t.VPCPeering, v1alpha1.FinalizerVPCPeering)
	}
	// no need to update status for deleting case.
	if err := t.Local.Update(ctx, t.VPCPeering); err != nil {
		logger.Error(err, "update vpcpeering failed")
		return res, err
	}
	return
}

func update(ctx context.Context, cli client.Client, peering *v1alpha1.VPCPeering) error {
	logger := ctrl.Log.WithValues("vpcpeering", client.ObjectKeyFromObject(peering).String())
	status := peering.Status.DeepCopy()
	if err := cli.Update(ctx, peering); err != nil {
		logger.Error(err, "update vpcpeering failed")
		return err
	}
	peering.Status = *status
	if err := cli.Status().Update(ctx, peering); err != nil {
		logger.Error(err, "update vpcpeering status failed")
		return err
	}
	return nil
}
