package br

import (
	"context"
	"errors"
	"testing"

	"github.com/stretchr/testify/require"
	apiv1 "github.com/tidbcloud/cluster-service-ng/api/cluster-service-ng/v1beta"
	"github.com/tidbcloud/cluster-service-ng/domain/entity"
	"github.com/tidbcloud/cluster-service-ng/pkg/clustergateway"
	clustergatewaymocks "github.com/tidbcloud/cluster-service-ng/pkg/clustergateway/mocks"
	"github.com/tidbcloud/cluster-service-ng/pkg/storage/mysql/schema"
	"github.com/tidbcloud/cluster-service-ng/pkg/storage/repo/mocks"
	"github.com/tidbcloud/cluster-service-ng/pkg/tioperator"
)

func TestCalDataRestoringStatus4SameSharedPool(t *testing.T) {
	tests := []struct {
		name       string
		kernelTask *clustergateway.KernelLightweightRestore
		expected   *GetLightweightRestoreOutput
	}{
		{
			name: "conflict restore error",
			kernelTask: &clustergateway.KernelLightweightRestore{
				ConflictRestoreId: 12345,
				DurationSeconds:   100,
				RestoreBytes:      1024,
				Progress: clustergateway.LightweightRestoreProgress{
					Step:               "testing",
					ProgressPercentage: 50,
				},
			},
			expected: &GetLightweightRestoreOutput{
				Status:                        apiv1.LightweightRestoreStatus_DATA_RESTORING_ERROR,
				Message:                       "restore is conflict with another restore(12345)",
				DataRestoreDurationSeconds:    100,
				DataRestoreBytes:              1024,
				DataRestoreStep:               "testing",
				DataRestoreProgressPercentage: 50,
			},
		},
		{
			name: "kernel task error status",
			kernelTask: &clustergateway.KernelLightweightRestore{
				ConflictRestoreId: 0,
				Status:            clustergateway.LightweightRestoreStatus_Error,
				Error:             "database connection failed",
				DurationSeconds:   200,
				RestoreBytes:      2048,
				Progress: clustergateway.LightweightRestoreProgress{
					Step:               "connecting",
					ProgressPercentage: 25,
				},
			},
			expected: &GetLightweightRestoreOutput{
				Status:                        apiv1.LightweightRestoreStatus_DATA_RESTORING_ERROR,
				Message:                       "data restoring is error: database connection failed",
				DataRestoreDurationSeconds:    200,
				DataRestoreBytes:              2048,
				DataRestoreStep:               "connecting",
				DataRestoreProgressPercentage: 25,
			},
		},
		{
			name: "kernel task succeed status",
			kernelTask: &clustergateway.KernelLightweightRestore{
				ConflictRestoreId: 0,
				Status:            clustergateway.LightweightRestoreStatus_Succeed,
				DurationSeconds:   300,
				RestoreBytes:      4096,
				Progress: clustergateway.LightweightRestoreProgress{
					Step:               "finalizing",
					ProgressPercentage: 95,
				},
			},
			expected: &GetLightweightRestoreOutput{
				Status:                        apiv1.LightweightRestoreStatus_DATA_RESTORING,
				Message:                       "data restoring is succeed, restarting tidb for the target cluster",
				DataRestoreDurationSeconds:    300,
				DataRestoreBytes:              4096,
				DataRestoreStep:               "finalizing",
				DataRestoreProgressPercentage: 100,
			},
		},
		{
			name: "kernel task running status",
			kernelTask: &clustergateway.KernelLightweightRestore{
				ConflictRestoreId: 0,
				Status:            clustergateway.LightweightRestoreStatus_Running,
				DurationSeconds:   150,
				RestoreBytes:      1536,
				Progress: clustergateway.LightweightRestoreProgress{
					Step:               "restoring data",
					ProgressPercentage: 75,
				},
			},
			expected: &GetLightweightRestoreOutput{
				Status:                        apiv1.LightweightRestoreStatus_DATA_RESTORING,
				Message:                       "data is restoring",
				DataRestoreDurationSeconds:    150,
				DataRestoreBytes:              1536,
				DataRestoreStep:               "restoring data",
				DataRestoreProgressPercentage: 75,
			},
		},
		{
			name: "kernel task pending status",
			kernelTask: &clustergateway.KernelLightweightRestore{
				ConflictRestoreId: 0,
				Status:            clustergateway.LightweightRestoreStatus_Pending,
				DurationSeconds:   0,
				RestoreBytes:      0,
				Progress: clustergateway.LightweightRestoreProgress{
					Step:               "pending",
					ProgressPercentage: 0,
				},
			},
			expected: &GetLightweightRestoreOutput{
				Status:                        apiv1.LightweightRestoreStatus_DATA_RESTORING,
				Message:                       "data is restoring",
				DataRestoreDurationSeconds:    0,
				DataRestoreBytes:              0,
				DataRestoreStep:               "pending",
				DataRestoreProgressPercentage: 0,
			},
		},
		{
			name: "kernel task init status",
			kernelTask: &clustergateway.KernelLightweightRestore{
				ConflictRestoreId: 0,
				Status:            clustergateway.LightweightRestoreStatus_Init,
				DurationSeconds:   10,
				RestoreBytes:      0,
				Progress: clustergateway.LightweightRestoreProgress{
					Step:               "initializing",
					ProgressPercentage: 5,
				},
			},
			expected: &GetLightweightRestoreOutput{
				Status:                        apiv1.LightweightRestoreStatus_DATA_RESTORING,
				Message:                       "data is restoring",
				DataRestoreDurationSeconds:    10,
				DataRestoreBytes:              0,
				DataRestoreStep:               "initializing",
				DataRestoreProgressPercentage: 5,
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := calDataRestoringStatus4SameSharedPool(tt.kernelTask)

			if result.Status != tt.expected.Status {
				t.Errorf("calDataRestoringStatus4SameSharedPool() Status = %v, expected %v", result.Status, tt.expected.Status)
			}

			if result.Message != tt.expected.Message {
				t.Errorf("calDataRestoringStatus4SameSharedPool() Message = %q, expected %q", result.Message, tt.expected.Message)
			}

			if result.DataRestoreDurationSeconds != tt.expected.DataRestoreDurationSeconds {
				t.Errorf("calDataRestoringStatus4SameSharedPool() DataRestoreDurationSeconds = %v, expected %v", result.DataRestoreDurationSeconds, tt.expected.DataRestoreDurationSeconds)
			}

			if result.DataRestoreBytes != tt.expected.DataRestoreBytes {
				t.Errorf("calDataRestoringStatus4SameSharedPool() DataRestoreBytes = %v, expected %v", result.DataRestoreBytes, tt.expected.DataRestoreBytes)
			}

			if result.DataRestoreStep != tt.expected.DataRestoreStep {
				t.Errorf("calDataRestoringStatus4SameSharedPool() DataRestoreStep = %q, expected %q", result.DataRestoreStep, tt.expected.DataRestoreStep)
			}

			if result.DataRestoreProgressPercentage != tt.expected.DataRestoreProgressPercentage {
				t.Errorf("calDataRestoringStatus4SameSharedPool() DataRestoreProgressPercentage = %v, expected %v", result.DataRestoreProgressPercentage, tt.expected.DataRestoreProgressPercentage)
			}
		})
	}
}

func TestLwRestoreSameSharedPoolAdaptor_GetPostActions(t *testing.T) {
	adaptor := &lwRestoreSameSharedPoolAdaptor{}
	resp, err := adaptor.GetPostActions(context.Background(), &entity.LightweightRestore{})

	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Empty(t, resp.ExecSql)
	require.Empty(t, resp.CloudStorageUri)
}

func TestLwRestoreSameSharedPoolAdaptor_GetDataRestoringOutput(t *testing.T) {
	tests := []struct {
		name                  string
		restore               *entity.LightweightRestore
		setupMocks            func(*mocks.SharedPoolRepo, *clustergatewaymocks.Client)
		expectedStatus        apiv1.LightweightRestoreStatus
		expectedMessage       string
		expectedError         bool
		expectedErrorContains string
	}{
		{
			name: "successful data restoring with kernel task",
			restore: &entity.LightweightRestore{
				LightweightRestore: schema.LightweightRestore{
					RestoreID:          "restore-123",
					TargetSharedPoolID: "pool-456",
					TargetPdClusterID:  "pd-789",
					TargetKeyspace:     "test-keyspace",
				},
			},
			setupMocks: func(sharedPoolRepo *mocks.SharedPoolRepo, clustergatewayClient *clustergatewaymocks.Client) {
				// Mock SharedPoolRepo.Load
				sharedPoolRepo.On("Load", context.Background(), "pool-456", false).Return(
					&entity.SharedPool{}, nil)

				// Mock ClustergatewayClient.GetLightweightRestore
				kernelTask := &clustergateway.KernelLightweightRestore{
					ConflictRestoreId: 0,
					Status:            clustergateway.LightweightRestoreStatus_Running,
					DurationSeconds:   100,
					RestoreBytes:      1024,
					Progress: clustergateway.LightweightRestoreProgress{
						Step:               "restoring",
						ProgressPercentage: 50,
					},
				}
				clustergatewayClient.On("GetLightweightRestore", context.Background(),
					&entity.SharedPool{}, tioperator.TiBRCRNameOfRestore("restore-123"),
					&clustergateway.KernelLightweightRestoreIdentity{
						RestoreId:   "restore-123",
						PDClusterId: "pd-789",
						KeySpace:    "test-keyspace",
					}).Return(kernelTask, nil)
			},
			expectedStatus:  apiv1.LightweightRestoreStatus_DATA_RESTORING,
			expectedMessage: "data is restoring",
			expectedError:   false,
		},
		{
			name: "kernel restore task not found",
			restore: &entity.LightweightRestore{
				LightweightRestore: schema.LightweightRestore{
					RestoreID:          "restore-123",
					TargetSharedPoolID: "pool-456",
					TargetPdClusterID:  "pd-789",
					TargetKeyspace:     "test-keyspace",
				},
			},
			setupMocks: func(sharedPoolRepo *mocks.SharedPoolRepo, clustergatewayClient *clustergatewaymocks.Client) {
				// Mock SharedPoolRepo.Load
				sharedPoolRepo.On("Load", context.Background(), "pool-456", false).Return(
					&entity.SharedPool{}, nil)

				// Mock ClustergatewayClient.GetLightweightRestore - return not found error
				clustergatewayClient.On("GetLightweightRestore", context.Background(),
					&entity.SharedPool{}, tioperator.TiBRCRNameOfRestore("restore-123"),
					&clustergateway.KernelLightweightRestoreIdentity{
						RestoreId:   "restore-123",
						PDClusterId: "pd-789",
						KeySpace:    "test-keyspace",
					}).Return(nil, clustergateway.RestoreNotFoundErr)
			},
			expectedStatus:  apiv1.LightweightRestoreStatus_DATA_RESTORING,
			expectedMessage: "wait for the kernel restore task created",
			expectedError:   false,
		},
		{
			name: "shared pool load failed",
			restore: &entity.LightweightRestore{
				LightweightRestore: schema.LightweightRestore{
					RestoreID:          "restore-123",
					TargetSharedPoolID: "pool-456",
				},
			},
			setupMocks: func(sharedPoolRepo *mocks.SharedPoolRepo, clustergatewayClient *clustergatewaymocks.Client) {
				// Mock SharedPoolRepo.Load to fail
				sharedPoolRepo.On("Load", context.Background(), "pool-456", false).Return(
					nil, errors.New("shared pool not found"))
			},
			expectedError:         true,
			expectedErrorContains: "get target sharedPool pool-456 failed: shared pool not found",
		},
		{
			name: "clustergateway get restore failed",
			restore: &entity.LightweightRestore{
				LightweightRestore: schema.LightweightRestore{
					RestoreID:          "restore-123",
					TargetSharedPoolID: "pool-456",
					TargetPdClusterID:  "pd-789",
					TargetKeyspace:     "test-keyspace",
				},
			},
			setupMocks: func(sharedPoolRepo *mocks.SharedPoolRepo, clustergatewayClient *clustergatewaymocks.Client) {
				// Mock SharedPoolRepo.Load
				sharedPoolRepo.On("Load", context.Background(), "pool-456", false).Return(
					&entity.SharedPool{}, nil)

				// Mock ClustergatewayClient.GetLightweightRestore to fail with non-NotFound error
				clustergatewayClient.On("GetLightweightRestore", context.Background(),
					&entity.SharedPool{}, tioperator.TiBRCRNameOfRestore("restore-123"),
					&clustergateway.KernelLightweightRestoreIdentity{
						RestoreId:   "restore-123",
						PDClusterId: "pd-789",
						KeySpace:    "test-keyspace",
					}).Return(nil, errors.New("cluster gateway error"))
			},
			expectedError:         true,
			expectedErrorContains: "get kernel restore task failed: cluster gateway error",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Create mocks
			sharedPoolRepo := mocks.NewSharedPoolRepo(t)
			clustergatewayClient := clustergatewaymocks.NewClient(t)

			// Setup mock expectations
			tt.setupMocks(sharedPoolRepo, clustergatewayClient)

			// Create adaptor
			adaptor := &lwRestoreSameSharedPoolAdaptor{
				clustergatewayCli: clustergatewayClient,
				sharedPoolRepo:    sharedPoolRepo,
			}

			// Call the method
			ctx := context.Background()
			result, err := adaptor.getDataRestoringOutput(ctx, tt.restore)

			// Check error expectations
			if tt.expectedError {
				if err == nil {
					t.Errorf("expected error but got none")
					return
				}
				if tt.expectedErrorContains != "" && !errors.Is(err, errors.New(tt.expectedErrorContains)) {
					if err.Error() != tt.expectedErrorContains {
						t.Errorf("expected error to contain %q, got %q", tt.expectedErrorContains, err.Error())
					}
				}
				return
			}

			// Check success expectations
			if err != nil {
				t.Errorf("unexpected error: %v", err)
				return
			}

			if result == nil {
				t.Errorf("expected result but got nil")
				return
			}

			if result.Status != tt.expectedStatus {
				t.Errorf("expected status %v, got %v", tt.expectedStatus, result.Status)
			}

			if result.Message != tt.expectedMessage {
				t.Errorf("expected message %q, got %q", tt.expectedMessage, result.Message)
			}
		})
	}
}
