package service

import (
	"context"
	"testing"

	opv2 "github.com/pingcap/tidb-operator/api/v2/core/v1alpha1"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/client/fake"
)

func TestNewPodRestarter(t *testing.T) {
	restarter := NewPodRestarter()
	assert.NotNil(t, restarter)
	assert.IsType(t, &podRestarterV2{}, restarter)
}

func TestPodRestarterV2_Restart(t *testing.T) {
	scheme := runtime.NewScheme()
	_ = corev1.AddToScheme(scheme)

	tests := []struct {
		name      string
		pod       *corev1.Pod
		force     bool
		wantErr   bool
		checkFunc func(t *testing.T, cli client.Client, pod *corev1.Pod)
	}{
		{
			name: "restart normal pod",
			pod: &corev1.Pod{
				ObjectMeta: metav1.ObjectMeta{
					Name:      "test-pod",
					Namespace: "default",
					Labels: map[string]string{
						opv2.LabelKeyComponent: opv2.LabelValComponentTiDB,
					},
				},
			},
			force:   false,
			wantErr: false,
			checkFunc: func(t *testing.T, cli client.Client, pod *corev1.Pod) {
				t.Helper()
				// Pod should be deleted
				var p corev1.Pod
				err := cli.Get(context.Background(), client.ObjectKey{
					Namespace: pod.Namespace,
					Name:      pod.Name,
				}, &p)
				assert.Error(t, err)
			},
		},
		{
			name: "restart tikv pod - should use grace period",
			pod: &corev1.Pod{
				ObjectMeta: metav1.ObjectMeta{
					Name:      "test-tikv-pod",
					Namespace: "default",
					Labels: map[string]string{
						opv2.LabelKeyComponent: opv2.LabelValComponentTiKV,
					},
				},
			},
			force:   false,
			wantErr: false,
		},
		{
			name: "force restart pod",
			pod: &corev1.Pod{
				ObjectMeta: metav1.ObjectMeta{
					Name:      "test-force-pod",
					Namespace: "default",
					Labels: map[string]string{
						opv2.LabelKeyComponent: opv2.LabelValComponentTiDB,
					},
				},
			},
			force:   true,
			wantErr: false,
		},
		{
			name: "pod already deleting - should skip",
			pod: &corev1.Pod{
				ObjectMeta: metav1.ObjectMeta{
					Name:              "deleting-pod",
					Namespace:         "default",
					DeletionTimestamp: &metav1.Time{Time: metav1.Now().Time},
					Finalizers:        []string{"kubernetes"},
					Labels: map[string]string{
						opv2.LabelKeyComponent: opv2.LabelValComponentTiDB,
					},
				},
			},
			force:   false,
			wantErr: false,
			checkFunc: func(t *testing.T, cli client.Client, pod *corev1.Pod) {
				t.Helper()
				// Pod should still exist (not deleted again)
				var p corev1.Pod
				err := cli.Get(context.Background(), client.ObjectKey{
					Namespace: pod.Namespace,
					Name:      pod.Name,
				}, &p)
				assert.NoError(t, err)
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cli := fake.NewClientBuilder().
				WithScheme(scheme).
				WithObjects(tt.pod).
				Build()

			restarter := &podRestarterV2{}
			err := restarter.Restart(context.Background(), cli, tt.pod, tt.force)

			if tt.wantErr {
				assert.Error(t, err)
				return
			}

			require.NoError(t, err)

			if tt.checkFunc != nil {
				tt.checkFunc(t, cli, tt.pod)
			}
		})
	}
}

func TestIsDeleting(t *testing.T) {
	tests := []struct {
		name string
		pod  *corev1.Pod
		want bool
	}{
		{
			name: "pod not deleting",
			pod: &corev1.Pod{
				ObjectMeta: metav1.ObjectMeta{
					Name: "normal-pod",
				},
			},
			want: false,
		},
		{
			name: "pod is deleting",
			pod: &corev1.Pod{
				ObjectMeta: metav1.ObjectMeta{
					Name:              "deleting-pod",
					DeletionTimestamp: &metav1.Time{Time: metav1.Now().Time},
				},
			},
			want: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := isDeleting(tt.pod)
			assert.Equal(t, tt.want, got)
		})
	}
}
