package backup

import (
	"testing"

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

	globalngv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
	openapingv1beta2 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_cloud_open_api/nextgen/v1beta2"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/adapter/account/mock_account"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/adapter/nextgenglobal/mock_nextgenglobal"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/entity"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/repository"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/service/nextgen/cluster/mock_cluster"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/pkg/common"
	"github.com/tidbcloud/tidb-management-service/utilities/grpc/metadata"
)

func TestListBackupsAppliesProjectRBAC(t *testing.T) {
	ctx := t.Context()
	orgID := uint64(123)
	userID := "456"

	ctl := gomock.NewController(t)
	defer ctl.Finish()

	mockNextGenCli := mock_nextgenglobal.NewMockNextGenGlobal(ctl)
	mockClusterSvc := mock_cluster.NewMockService(ctl)
	mockAccountCli := mock_account.NewMockClient(ctl)

	server := &serverImpl{
		nextGenCli: mockNextGenCli,
		clusterSvc: mockClusterSvc,
		accountCli: mockAccountCli,
	}

	ctxWithMeta := metadata.AppendToIncomingMeta(ctx, &metadata.RequestMeta{
		OrgID:             orgID,
		DefaultProjectID:  3201779,
		ValidProjectList:  []uint64{3201779},
		UserID:            userID,
		IsCheckPermission: lo.ToPtr(true),
	})

	permissionFilter := &repository.PermissionFilter{
		All:        false,
		ProjectIds: []uint64{3201779},
		ClusterIds: []uint64{1001},
	}
	mockAccountCli.EXPECT().
		GetInstancePermissionScopeOfUser(gomock.Any(), orgID, userID).
		Return(permissionFilter.All, permissionFilter.ProjectIds, permissionFilter.ClusterIds, nil)

	mockClusterSvc.EXPECT().
		CountClusters(gomock.Any(), gomock.Any()).
		Return(int64(1), nil)
	mockClusterSvc.EXPECT().
		ListClusters(gomock.Any(), gomock.Any(), &common.Page{Offset: 0, PageSize: 1}).
		Return([]*entity.Cluster{{ClusterId: 1001}}, nil)

	mockNextGenCli.EXPECT().
		ListBackups(gomock.Any(), &globalngv1.ListBackupsRequest{
			ClusterIds:      []string{"1001"},
			OrganizationIds: []string{"123"},
			PageSize:        2,
		}).
		Return(&globalngv1.ListBackupsResponse{
			Backups: []*globalngv1.Backup{
				{Id: "backup-1", ClusterId: "1001"},
			},
			TotalSize: 1,
		}, nil)

	resp, err := server.ListBackups(ctxWithMeta, &openapingv1beta2.ListBackupsRequest{
		PageSize: 2,
	})

	require.NoError(t, err)
	require.NotNil(t, resp)
	assert.Equal(t, int32(1), resp.TotalSize)
	assert.Len(t, resp.Backups, 1)
	assert.Equal(t, "1001", resp.Backups[0].TidbId)
}

func TestListBackupsReturnsEmptyWhenExplicitTidbIsUnauthorized(t *testing.T) {
	ctx := t.Context()
	orgID := uint64(123)
	userID := "456"

	ctl := gomock.NewController(t)
	defer ctl.Finish()

	mockClusterSvc := mock_cluster.NewMockService(ctl)
	mockAccountCli := mock_account.NewMockClient(ctl)

	server := &serverImpl{
		clusterSvc: mockClusterSvc,
		accountCli: mockAccountCli,
	}

	ctxWithMeta := metadata.AppendToIncomingMeta(ctx, &metadata.RequestMeta{
		OrgID:             orgID,
		DefaultProjectID:  3201779,
		ValidProjectList:  []uint64{3201779},
		UserID:            userID,
		IsCheckPermission: lo.ToPtr(true),
	})

	permissionFilter := &repository.PermissionFilter{
		All:        false,
		ProjectIds: []uint64{3201779},
	}
	mockAccountCli.EXPECT().
		GetInstancePermissionScopeOfUser(gomock.Any(), orgID, userID).
		Return(permissionFilter.All, permissionFilter.ProjectIds, permissionFilter.ClusterIds, nil)

	mockClusterSvc.EXPECT().
		ListClusters(gomock.Any(), gomock.Any(), &common.Page{Offset: 0, PageSize: 1}).
		Return(nil, nil)

	resp, err := server.ListBackups(ctxWithMeta, &openapingv1beta2.ListBackupsRequest{
		TidbIds: []string{"1002"},
	})

	require.NoError(t, err)
	require.NotNil(t, resp)
	assert.Equal(t, int32(0), resp.TotalSize)
	assert.Empty(t, resp.Backups)
	assert.Empty(t, resp.NextPageToken)
}

func TestListBackupsReturnsEmptyWhenProjectRBACHasNoPermittedTidbsWithoutExplicitTidb(t *testing.T) {
	ctx := t.Context()
	orgID := uint64(123)
	userID := "456"

	ctl := gomock.NewController(t)
	defer ctl.Finish()

	mockClusterSvc := mock_cluster.NewMockService(ctl)
	mockAccountCli := mock_account.NewMockClient(ctl)

	server := &serverImpl{
		clusterSvc: mockClusterSvc,
		accountCli: mockAccountCli,
	}

	ctxWithMeta := metadata.AppendToIncomingMeta(ctx, &metadata.RequestMeta{
		OrgID:             orgID,
		DefaultProjectID:  3201779,
		ValidProjectList:  []uint64{3201779},
		UserID:            userID,
		IsCheckPermission: lo.ToPtr(true),
	})

	permissionFilter := &repository.PermissionFilter{
		All:        false,
		ProjectIds: []uint64{3201779},
	}
	mockAccountCli.EXPECT().
		GetInstancePermissionScopeOfUser(gomock.Any(), orgID, userID).
		Return(permissionFilter.All, permissionFilter.ProjectIds, permissionFilter.ClusterIds, nil)

	mockClusterSvc.EXPECT().
		CountClusters(gomock.Any(), gomock.Any()).
		Return(int64(0), nil)

	resp, err := server.ListBackups(ctxWithMeta, &openapingv1beta2.ListBackupsRequest{})

	require.NoError(t, err)
	require.NotNil(t, resp)
	assert.Equal(t, int32(0), resp.TotalSize)
	assert.Empty(t, resp.Backups)
	assert.Empty(t, resp.NextPageToken)
}
