package serverless

import (
	"context"
	"time"

	observabilitypingcapcomv1 "github.com/tidbcloud/observability/api/regionalserver/resourceworker/v1"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/log"

	"github.com/tidbcloud/observability/pkg/common/constants"
	"github.com/tidbcloud/observability/pkg/common/deploy"
	mycli "github.com/tidbcloud/observability/pkg/mysql/client"
	"github.com/tidbcloud/observability/pkg/resourceworker/controllers/serverless/task"
)

func updateCreate(ctx context.Context, t *task.Context, results []deploy.Result) (res ctrl.Result, err error) {
	var retriableErr error
	res, err, retriableErr = deploy.MinRequeue(results)
	if err == nil && retriableErr == nil {
		if t.Cluster.Status.Phase != constants.ACTIVE {
			age := time.Since(t.Cluster.CreationTimestamp.Time)
			key := "first-pass-when-creating.cloud.pingcap.com/All"
			if _, ok := t.Cluster.Annotations[key]; !ok {
				if t.Cluster.Annotations == nil {
					t.Cluster.Annotations = make(map[string]string)
				}
				t.Cluster.Annotations[key] = age.String()
			}

			log.FromContext(ctx).Info("mark o11y become running when creating o11y", "sinceCreate", age)
			t.Cluster.Status.Phase = constants.ACTIVE
		}
	}

	if err != nil {
		log.FromContext(ctx).Error(err, "failed in MinRequeue for serverless cluster")
	}

	if retriableErr != nil {
		log.FromContext(ctx).Info("retriable error", "error", retriableErr)
	}

	if t.IsClusterChanged() {
		return res, update(ctx, t.Local, t.Cluster)
	}

	return res, err
}

func updateDelete(ctx context.Context, t *task.Context, results []deploy.Result) (res ctrl.Result, err error) {
	var deleted bool
	var retriableErr error
	res, err, retriableErr = deploy.MinRequeue(results)
	if err == nil && retriableErr == nil {
		deleted = true
	}
	t.Cluster.Status.Phase = constants.DELETING
	if err != nil {
		log.FromContext(ctx).Error(err, "failed in MinRequeue for serverless cluster")
	}
	if retriableErr != nil {
		log.FromContext(ctx).Info("retriable error", "error", retriableErr)
	}

	if deleted {
		// delete in mysql client
		return res, t.Local.Delete(ctx, t.Cluster, mycli.WithForce(func() {}))
	}

	if t.IsClusterChanged() {
		return res, update(ctx, t.Local, t.Cluster)
	}
	return res, err
}

func update(ctx context.Context, cli client.Client, cluster *observabilitypingcapcomv1.Cluster) error {
	clusterStatus := cluster.Status.DeepCopy()
	if err := cli.Update(ctx, cluster); err != nil {
		return err
	}
	cluster.Status = *clusterStatus
	return cli.Status().Update(ctx, cluster)
}
