package unit

import (
	"context"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/internal/store/postgres"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	"github.com/break/junhong_cmp_fiber/tests/testutils"
)

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

	store := postgres.NewEnterpriseCardAuthorizationStore(tx, rdb)
	ctx := context.Background()

	auth := &model.EnterpriseCardAuthorization{
		EnterpriseID:   1,
		CardID:         100,
		AuthorizedBy:   1,
		AuthorizedAt:   time.Now(),
		AuthorizerType: constants.UserTypePlatform,
	}

	err := store.Create(ctx, auth)
	require.NoError(t, err)
	assert.NotZero(t, auth.ID)
}

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

	store := postgres.NewEnterpriseCardAuthorizationStore(tx, rdb)
	ctx := context.Background()

	now := time.Now()
	auths := []*model.EnterpriseCardAuthorization{
		{
			EnterpriseID:   1,
			CardID:         101,
			AuthorizedBy:   1,
			AuthorizedAt:   now,
			AuthorizerType: constants.UserTypePlatform,
		},
		{
			EnterpriseID:   1,
			CardID:         102,
			AuthorizedBy:   1,
			AuthorizedAt:   now,
			AuthorizerType: constants.UserTypePlatform,
		},
	}

	err := store.BatchCreate(ctx, auths)
	require.NoError(t, err)

	for _, auth := range auths {
		assert.NotZero(t, auth.ID)
	}
}

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

	store := postgres.NewEnterpriseCardAuthorizationStore(tx, rdb)
	ctx := context.Background()

	auth := &model.EnterpriseCardAuthorization{
		EnterpriseID:   2,
		CardID:         200,
		AuthorizedBy:   1,
		AuthorizedAt:   time.Now(),
		AuthorizerType: constants.UserTypePlatform,
	}
	err := store.Create(ctx, auth)
	require.NoError(t, err)

	found, err := store.GetByEnterpriseAndCard(ctx, 2, 200)
	require.NoError(t, err)
	assert.Equal(t, auth.ID, found.ID)
	assert.Equal(t, uint(2), found.EnterpriseID)
	assert.Equal(t, uint(200), found.CardID)

	_, err = store.GetByEnterpriseAndCard(ctx, 999, 999)
	assert.Error(t, err)
}

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

	store := postgres.NewEnterpriseCardAuthorizationStore(tx, rdb)
	ctx := context.Background()

	now := time.Now()
	revokedAt := time.Now()

	auths := []*model.EnterpriseCardAuthorization{
		{EnterpriseID: 3, CardID: 301, AuthorizedBy: 1, AuthorizedAt: now, AuthorizerType: constants.UserTypePlatform},
		{EnterpriseID: 3, CardID: 302, AuthorizedBy: 1, AuthorizedAt: now, AuthorizerType: constants.UserTypePlatform},
		{EnterpriseID: 3, CardID: 303, AuthorizedBy: 1, AuthorizedAt: now, AuthorizerType: constants.UserTypePlatform, RevokedAt: &revokedAt, RevokedBy: ptrUint(1)},
	}
	err := store.BatchCreate(ctx, auths)
	require.NoError(t, err)

	activeAuths, err := store.ListByEnterprise(ctx, 3, false)
	require.NoError(t, err)
	assert.Len(t, activeAuths, 2)

	allAuths, err := store.ListByEnterprise(ctx, 3, true)
	require.NoError(t, err)
	assert.Len(t, allAuths, 3)
}

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

	store := postgres.NewEnterpriseCardAuthorizationStore(tx, rdb)
	ctx := context.Background()

	now := time.Now()
	auths := []*model.EnterpriseCardAuthorization{
		{EnterpriseID: 4, CardID: 401, AuthorizedBy: 1, AuthorizedAt: now, AuthorizerType: constants.UserTypePlatform},
		{EnterpriseID: 4, CardID: 402, AuthorizedBy: 1, AuthorizedAt: now, AuthorizerType: constants.UserTypePlatform},
	}
	err := store.BatchCreate(ctx, auths)
	require.NoError(t, err)

	err = store.RevokeAuthorizations(ctx, 4, []uint{401}, 2)
	require.NoError(t, err)

	activeAuths, err := store.ListByEnterprise(ctx, 4, false)
	require.NoError(t, err)
	assert.Len(t, activeAuths, 1)
	assert.Equal(t, uint(402), activeAuths[0].CardID)

	allAuths, err := store.ListByEnterprise(ctx, 4, true)
	require.NoError(t, err)
	assert.Len(t, allAuths, 2)
}

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

	store := postgres.NewEnterpriseCardAuthorizationStore(tx, rdb)
	ctx := context.Background()

	now := time.Now()
	revokedAt := time.Now()
	auths := []*model.EnterpriseCardAuthorization{
		{EnterpriseID: 5, CardID: 501, AuthorizedBy: 1, AuthorizedAt: now, AuthorizerType: constants.UserTypePlatform},
		{EnterpriseID: 5, CardID: 502, AuthorizedBy: 1, AuthorizedAt: now, AuthorizerType: constants.UserTypePlatform},
		{EnterpriseID: 5, CardID: 503, AuthorizedBy: 1, AuthorizedAt: now, AuthorizerType: constants.UserTypePlatform, RevokedAt: &revokedAt, RevokedBy: ptrUint(1)},
	}
	err := store.BatchCreate(ctx, auths)
	require.NoError(t, err)

	cardIDs, err := store.GetActiveAuthorizedCardIDs(ctx, 5)
	require.NoError(t, err)
	assert.Len(t, cardIDs, 2)
	assert.Contains(t, cardIDs, uint(501))
	assert.Contains(t, cardIDs, uint(502))
	assert.NotContains(t, cardIDs, uint(503))
}

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

	store := postgres.NewEnterpriseCardAuthorizationStore(tx, rdb)
	ctx := context.Background()

	auth := &model.EnterpriseCardAuthorization{
		EnterpriseID:   6,
		CardID:         600,
		AuthorizedBy:   1,
		AuthorizedAt:   time.Now(),
		AuthorizerType: constants.UserTypePlatform,
	}
	err := store.Create(ctx, auth)
	require.NoError(t, err)

	exists, err := store.CheckAuthorizationExists(ctx, 6, 600)
	require.NoError(t, err)
	assert.True(t, exists)

	exists, err = store.CheckAuthorizationExists(ctx, 6, 999)
	require.NoError(t, err)
	assert.False(t, exists)
}

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

	store := postgres.NewEnterpriseCardAuthorizationStore(tx, rdb)
	ctx := context.Background()

	now := time.Now()
	auths := []*model.EnterpriseCardAuthorization{
		{EnterpriseID: 7, CardID: 701, AuthorizedBy: 1, AuthorizedAt: now, AuthorizerType: constants.UserTypePlatform},
		{EnterpriseID: 7, CardID: 702, AuthorizedBy: 1, AuthorizedAt: now, AuthorizerType: constants.UserTypePlatform},
	}
	err := store.BatchCreate(ctx, auths)
	require.NoError(t, err)

	result, err := store.GetActiveAuthsByCardIDs(ctx, 7, []uint{701, 702, 703})
	require.NoError(t, err)
	assert.True(t, result[701])
	assert.True(t, result[702])
	assert.False(t, result[703])
}

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

	store := postgres.NewEnterpriseCardAuthorizationStore(tx, rdb)
	ctx := context.Background()

	now := time.Now()
	for i := uint(0); i < 15; i++ {
		auth := &model.EnterpriseCardAuthorization{
			EnterpriseID:   8,
			CardID:         800 + i,
			AuthorizedBy:   1,
			AuthorizedAt:   now,
			AuthorizerType: constants.UserTypePlatform,
		}
		err := store.Create(ctx, auth)
		require.NoError(t, err)
	}

	enterpriseID := uint(8)
	opts := postgres.AuthorizationListOptions{
		EnterpriseID: &enterpriseID,
		Limit:        10,
		Offset:       0,
	}
	auths, total, err := store.ListWithOptions(ctx, opts)
	require.NoError(t, err)
	assert.Equal(t, int64(15), total)
	assert.Len(t, auths, 10)

	opts.Offset = 10
	auths, total, err = store.ListWithOptions(ctx, opts)
	require.NoError(t, err)
	assert.Equal(t, int64(15), total)
	assert.Len(t, auths, 5)
}

func ptrUint(v uint) *uint {
	return &v
}

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

	store := postgres.NewEnterpriseCardAuthorizationStore(tx, rdb)
	ctx := context.Background()

	auth := &model.EnterpriseCardAuthorization{
		EnterpriseID:   10,
		CardID:         1000,
		AuthorizedBy:   1,
		AuthorizedAt:   time.Now(),
		AuthorizerType: constants.UserTypePlatform,
		Remark:         "原始备注",
	}
	err := store.Create(ctx, auth)
	require.NoError(t, err)

	err = store.UpdateRemark(ctx, auth.ID, "更新后的备注")
	require.NoError(t, err)

	updated, err := store.GetByID(ctx, auth.ID)
	require.NoError(t, err)
	assert.Equal(t, "更新后的备注", updated.Remark)

	err = store.UpdateRemark(ctx, 99999, "不存在的记录")
	assert.Error(t, err)
}

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

	store := postgres.NewEnterpriseCardAuthorizationStore(tx, rdb)
	ctx := context.Background()

	auth := &model.EnterpriseCardAuthorization{
		EnterpriseID:   11,
		CardID:         1100,
		AuthorizedBy:   1,
		AuthorizedAt:   time.Now(),
		AuthorizerType: constants.UserTypePlatform,
	}
	err := store.Create(ctx, auth)
	require.NoError(t, err)

	found, err := store.GetByID(ctx, auth.ID)
	require.NoError(t, err)
	assert.Equal(t, auth.ID, found.ID)
	assert.Equal(t, uint(11), found.EnterpriseID)
	assert.Equal(t, uint(1100), found.CardID)

	_, err = store.GetByID(ctx, 99999)
	assert.Error(t, err)
}
