/*
Copyright 2024.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package task

import (
	"context"
	"testing"

	"github.com/go-logr/logr/testr"
	"github.com/stretchr/testify/assert"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	o11yv1 "github.com/tidbcloud/observability/api/regionalserver/resourceworker/v1"
	"github.com/tidbcloud/observability/pkg/common/constants"
)

func newTestContext(t *testing.T, phase constants.ResourceStatus, deletionTimestamp *metav1.Time) *Context {
	task := &o11yv1.IntegrationTask{
		ObjectMeta: metav1.ObjectMeta{
			Name:              "test-task",
			Namespace:         "default",
			DeletionTimestamp: deletionTimestamp,
			// The finalizer is added on the first reconcile; seed it here so
			// Create/Delete exercise the phase-transition logic instead of the
			// one-shot finalizer-add requeue branch.
			Finalizers: []string{Finalizer},
		},
		Status: o11yv1.IntegrationTaskStatus{
			Phase: phase,
		},
	}

	return &Context{
		Task:   task,
		Logger: testr.New(t),
		Client: nil, // Guardian doesn't need K8s client for phase transitions
	}
}

func TestGuardian_Create_EmptyPhase(t *testing.T) {
	ctx := newTestContext(t, "", nil)
	guardian := NewGuardian(ctx)

	result := guardian.Create(context.Background())

	// Should set phase to CREATING
	assert.Equal(t, constants.CREATING, ctx.Task.Status.Phase)
	assert.True(t, result.IsDoneState())
	assert.Nil(t, result.Error())
}

func TestGuardian_Create_ActivePhase(t *testing.T) {
	ctx := newTestContext(t, constants.ACTIVE, nil)
	guardian := NewGuardian(ctx)

	result := guardian.Create(context.Background())

	// Phase should remain ACTIVE
	assert.Equal(t, constants.ACTIVE, ctx.Task.Status.Phase)
	assert.True(t, result.IsDoneState())
	assert.Nil(t, result.Error())
}

func TestGuardian_Create_UnexpectedPhase(t *testing.T) {
	// Test with DELETING phase - should panic with error
	ctx := newTestContext(t, constants.DELETING, nil)
	guardian := NewGuardian(ctx)

	result := guardian.Create(context.Background())

	// Should return Panic with error
	assert.False(t, result.IsDoneState())
	err := result.Error()
	assert.NotNil(t, err)
	assert.Contains(t, err.Error(), "unexpected integration task phase during creation")
	assert.Contains(t, err.Error(), string(constants.DELETING))
}

func TestGuardian_Delete_ActivePhase(t *testing.T) {
	now := metav1.Now()
	ctx := newTestContext(t, constants.ACTIVE, &now)
	guardian := NewGuardian(ctx)

	result := guardian.Delete(context.Background())

	// Should set phase to DELETING
	assert.Equal(t, constants.DELETING, ctx.Task.Status.Phase)
	// Should return Panic with RequeueAfter=0 (immediate requeue)
	assert.False(t, result.IsDoneState())
}

func TestGuardian_Delete_DeletingPhase(t *testing.T) {
	now := metav1.Now()
	ctx := newTestContext(t, constants.DELETING, &now)
	guardian := NewGuardian(ctx)

	result := guardian.Delete(context.Background())

	// Phase should remain DELETING
	assert.Equal(t, constants.DELETING, ctx.Task.Status.Phase)
	assert.True(t, result.IsDoneState())
	assert.Nil(t, result.Error())
}

func TestGuardian_Delete_UnexpectedPhase(t *testing.T) {
	// Test with UPDATING phase - should panic with error
	now := metav1.Now()
	ctx := newTestContext(t, constants.UPDATING, &now)
	guardian := NewGuardian(ctx)

	result := guardian.Delete(context.Background())

	// Should return Panic with error
	assert.False(t, result.IsDoneState())
	err := result.Error()
	assert.NotNil(t, err)
	assert.Contains(t, err.Error(), "unexpected integration task phase during deletion")
	assert.Contains(t, err.Error(), string(constants.UPDATING))
}
