package common

import (
	"testing"
	"time"

	"github.com/stretchr/testify/require"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
)

func newUOForProgressTest() *cloudv1.UpgradeOperation {
	return &cloudv1.UpgradeOperation{
		Status: cloudv1.UpgradeOperationStatus{
			CurrentStep: &cloudv1.Step{
				Options: map[string]string{},
			},
		},
	}
}

func TestTrackProgress_phaseAdvance(t *testing.T) {
	uo := newUOForProgressTest()
	now := time.Date(2026, 6, 12, 10, 0, 0, 0, time.UTC)
	changed := TrackProgress(uo, PhaseWaitNodesReady, 1, 3, now)
	require.True(t, changed)
	require.Len(t, uo.Status.CurrentStep.Phases, 1)
	active := uo.Status.CurrentStep.ActivePhase()
	require.Equal(t, PhaseWaitNodesReady, active.Name)
	require.Equal(t, "wait-nodes-ready=1/3", active.ProgressInfo)
	require.Equal(t, 1, active.MaxDone)
	require.Equal(t, now, active.LastProgressTime.Time)
	require.Equal(t, now, uo.Status.CurrentStep.LastProgressTime.Time)
}

func TestTrackProgress_samePhaseCountAdvance(t *testing.T) {
	uo := newUOForProgressTest()
	t0 := time.Date(2026, 6, 12, 10, 0, 0, 0, time.UTC)
	require.True(t, TrackProgress(uo, PhaseWaitNodesReady, 1, 3, t0))

	t1 := t0.Add(time.Minute)
	changed := TrackProgress(uo, PhaseWaitNodesReady, 2, 3, t1)
	require.True(t, changed)
	active := uo.Status.CurrentStep.ActivePhase()
	require.Equal(t, 2, active.MaxDone)
	require.Equal(t, t1, uo.Status.CurrentStep.LastProgressTime.Time)
	require.Len(t, uo.Status.CurrentStep.Phases, 1)
}

func TestTrackProgress_regressionDoesNotUpdatePhases(t *testing.T) {
	uo := newUOForProgressTest()
	t0 := time.Date(2026, 6, 12, 10, 0, 0, 0, time.UTC)
	require.True(t, TrackProgress(uo, PhaseWaitNodesReady, 3, 3, t0))
	t1 := t0.Add(10 * time.Minute)
	require.True(t, TrackProgress(uo, PhaseRestartStorage, 1, 5, t1))

	t2 := t1.Add(5 * time.Minute)
	changed := TrackProgress(uo, PhaseWaitNodesReady, 2, 3, t2)
	require.False(t, changed)
	require.Len(t, uo.Status.CurrentStep.Phases, 2)
	require.NotNil(t, uo.Status.CurrentStep.Phases[0].EndTime)
	active := uo.Status.CurrentStep.ActivePhase()
	require.Equal(t, PhaseRestartStorage, active.Name)
	require.Equal(t, 1, active.Done)
	require.Equal(t, t1, uo.Status.CurrentStep.LastProgressTime.Time)
}

func TestTrackProgress_countDecreaseDoesNotRefreshTime(t *testing.T) {
	uo := newUOForProgressTest()
	t0 := time.Date(2026, 6, 12, 10, 0, 0, 0, time.UTC)
	require.True(t, TrackProgress(uo, PhaseWaitAllPodsMigrated, 10, 10, t0))
	t1 := t0.Add(time.Minute)
	changed := TrackProgress(uo, PhaseWaitAllPodsMigrated, 8, 10, t1)
	require.True(t, changed)
	active := uo.Status.CurrentStep.ActivePhase()
	require.Equal(t, 10, active.MaxDone)
	require.Equal(t, t0, uo.Status.CurrentStep.LastProgressTime.Time)
}

func TestTrackProgress_recoverAfterRegression(t *testing.T) {
	uo := newUOForProgressTest()
	t0 := time.Date(2026, 6, 12, 10, 0, 0, 0, time.UTC)
	require.True(t, TrackProgress(uo, PhaseWaitNodesReady, 3, 3, t0))
	t1 := t0.Add(time.Minute)
	require.True(t, TrackProgress(uo, PhaseRestartStorage, 2, 5, t1))
	require.False(t, TrackProgress(uo, PhaseWaitNodesReady, 2, 3, t1.Add(time.Minute)))

	t2 := t1.Add(30 * time.Minute)
	changed := TrackProgress(uo, PhaseRestartStorage, 3, 5, t2)
	require.True(t, changed)
	require.Equal(t, t2, uo.Status.CurrentStep.LastProgressTime.Time)
}

func TestStep_ResetPhases(t *testing.T) {
	step := &cloudv1.Step{
		LastProgressTime: &metav1.Time{Time: time.Now()},
		Phases: []cloudv1.StepPhase{
			{Name: PhaseWaitNodesReady},
		},
	}
	step.ResetPhases()
	require.Empty(t, step.Phases)
	require.Nil(t, step.LastProgressTime)
}
