package service

import (
	"context"
	"testing"
	"time"

	"github.com/sandbox0-ai/sandbox0/manager/pkg/controller"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.uber.org/zap"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes/fake"
	ktesting "k8s.io/client-go/testing"
)

func TestNewSandboxServiceInitializesCtldClientDefaults(t *testing.T) {
	svc := NewSandboxService(nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, SandboxServiceConfig{CtldEnabled: true}, zap.NewNop(), nil)
	assert.Equal(t, 8095, svc.config.CtldPort)
	assert.Equal(t, 15*time.Second, svc.config.CtldClientTimeout)
	require.NotNil(t, svc.ctldClient)
	assert.Equal(t, 15*time.Second, svc.ctldClient.httpClient.Timeout)
}

func TestCheckpointPauseRequiresCtldEnabled(t *testing.T) {
	pod := &corev1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "sandbox-1",
			Namespace: "default",
			Labels: map[string]string{
				controller.LabelSandboxID: "sandbox-1",
			},
		},
		Status: corev1.PodStatus{Phase: corev1.PodRunning},
	}
	store := &memorySandboxStore{records: map[string]*SandboxRecord{
		"sandbox-1": {
			ID:     "sandbox-1",
			TeamID: "team-1",
			Status: SandboxStatusRunning,
		},
	}}
	svc := &SandboxService{
		k8sClient:    fake.NewSimpleClientset(pod),
		podLister:    newTestPodLister(t, pod),
		sandboxStore: store,
		config:       SandboxServiceConfig{},
		logger:       zap.NewNop(),
		clock:        systemTime{},
	}

	_, err := svc.PauseSandbox(context.Background(), "sandbox-1")
	require.ErrorIs(t, err, ErrSandboxCheckpointRequiresCtld)
}

func TestCtldAddressForPodUsesHostIPWithoutK8sClient(t *testing.T) {
	pod := &corev1.Pod{
		ObjectMeta: metav1.ObjectMeta{Name: "sandbox-1", Namespace: "default"},
		Status:     corev1.PodStatus{HostIP: "10.24.15.221"},
	}
	svc := &SandboxService{
		config: SandboxServiceConfig{CtldPort: 8095},
	}

	address, err := svc.ctldAddressForPod(context.Background(), pod)
	require.NoError(t, err)
	assert.Equal(t, "http://10.24.15.221:8095", address)
}

func TestCtldAddressForPodUsesNodeCacheBeforeLiveGet(t *testing.T) {
	pod := &corev1.Pod{
		ObjectMeta: metav1.ObjectMeta{Name: "sandbox-1", Namespace: "default"},
		Spec:       corev1.PodSpec{NodeName: "node-a"},
	}
	k8sClient := fake.NewSimpleClientset()
	svc := &SandboxService{
		k8sClient:  k8sClient,
		nodeLister: newClaimTestNodeLister(t, newClaimTestNode("node-a", "10.0.0.1")),
		config:     SandboxServiceConfig{CtldPort: 8095},
	}

	address, err := svc.ctldAddressForPod(context.Background(), pod)
	require.NoError(t, err)
	assert.Equal(t, "http://10.0.0.1:8095", address)
	assert.Empty(t, k8sClient.Actions())
}

func TestTerminateSandboxDoesNotRequestPowerStateBeforeDelete(t *testing.T) {
	pod := &corev1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "sandbox-1",
			Namespace: "default",
			Labels: map[string]string{
				controller.LabelSandboxID: "sandbox-1",
			},
			Annotations: map[string]string{
				controller.AnnotationPaused: "true",
			},
		},
		Spec: corev1.PodSpec{NodeName: "node-1"},
	}
	k8sClient := fake.NewSimpleClientset(pod)
	svc := &SandboxService{
		k8sClient: k8sClient,
		podLister: newTestPodLister(t, pod),
		config:    SandboxServiceConfig{CtldEnabled: true, CtldPort: 8095},
		logger:    zap.NewNop(),
	}

	err := svc.TerminateSandbox(context.Background(), "sandbox-1")
	require.NoError(t, err)

	actions := k8sClient.Actions()
	assert.Equal(t, "delete", actions[len(actions)-1].GetVerb())
	for _, action := range actions {
		updateAction, ok := action.(ktesting.UpdateAction)
		if !ok || action.GetSubresource() != "" {
			continue
		}
		updatedPod, _ := updateAction.GetObject().(*corev1.Pod)
		require.NotNil(t, updatedPod)
		assert.Empty(t, updatedPod.Annotations[controller.AnnotationPowerStateDesired])
		assert.Empty(t, updatedPod.Annotations[controller.AnnotationPowerStateObserved])
		assert.Empty(t, updatedPod.Annotations[controller.AnnotationPowerStatePhase])
	}
}
