package cluster

import (
	"fmt"
	"strconv"
	"time"

	"github.com/pingcap/errors"
	"github.com/tidbcloud/aws-shared-provider/pkg/metrics"
	"github.com/tidbcloud/aws-shared-provider/pkg/types"
	infra "github.com/tidbcloud/tidb-management-service/api/serverless_infra"
)

const (
	defaultWaitInterval = 2 * time.Second
)

func (r *reconcileContext) createCluster() error {
	metrics.Reclaimer.GetHistogramVec(metrics.ClusterCreationHistogramName, r.cluster.Name).WithLabelValues(
		r.cluster.TagsProject,
		r.cluster.TagsTenant,
		string(metrics.Cluster),
		strconv.FormatBool(r.isClusterCreateFromRestore()),
		string(r.cluster.Status.Phase),
	).Observe(time.Since(r.cluster.CreationTimestamp.Time).Seconds())

	if r.needResetTidbCluster() {
		if err := r.updateProgress(infra.ClusterPhaseCreating, "ensuring tidb cluster"); err != nil {
			return err
		}
		if err := r.createServerlessTidbCluster(); err != nil {
			return err
		}
	}

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

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

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

	if r.isClusterCreateFromRestore() {
		restoreFinished, err := r.ensureRestore()
		if err != nil {
			return err
		}
		if !restoreFinished {
			return nil
		}
	}

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

	return r.observeCreation()
}

func (r *reconcileContext) isClusterCreateFromRestore() bool {
	return r.cluster.Annotations[types.CreateFromRestore] == "true"
}

func (r *reconcileContext) ensureRestore() (bool, error) {
	restoreStatus := r.cluster.Annotations[types.AnnoKeyRestoreToNewCluster]
	// continue the reconciler when restore has been finished
	if restoreStatus == types.AnnoKeyRestoreToNewClusterFinish {
		return true, nil
	}
	// set the status to wait to notify the control plane to start the restore
	if restoreStatus == "" {
		if err := r.InfraClusterCli.Patch(r.ctx, map[string]any{
			fmt.Sprintf("annotations.'%s'", types.AnnoKeyRestoreToNewCluster): types.AnnoKeyRestoreToNewClusterWait,
		}, r.cluster); err != nil {
			return false, errors.Annotate(err, "failed to update restore new cluster annotation to wait")
		}
	}
	return false, nil
}

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

	age := time.Since(r.cluster.CreationTimestamp.Time)
	r.Info("update cluster status event", "phase", r.cluster.Status.Phase, "reason", r.cluster.Status.Reason, "age", age.Round(10*time.Millisecond))

	metrics.Reclaimer.GetHistogramVec(metrics.ClusterCreationHistogramName, r.cluster.Name).WithLabelValues(
		r.cluster.TagsProject,
		r.cluster.TagsTenant,
		string(metrics.Cluster),
		strconv.FormatBool(r.isClusterCreateFromRestore()),
		string(r.cluster.Status.Phase),
	).Observe(age.Seconds())

	return nil
}
