package branch

import (
	"fmt"
	"time"

	"github.com/pingcap/errors"
	"github.com/tidbcloud/aws-shared-provider/pkg/metrics"
	"github.com/tidbcloud/aws-shared-provider/pkg/utils"
	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/tidbcloud/infra-api/pkg/consts"
	corev1 "k8s.io/api/core/v1"
)

const (
	ParentBranchIdAnnKey  = "branch.tidbcloud.com/parent-branch-id"
	ParentTypeAnnKey      = "branch.tidbcloud.com/parent-type"
	ParentTypeCluster     = "cluster"
	ParentTypeBranch      = "branch"
	ParentTimestampAnnKey = "branch.tidbcloud.com/parent-timestamp"
	MainBranchIdAnnKey    = "branch.tidbcloud.com/main-branch-id"
)

// The fast-poll window intentionally starts at branch creation, so it also
// covers the pre-restore steps (tidb cluster creation); a restore that
// finishes before the window closes is detected within ~1s.
const (
	restoreFastPollInterval = 1 * time.Second
	restoreSlowPollInterval = 5 * time.Second
	restoreFastPollWindow   = 60 * time.Second
)

func restorePollInterval(sinceCreation time.Duration) time.Duration {
	if sinceCreation < restoreFastPollWindow {
		return restoreFastPollInterval
	}
	return restoreSlowPollInterval
}

func (r *reconcileContext) createBranch() error {
	metrics.Reclaimer.GetHistogramVec(metrics.ClusterCreationHistogramName, r.branch.Name).WithLabelValues(
		r.branch.Labels[consts.ProjectTagKey],
		r.branch.Labels[consts.TenantTagKey],
		string(metrics.Branch),
		"true",
		string(r.branch.Status.Phase),
	).Observe(time.Since(r.branch.CreationTimestamp.Time).Seconds())

	if r.needResetTidbReplicas() {
		if err := r.updateProgress(cloudv1.BranchPhaseCreating, "ensuring tidb cluster"); err != nil {
			return err
		}
		if err := r.createServerlessTidbCluster(); err != nil {
			return err
		}
	}

	if r.needRestoreKeyspace() {
		if err := r.updateProgress(cloudv1.BranchPhaseCreating, "starting restore keyspace"); err != nil {
			return err
		}
		if err := r.createRestoreKeyspace(); err != nil {
			return err
		}
	}

	if ok, err := r.checkRestoreKeyspace(); err != nil {
		return errors.Annotate(err, "failed to check restore keyspace")
	} else if !ok {
		if r.branch.Status.Reason == "still restoring keyspace" {
			return &utils.RequeueAfterError{
				Cause:        nil,
				RequeueAfter: restorePollInterval(time.Since(r.branch.CreationTimestamp.Time)),
			}
		}
		if err := r.updateProgress(cloudv1.BranchPhaseCreating, "still restoring keyspace"); err != nil {
			return err
		}
		return nil
	}

	if err := r.ensureResourceGroup(); err != nil {
		return err
	}

	if err := r.ensureZoneRedundant(); err != nil {
		return err
	}

	if err := r.ensureRcuConfig(); err != nil {
		return err
	}

	if r.O11yAgentEnabled {
		if err := r.ensureO11yCluster(); err != nil {
			return err
		}
	}

	if err := r.ensureSqlEndpoints(); err != nil {
		return err
	}

	// observe and set branch status to running
	return r.observeCreation()
}

func (r *reconcileContext) observeCreation() error {
	// set branch.status.phase Running
	if err := r.observeTidbClusterStatus(); err != nil {
		return err
	}

	age := time.Since(r.branch.CreationTimestamp.Time)
	r.Info("[branch lifecycle] branch created",
		"branch", r.branch.Name,
		"age", age.Round(10*time.Millisecond),
		"restore_mode", r.BranchRestoreMode,
	)
	r.InfraRecorder.Event(r.branch, corev1.EventTypeNormal, string(r.branch.Status.Phase),
		fmt.Sprintf("reason: %s, age: %s", r.branch.Status.Reason, age.Round(10*time.Millisecond)))

	metrics.Reclaimer.GetHistogramVec(metrics.ClusterCreationHistogramName, r.branch.Name).WithLabelValues(
		r.branch.Labels[consts.ProjectTagKey],
		r.branch.Labels[consts.TenantTagKey],
		string(metrics.Branch),
		"true",
		string(r.branch.Status.Phase),
	).Observe(age.Seconds())

	return nil
}
