package common

import (
	"context"
	"errors"
	"fmt"
	"strconv"
	"time"

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

	"github.com/tidbcloud/eks-provider/controllers/imports/task"
	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/tidbcloud/infra-provider/pkg/deploy"
	"github.com/tidbcloud/infra-provider/pkg/util"
)

const (
	jobDeletionAnnotation = "import.tidbcloud.com/scheduled-deletion-time"
)

type Guardian struct {
	*task.BaseTask
}

func NewGuardian(ctx *task.Context) *Guardian {
	return &Guardian{
		BaseTask: &task.BaseTask{Context: ctx},
	}
}

func (t *Guardian) Name() string {
	return "Guardian"
}

func (t *Guardian) Create(ctx context.Context) deploy.Result {
	// prevent reconciling objects from different regions
	if t.EnvProvider.GetRegion() != t.Config.Network.Region {
		return deploy.Panic(nil)
	}

	// ignore resource that doesn't belong to this region
	if !util.ContainsRegionLabel(t.Import) {
		// ensure the object contains the region label
		if !util.EnsureRegion(t.Import, t.GetRegion()) {
			return deploy.Panic(nil).WithRequeuAfter(0)
		}
	}

	if !controllerutil.ContainsFinalizer(t.Import, task.Finalizer) {
		controllerutil.AddFinalizer(t.Import, task.Finalizer)
		// Require immediate requeue without running other tasks
		return deploy.Panic(nil).WithRequeuAfter(0)
	}

	if t.Import.DeletionTimestamp != nil {
		return deploy.Done()
	}

	switch phase := t.Import.Status.Phase; phase {
	case "", cloudv1.ImportPrepare:
		t.Import.Status.Phase = cloudv1.ImportPrepare
	case cloudv1.ImportFailed, cloudv1.ImportCompleted, cloudv1.ImportRunning:
		// nothing to do
	default:
		return deploy.Panic(fmt.Errorf("unexpected phase %s", phase))
	}

	return deploy.Done()
}

func (t *Guardian) Delete(ctx context.Context) deploy.Result {
	if t.Import.Annotations == nil {
		t.Import.Annotations = make(map[string]string)
	}

	// check if the job has a scheduled deletion time
	deletionTimeStr, ok := t.Import.Annotations[jobDeletionAnnotation]
	if !ok {
		// add deletion time
		var duration time.Duration
		if t.Import.Status.Phase == cloudv1.ImportFailed {
			duration = time.Hour * 1

			if t.Config.ImportConfig.UnnormalPodAliveMinutes > 0 {
				duration = time.Minute * time.Duration(t.Config.ImportConfig.UnnormalPodAliveMinutes)
			}
		} else {
			duration = time.Minute * 10

			if t.Config.ImportConfig.NormalPodAliveMinutes > 0 {
				duration = time.Minute * time.Duration(t.Config.ImportConfig.NormalPodAliveMinutes)
			}
		}
		deletionTimeStr = strconv.FormatInt(time.Now().Add(duration).Unix(), 10)
		t.Import.Annotations[jobDeletionAnnotation] = deletionTimeStr
	}

	deletionTime, err := strconv.ParseInt(deletionTimeStr, 10, 64)
	if err != nil {
		t.Logger.Error(err, "Failed to parse deletion time")
		return deploy.Error(err)
	}

	// check if the deletion time has passed
	if time.Now().Unix() >= deletionTime {
		t.Logger.Info("Deletion time reached, deleting job and pods")
		// Continue with deletion
		return deploy.Done()
	} else {
		// Not time to delete yet
		remaining := time.Until(time.Unix(deletionTime, 0))
		t.Logger.Info("Job and pods preserved until scheduled deletion time",
			"remaining", remaining.String(),
			"deletionTime", time.Unix(deletionTime, 0).String())
		return deploy.Panic(errors.New("job preserved")).WithRequeuAfter(remaining)
	}
}

func (t *Guardian) PostDelete(ctx context.Context) deploy.Result {
	return deploy.Done()
}
