package agentbuild

import (
	"context"
	"testing"
	"time"

	gkecontainerv1beta1 "github.com/crossplane-contrib/provider-gcp/apis/container/v1beta1"
	"github.com/crossplane/crossplane-runtime/pkg/meta"
	"github.com/stretchr/testify/assert"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"sigs.k8s.io/controller-runtime/pkg/client"

	"github.com/tidbcloud/observability/agent-control-server/pkg/testkits"
)

const testNodePoolName = "np-test"

func makeTrackedNodePool(pending, failed time.Time) *gkecontainerv1beta1.NodePool {
	np := &gkecontainerv1beta1.NodePool{
		ObjectMeta: metav1.ObjectMeta{Name: testNodePoolName},
	}
	if !pending.IsZero() {
		meta.SetExternalCreatePending(np, pending)
	}
	if !failed.IsZero() {
		meta.SetExternalCreateFailed(np, failed)
	}
	return np
}

func TestClearStalledExternalCreate(t *testing.T) {
	now := time.Now()

	tests := []struct {
		name           string
		setupResources []client.Object
		wantCleared    bool
		verify         func(t *testing.T, cli client.Client)
	}{
		{
			name:        "managed resource not found",
			wantCleared: false,
		},
		{
			name:           "no tracking annotations",
			setupResources: []client.Object{makeTrackedNodePool(time.Time{}, time.Time{})},
			wantCleared:    false,
		},
		{
			name:           "pending newest but creation may still be in flight",
			setupResources: []client.Object{makeTrackedNodePool(now.Add(-time.Minute), now.Add(-2*time.Minute))},
			wantCleared:    false,
			verify: func(t *testing.T, cli client.Client) {
				var np gkecontainerv1beta1.NodePool
				assert.NoError(t, cli.Get(context.Background(), client.ObjectKey{Name: testNodePoolName}, &np))
				assert.False(t, meta.GetExternalCreatePending(&np).IsZero())
			},
		},
		{
			name:           "pending newest and stalled",
			setupResources: []client.Object{makeTrackedNodePool(now.Add(-11*time.Minute), now.Add(-12*time.Minute))},
			wantCleared:    true,
			verify: func(t *testing.T, cli client.Client) {
				var np gkecontainerv1beta1.NodePool
				assert.NoError(t, cli.Get(context.Background(), client.ObjectKey{Name: testNodePoolName}, &np))
				assert.Zero(t, meta.GetExternalCreatePending(&np))
				assert.False(t, meta.GetExternalCreateFailed(&np).IsZero())
			},
		},
		{
			name:           "failed newer than pending, crossplane retrying",
			setupResources: []client.Object{makeTrackedNodePool(now.Add(-12*time.Minute), now.Add(-11*time.Minute))},
			wantCleared:    false,
			verify: func(t *testing.T, cli client.Client) {
				var np gkecontainerv1beta1.NodePool
				assert.NoError(t, cli.Get(context.Background(), client.ObjectKey{Name: testNodePoolName}, &np))
				assert.False(t, meta.GetExternalCreatePending(&np).IsZero())
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cli := testkits.BuildClient(tt.setupResources...)
			cleared, err := ClearStalledExternalCreate(context.Background(), cli,
				&gkecontainerv1beta1.NodePool{ObjectMeta: metav1.ObjectMeta{Name: testNodePoolName}})
			assert.NoError(t, err)
			assert.Equal(t, tt.wantCleared, cleared)
			if tt.verify != nil {
				tt.verify(t, cli)
			}
		})
	}
}
