package export

import (
	"context"
	"errors"
	"testing"

	"github.com/stretchr/testify/require"
	"go.uber.org/mock/gomock"

	globalngv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/entity"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/export/mock_export"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

func TestValidateExportId(t *testing.T) {
	const (
		clusterID = "cluster-123"
		exportID  = "export-456"
	)

	t.Run("returns nil when export_id does not exist", func(t *testing.T) {
		ctrl := gomock.NewController(t)
		repo := mock_export.NewMockExportRepo(ctrl)

		svc := &serviceImpl{
			exportRepo: repo,
		}

		// Mock: GetByClusterAndExportId returns ErrCodeRecordNotFound
		notFoundErr := bizerr.New(bizerr.ErrCodeRecordNotFound, "not found")
		repo.EXPECT().GetByClusterAndExportId(gomock.Any(), clusterID, exportID).Return(nil, notFoundErr)

		err := svc.validateExportId(context.Background(), clusterID, exportID)
		require.NoError(t, err)
	})

	t.Run("returns error when export_id already exists", func(t *testing.T) {
		ctrl := gomock.NewController(t)
		repo := mock_export.NewMockExportRepo(ctrl)

		svc := &serviceImpl{
			exportRepo: repo,
		}

		// Mock: GetByClusterAndExportId returns an existing export
		existingExport := &entity.Export{}
		repo.EXPECT().GetByClusterAndExportId(gomock.Any(), clusterID, exportID).Return(existingExport, nil)

		err := svc.validateExportId(context.Background(), clusterID, exportID)
		require.Error(t, err)
		require.Equal(t, bizerr.ErrCodeRecordExist, err.ErrCode())
	})

	t.Run("returns error when repo query fails with unexpected error", func(t *testing.T) {
		ctrl := gomock.NewController(t)
		repo := mock_export.NewMockExportRepo(ctrl)

		svc := &serviceImpl{
			exportRepo: repo,
		}

		// Mock: GetByClusterAndExportId returns an unexpected error
		unexpectedErr := bizerr.WrapError(errors.New("database connection failed"), "db error")
		repo.EXPECT().GetByClusterAndExportId(gomock.Any(), clusterID, exportID).Return(nil, unexpectedErr)

		err := svc.validateExportId(context.Background(), clusterID, exportID)
		require.Error(t, err)
	})
}

func TestValidateCreateExportWithExportId(t *testing.T) {
	const (
		clusterID = "cluster-123"
		exportID  = "export-456"
	)

	tests := []struct {
		name            string
		request         *globalngv1.CreateExportRequest
		mockSetup       func(*mock_export.MockExportRepo)
		expectedErr     bool
		expectedErrCode bizerr.ErrCode
	}{
		{
			name: "skips validation when export_id is empty",
			request: &globalngv1.CreateExportRequest{
				ClusterId: clusterID,
				Export: &globalngv1.Export{
					TidbId: "tidb-123",
				},
			},
			mockSetup: func(repo *mock_export.MockExportRepo) {
				// No expectations - should not call GetByClusterAndExportId
			},
			expectedErr: false,
		},
		{
			name: "validates and accepts new export_id",
			request: &globalngv1.CreateExportRequest{
				ClusterId: clusterID,
				Export: &globalngv1.Export{
					TidbId:   "tidb-123",
					ExportId: exportID,
				},
			},
			mockSetup: func(repo *mock_export.MockExportRepo) {
				notFoundErr := bizerr.New(bizerr.ErrCodeRecordNotFound, "not found")
				repo.EXPECT().GetByClusterAndExportId(gomock.Any(), clusterID, exportID).Return(nil, notFoundErr)
			},
			expectedErr: false,
		},
		{
			name: "rejects duplicate export_id",
			request: &globalngv1.CreateExportRequest{
				ClusterId: clusterID,
				Export: &globalngv1.Export{
					TidbId:   "tidb-123",
					ExportId: exportID,
				},
			},
			mockSetup: func(repo *mock_export.MockExportRepo) {
				existingExport := &entity.Export{}
				repo.EXPECT().GetByClusterAndExportId(gomock.Any(), clusterID, exportID).Return(existingExport, nil)
			},
			expectedErr:     true,
			expectedErrCode: bizerr.ErrCodeRecordExist,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ctrl := gomock.NewController(t)
			repo := mock_export.NewMockExportRepo(ctrl)
			tt.mockSetup(repo)

			svc := &serviceImpl{
				exportRepo: repo,
			}

			// We only test the export_id validation part
			// Other validations (like snapshot TSO) are tested in snapshot_tso_test.go
			if tt.request.GetExport().GetExportId() != "" {
				err := svc.validateExportId(context.Background(), tt.request.GetClusterId(), tt.request.GetExport().GetExportId())
				if tt.expectedErr {
					require.Error(t, err)
					require.Equal(t, tt.expectedErrCode, err.ErrCode())
				} else {
					require.NoError(t, err)
				}
			}
		})
	}
}
