package usecase

import (
	"backend/pkg/permission/domain/entity"
	"backend/pkg/permission/domain/permission"
	mockRepo "backend/pkg/permission/mock/repository"
	"context"
	"errors"
	"testing"

	"github.com/stretchr/testify/assert"
	"go.mongodb.org/mongo-driver/v2/bson"
	"go.uber.org/mock/gomock"
)

func TestPermissionUseCase_GetAll(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()

	mockPermRepo := mockRepo.NewMockPermissionRepository(mockCtrl)

	uc := NewPermissionUseCase(PermissionUseCaseParam{
		PermRepo: mockPermRepo,
	})
	ctx := context.Background()

	tests := []struct {
		name      string
		mockSetup func()
		wantCount int
		wantErr   bool
	}{
		{
			name: "成功獲取所有權限",
			mockSetup: func() {
				perms := []*entity.Permission{
					{ID: bson.NewObjectID(), Name: "user.list", State: permission.RecordActive},
					{ID: bson.NewObjectID(), Name: "user.create", State: permission.RecordActive},
				}
				mockPermRepo.EXPECT().ListActive(ctx).Return(perms, nil)
			},
			wantCount: 2,
			wantErr:   false,
		},
		{
			name: "沒有權限",
			mockSetup: func() {
				mockPermRepo.EXPECT().ListActive(ctx).Return([]*entity.Permission{}, nil)
			},
			wantCount: 0,
			wantErr:   false,
		},
		{
			name: "Repository 錯誤",
			mockSetup: func() {
				mockPermRepo.EXPECT().ListActive(ctx).Return(nil, errors.New("db error"))
			},
			wantCount: 0,
			wantErr:   true,
		},
	}

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

			result, err := uc.GetAll(ctx)

			if tt.wantErr {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
				assert.Len(t, result, tt.wantCount)
			}
		})
	}
}

func TestPermissionUseCase_GetTree(t *testing.T) {
	ctx := context.Background()

	tests := []struct {
		name      string
		mockSetup func(*mockRepo.MockPermissionRepository)
		wantErr   bool
	}{
		{
			name: "成功獲取權限樹",
			mockSetup: func(mockPermRepo *mockRepo.MockPermissionRepository) {
				perms := []*entity.Permission{
					{
						ID:       bson.NewObjectID(),
						Name:     "user",
						State:    permission.RecordActive,
						ParentID: bson.ObjectID{},
					},
					{
						ID:       bson.NewObjectID(),
						Name:     "user.list",
						State:    permission.RecordActive,
						ParentID: bson.NewObjectID(),
					},
				}
				mockPermRepo.EXPECT().ListActive(ctx).Return(perms, nil)
			},
			wantErr: false,
		},
		{
			name: "Repository 錯誤",
			mockSetup: func(mockPermRepo *mockRepo.MockPermissionRepository) {
				mockPermRepo.EXPECT().ListActive(ctx).Return(nil, errors.New("db error"))
			},
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// 為每個測試案例創建新的 mock controller 和 usecase 實例，避免快取問題
			mockCtrl := gomock.NewController(t)
			defer mockCtrl.Finish()

			mockPermRepo := mockRepo.NewMockPermissionRepository(mockCtrl)
			tt.mockSetup(mockPermRepo)

			uc := NewPermissionUseCase(PermissionUseCaseParam{
				PermRepo: mockPermRepo,
			})

			result, err := uc.GetTree(ctx)

			if tt.wantErr {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
				assert.NotNil(t, result)
			}
		})
	}
}

func TestPermissionUseCase_GetByHTTP(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()

	mockPermRepo := mockRepo.NewMockPermissionRepository(mockCtrl)

	uc := NewPermissionUseCase(PermissionUseCaseParam{
		PermRepo: mockPermRepo,
	})
	ctx := context.Background()

	tests := []struct {
		name      string
		path      string
		method    string
		mockSetup func()
		wantNil   bool
		wantErr   bool
	}{
		{
			name:   "成功找到權限",
			path:   "/api/users",
			method: "GET",
			mockSetup: func() {
				perm := &entity.Permission{
					ID:         bson.NewObjectID(),
					Name:       "user.list",
					HTTPPath:   "/api/users",
					HTTPMethod: "GET",
					State:      permission.RecordActive,
				}
				mockPermRepo.EXPECT().FindByHTTP(ctx, "/api/users", "GET").Return(perm, nil)
			},
			wantNil: false,
			wantErr: false,
		},
		{
			name:   "找不到權限",
			path:   "/api/unknown",
			method: "POST",
			mockSetup: func() {
				mockPermRepo.EXPECT().FindByHTTP(ctx, "/api/unknown", "POST").Return(nil, errors.New("not found"))
			},
			wantNil: true,
			wantErr: true,
		},
	}

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

			result, err := uc.GetByHTTP(ctx, tt.path, tt.method)

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

			if tt.wantNil {
				assert.Nil(t, result)
			} else {
				assert.NotNil(t, result)
			}
		})
	}
}

func TestPermissionUseCase_ExpandPermissions(t *testing.T) {
	ctx := context.Background()

	tests := []struct {
		name      string
		perms     permission.Permissions
		mockSetup func(*mockRepo.MockPermissionRepository)
		wantCount int
		wantErr   bool
	}{
		{
			name: "成功展開權限",
			perms: permission.Permissions{
				"user":        permission.Open,
				"user.list":   permission.Open,
				"user.create": permission.Open,
			},
			mockSetup: func(mockPermRepo *mockRepo.MockPermissionRepository) {
				allPerms := []*entity.Permission{
					{ID: bson.NewObjectID(), Name: "user", State: permission.RecordActive},
					{ID: bson.NewObjectID(), Name: "user.list", State: permission.RecordActive},
					{ID: bson.NewObjectID(), Name: "user.create", State: permission.RecordActive},
				}
				mockPermRepo.EXPECT().ListActive(ctx).Return(allPerms, nil)
			},
			wantCount: 3,
			wantErr:   false,
		},
		{
			name:  "空權限列表",
			perms: permission.Permissions{},
			mockSetup: func(mockPermRepo *mockRepo.MockPermissionRepository) {
				mockPermRepo.EXPECT().ListActive(ctx).Return([]*entity.Permission{}, nil)
			},
			wantCount: 0,
			wantErr:   false,
		},
		{
			name:  "Repository 錯誤",
			perms: permission.Permissions{"user": permission.Open},
			mockSetup: func(mockPermRepo *mockRepo.MockPermissionRepository) {
				mockPermRepo.EXPECT().ListActive(ctx).Return(nil, errors.New("db error"))
			},
			wantCount: 0,
			wantErr:   true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// 為每個測試案例創建新的 mock controller 和 usecase 實例，避免快取問題
			mockCtrl := gomock.NewController(t)
			defer mockCtrl.Finish()

			mockPermRepo := mockRepo.NewMockPermissionRepository(mockCtrl)
			tt.mockSetup(mockPermRepo)

			uc := NewPermissionUseCase(PermissionUseCaseParam{
				PermRepo: mockPermRepo,
			})

			result, err := uc.ExpandPermissions(ctx, tt.perms)

			if tt.wantErr {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
				assert.Len(t, result, tt.wantCount)
			}
		})
	}
}

// GetUsersByPermission is not in PermissionRepository interface, skip this test
// func TestPermissionUseCase_GetUsersByPermission(t *testing.T) {
// 	mockCtrl := gomock.NewController(t)
// 	defer mockCtrl.Finish()
//
// 	mockPermRepo := mockRepo.NewMockPermissionRepository(mockCtrl)
//
// 	uc := NewPermissionUseCase(PermissionUseCaseParam{
// 		PermRepo: mockPermRepo,
// 	})
// 	ctx := context.Background()
//
// 	tests := []struct {
// 		name          string
// 		permissionUID string
// 		mockSetup     func()
// 		wantCount     int
// 		wantErr       bool
// 	}{
// 		{
// 			name:          "成功獲取使用者列表",
// 			permissionUID: "perm123",
// 			mockSetup: func() {
// 				mockPermRepo.EXPECT().GetUsersByPermission(ctx, "perm123").Return([]string{"user1", "user2"}, nil)
// 			},
// 			wantCount: 2,
// 			wantErr:   false,
// 		},
// 	}
//
// 	for _, tt := range tests {
// 		t.Run(tt.name, func(t *testing.T) {
// 			tt.mockSetup()
//
// 			result, err := uc.GetUsersByPermission(ctx, tt.permissionUID)
//
// 			if tt.wantErr {
// 				assert.Error(t, err)
// 			} else {
// 				assert.NoError(t, err)
// 				assert.Len(t, result, tt.wantCount)
// 			}
// 		})
// 	}
// }
