package unit

import (
	"context"
	"encoding/json"
	"testing"
	"time"

	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/internal/service/permission"
	"github.com/break/junhong_cmp_fiber/internal/store/postgres"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	"github.com/break/junhong_cmp_fiber/pkg/middleware"
	"github.com/break/junhong_cmp_fiber/tests/testutils"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestPermissionCache_FirstCallMissSecondHit(t *testing.T) {
	tx := testutils.NewTestTransaction(t)
	rdb := testutils.GetTestRedis(t)
	testutils.CleanTestRedisKeys(t, rdb)

	ctx := context.Background()

	accountStore := postgres.NewAccountStore(tx, rdb)
	roleStore := postgres.NewRoleStore(tx)
	permStore := postgres.NewPermissionStore(tx)
	accountRoleStore := postgres.NewAccountRoleStore(tx, rdb)
	rolePermStore := postgres.NewRolePermissionStore(tx, rdb)

	permSvc := permission.New(permStore, accountRoleStore, rolePermStore, rdb)

	testUser := &model.Account{
		Username: "testuser",
		Phone:    "13900000001",
		Password: "Test@123456",
		UserType: constants.UserTypePlatform,
		Status:   constants.StatusEnabled,
	}
	require.NoError(t, accountStore.Create(ctx, testUser))

	testRole := &model.Role{
		RoleName: "测试角色",
		RoleType: constants.RoleTypePlatform,
		Status:   constants.StatusEnabled,
	}
	require.NoError(t, roleStore.Create(ctx, testRole))

	testPerm := &model.Permission{
		PermName: "测试权限",
		PermCode: "test:read",
		PermType: constants.PermissionTypeButton,
		Platform: constants.PlatformWeb,
		Status:   constants.StatusEnabled,
	}
	require.NoError(t, permStore.Create(ctx, testPerm))

	require.NoError(t, accountRoleStore.Create(ctx, &model.AccountRole{
		AccountID: testUser.ID,
		RoleID:    testRole.ID,
	}))

	require.NoError(t, rolePermStore.Create(ctx, &model.RolePermission{
		RoleID: testRole.ID,
		PermID: testPerm.ID,
	}))

	ctx = middleware.SetUserContext(ctx, &middleware.UserContextInfo{
		UserID:   testUser.ID,
		UserType: testUser.UserType,
	})

	cacheKey := constants.RedisUserPermissionsKey(testUser.ID)
	cachedData, err := rdb.Get(ctx, cacheKey).Result()
	assert.Error(t, err)
	assert.Empty(t, cachedData)

	hasPermission, err := permSvc.CheckPermission(ctx, testUser.ID, "test:read", constants.PlatformWeb)
	require.NoError(t, err)
	assert.True(t, hasPermission)

	cachedData, err = rdb.Get(ctx, cacheKey).Result()
	require.NoError(t, err)
	assert.NotEmpty(t, cachedData)

	type cacheItem struct {
		PermCode string `json:"perm_code"`
		Platform string `json:"platform"`
	}
	var cached []cacheItem
	require.NoError(t, json.Unmarshal([]byte(cachedData), &cached))
	assert.Len(t, cached, 1)
	assert.Equal(t, "test:read", cached[0].PermCode)
	assert.Equal(t, constants.PlatformWeb, cached[0].Platform)

	hasPermission2, err := permSvc.CheckPermission(ctx, testUser.ID, "test:read", constants.PlatformWeb)
	require.NoError(t, err)
	assert.True(t, hasPermission2)
}

func TestPermissionCache_ExpiredAfter30Minutes(t *testing.T) {
	tx := testutils.NewTestTransaction(t)
	rdb := testutils.GetTestRedis(t)
	testutils.CleanTestRedisKeys(t, rdb)

	ctx := context.Background()

	accountStore := postgres.NewAccountStore(tx, rdb)
	roleStore := postgres.NewRoleStore(tx)
	permStore := postgres.NewPermissionStore(tx)
	accountRoleStore := postgres.NewAccountRoleStore(tx, rdb)
	rolePermStore := postgres.NewRolePermissionStore(tx, rdb)

	permSvc := permission.New(permStore, accountRoleStore, rolePermStore, rdb)

	testUser := &model.Account{
		Username: "testuser2",
		Phone:    "13900000002",
		Password: "Test@123456",
		UserType: constants.UserTypePlatform,
		Status:   constants.StatusEnabled,
	}
	require.NoError(t, accountStore.Create(ctx, testUser))

	testRole := &model.Role{
		RoleName: "测试角色2",
		RoleType: constants.RoleTypePlatform,
		Status:   constants.StatusEnabled,
	}
	require.NoError(t, roleStore.Create(ctx, testRole))

	testPerm := &model.Permission{
		PermName: "测试权限2",
		PermCode: "test:write",
		PermType: constants.PermissionTypeButton,
		Platform: constants.PlatformWeb,
		Status:   constants.StatusEnabled,
	}
	require.NoError(t, permStore.Create(ctx, testPerm))

	require.NoError(t, accountRoleStore.Create(ctx, &model.AccountRole{
		AccountID: testUser.ID,
		RoleID:    testRole.ID,
	}))

	require.NoError(t, rolePermStore.Create(ctx, &model.RolePermission{
		RoleID: testRole.ID,
		PermID: testPerm.ID,
	}))

	ctx = middleware.SetUserContext(ctx, &middleware.UserContextInfo{
		UserID:   testUser.ID,
		UserType: testUser.UserType,
	})

	hasPermission, err := permSvc.CheckPermission(ctx, testUser.ID, "test:write", constants.PlatformWeb)
	require.NoError(t, err)
	assert.True(t, hasPermission)

	cacheKey := constants.RedisUserPermissionsKey(testUser.ID)
	ttl, err := rdb.TTL(ctx, cacheKey).Result()
	require.NoError(t, err)
	assert.True(t, ttl > 29*time.Minute && ttl <= 30*time.Minute)
}
