package unit

import (
	"context"
	"testing"

	"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/model/dto"
	"github.com/break/junhong_cmp_fiber/internal/service/enterprise_card"
	"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 createEnterpriseCardTestContext(userID uint, shopID uint) context.Context {
	ctx := context.Background()
	ctx = context.WithValue(ctx, constants.ContextKeyUserID, userID)
	ctx = context.WithValue(ctx, constants.ContextKeyUserType, constants.UserTypePlatform)
	ctx = context.WithValue(ctx, constants.ContextKeyShopID, shopID)
	return ctx
}

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

	enterpriseStore := postgres.NewEnterpriseStore(tx, rdb)
	enterpriseCardAuthStore := postgres.NewEnterpriseCardAuthorizationStore(tx, rdb)

	service := enterprise_card.New(tx, enterpriseStore, enterpriseCardAuthStore)

	t.Run("授权预检-企业不存在应失败", func(t *testing.T) {
		ctx := createEnterpriseCardTestContext(1, 1)

		req := &dto.AllocateCardsPreviewReq{
			ICCIDs: []string{"898600000001"},
		}

		_, err := service.AllocateCardsPreview(ctx, 99999, req)
		assert.Error(t, err)
	})

	t.Run("授权预检-未授权用户应失败", func(t *testing.T) {
		ctx := context.Background()

		req := &dto.AllocateCardsPreviewReq{
			ICCIDs: []string{"898600000001"},
		}

		_, err := service.AllocateCardsPreview(ctx, 1, req)
		assert.Error(t, err)
	})

	t.Run("授权预检-空ICCID列表", func(t *testing.T) {
		ctx := createEnterpriseCardTestContext(1, 1)

		ent := &model.Enterprise{
			EnterpriseName: "预检测试企业",
			EnterpriseCode: "ENT_PREVIEW_001",
			Status:         constants.StatusEnabled,
		}
		ent.Creator = 1
		ent.Updater = 1
		err := tx.Create(ent).Error
		require.NoError(t, err)

		req := &dto.AllocateCardsPreviewReq{
			ICCIDs: []string{},
		}

		result, err := service.AllocateCardsPreview(ctx, ent.ID, req)
		require.NoError(t, err)
		assert.NotNil(t, result)
		assert.Equal(t, 0, result.Summary.TotalCardCount)
	})

	t.Run("授权预检-卡不存在", func(t *testing.T) {
		ctx := createEnterpriseCardTestContext(1, 1)

		ent := &model.Enterprise{
			EnterpriseName: "预检测试企业2",
			EnterpriseCode: "ENT_PREVIEW_002",
			Status:         constants.StatusEnabled,
		}
		ent.Creator = 1
		ent.Updater = 1
		err := tx.Create(ent).Error
		require.NoError(t, err)

		req := &dto.AllocateCardsPreviewReq{
			ICCIDs: []string{"NON_EXIST_ICCID"},
		}

		result, err := service.AllocateCardsPreview(ctx, ent.ID, req)
		require.NoError(t, err)
		assert.NotNil(t, result)
		assert.Equal(t, 1, result.Summary.FailedCount)
		assert.Len(t, result.FailedItems, 1)
		assert.Equal(t, "卡不存在", result.FailedItems[0].Reason)
	})

	t.Run("授权预检-独立卡", func(t *testing.T) {
		ctx := createEnterpriseCardTestContext(1, 1)

		ent := &model.Enterprise{
			EnterpriseName: "预检测试企业3",
			EnterpriseCode: "ENT_PREVIEW_003",
			Status:         constants.StatusEnabled,
		}
		ent.Creator = 1
		ent.Updater = 1
		err := tx.Create(ent).Error
		require.NoError(t, err)

		shopID := uint(1)
		card := &model.IotCard{
			ICCID:  "898600001234567890",
			MSISDN: "13800000001",
			Status: 1,
			ShopID: &shopID,
		}
		err = tx.Create(card).Error
		require.NoError(t, err)

		req := &dto.AllocateCardsPreviewReq{
			ICCIDs: []string{"898600001234567890"},
		}

		result, err := service.AllocateCardsPreview(ctx, ent.ID, req)
		require.NoError(t, err)
		assert.NotNil(t, result)
		assert.Equal(t, 1, result.Summary.StandaloneCardCount)
		assert.Len(t, result.StandaloneCards, 1)
	})
}

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

	enterpriseStore := postgres.NewEnterpriseStore(tx, rdb)
	enterpriseCardAuthStore := postgres.NewEnterpriseCardAuthorizationStore(tx, rdb)

	service := enterprise_card.New(tx, enterpriseStore, enterpriseCardAuthStore)

	t.Run("授权卡-企业不存在应失败", func(t *testing.T) {
		ctx := createEnterpriseCardTestContext(1, 1)

		req := &dto.AllocateCardsReq{
			ICCIDs: []string{"898600000001"},
		}

		_, err := service.AllocateCards(ctx, 99999, req)
		assert.Error(t, err)
	})

	t.Run("授权卡-成功", func(t *testing.T) {
		ctx := createEnterpriseCardTestContext(1, 1)

		ent := &model.Enterprise{
			EnterpriseName: "授权测试企业",
			EnterpriseCode: "ENT_ALLOC_001",
			Status:         constants.StatusEnabled,
		}
		ent.Creator = 1
		ent.Updater = 1
		err := tx.Create(ent).Error
		require.NoError(t, err)

		shopID := uint(1)
		card := &model.IotCard{
			ICCID:  "898600002345678901",
			MSISDN: "13800000002",
			Status: 1,
			ShopID: &shopID,
		}
		err = tx.Create(card).Error
		require.NoError(t, err)

		req := &dto.AllocateCardsReq{
			ICCIDs: []string{"898600002345678901"},
		}

		result, err := service.AllocateCards(ctx, ent.ID, req)
		require.NoError(t, err)
		assert.NotNil(t, result)
		assert.Equal(t, 1, result.SuccessCount)
	})

	t.Run("授权卡-重复授权不创建新记录", func(t *testing.T) {
		ctx := createEnterpriseCardTestContext(1, 1)

		ent := &model.Enterprise{
			EnterpriseName: "重复授权测试企业",
			EnterpriseCode: "ENT_ALLOC_002",
			Status:         constants.StatusEnabled,
		}
		ent.Creator = 1
		ent.Updater = 1
		err := tx.Create(ent).Error
		require.NoError(t, err)

		shopID := uint(1)
		card := &model.IotCard{
			ICCID:  "898600003456789012",
			MSISDN: "13800000003",
			Status: 1,
			ShopID: &shopID,
		}
		err = tx.Create(card).Error
		require.NoError(t, err)

		req := &dto.AllocateCardsReq{
			ICCIDs: []string{"898600003456789012"},
		}

		_, err = service.AllocateCards(ctx, ent.ID, req)
		require.NoError(t, err)

		_, err = service.AllocateCards(ctx, ent.ID, req)
		require.NoError(t, err)

		var count int64
		tx.Model(&model.EnterpriseCardAuthorization{}).
			Where("enterprise_id = ? AND card_id = ?", ent.ID, card.ID).
			Count(&count)
		assert.Equal(t, int64(1), count)
	})
}

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

	enterpriseStore := postgres.NewEnterpriseStore(tx, rdb)
	enterpriseCardAuthStore := postgres.NewEnterpriseCardAuthorizationStore(tx, rdb)

	service := enterprise_card.New(tx, enterpriseStore, enterpriseCardAuthStore)

	t.Run("回收授权-企业不存在应失败", func(t *testing.T) {
		ctx := createEnterpriseCardTestContext(1, 1)

		req := &dto.RecallCardsReq{
			ICCIDs: []string{"898600000001"},
		}

		_, err := service.RecallCards(ctx, 99999, req)
		assert.Error(t, err)
	})

	t.Run("回收授权-卡未授权应失败", func(t *testing.T) {
		ctx := createEnterpriseCardTestContext(1, 1)

		ent := &model.Enterprise{
			EnterpriseName: "回收测试企业",
			EnterpriseCode: "ENT_RECALL_001",
			Status:         constants.StatusEnabled,
		}
		ent.Creator = 1
		ent.Updater = 1
		err := tx.Create(ent).Error
		require.NoError(t, err)

		shopID := uint(1)
		card := &model.IotCard{
			ICCID:  "898600004567890123",
			MSISDN: "13800000004",
			Status: 1,
			ShopID: &shopID,
		}
		err = tx.Create(card).Error
		require.NoError(t, err)

		req := &dto.RecallCardsReq{
			ICCIDs: []string{"898600004567890123"},
		}

		result, err := service.RecallCards(ctx, ent.ID, req)
		require.NoError(t, err)
		assert.Equal(t, 1, result.FailCount)
		assert.Equal(t, "该卡未授权给此企业", result.FailedItems[0].Reason)
	})

	t.Run("回收授权-成功", func(t *testing.T) {
		ctx := createEnterpriseCardTestContext(1, 1)

		ent := &model.Enterprise{
			EnterpriseName: "回收成功测试企业",
			EnterpriseCode: "ENT_RECALL_002",
			Status:         constants.StatusEnabled,
		}
		ent.Creator = 1
		ent.Updater = 1
		err := tx.Create(ent).Error
		require.NoError(t, err)

		shopID := uint(1)
		card := &model.IotCard{
			ICCID:  "898600005678901234",
			MSISDN: "13800000005",
			Status: 1,
			ShopID: &shopID,
		}
		err = tx.Create(card).Error
		require.NoError(t, err)

		allocReq := &dto.AllocateCardsReq{
			ICCIDs: []string{"898600005678901234"},
		}
		_, err = service.AllocateCards(ctx, ent.ID, allocReq)
		require.NoError(t, err)

		recallReq := &dto.RecallCardsReq{
			ICCIDs: []string{"898600005678901234"},
		}
		result, err := service.RecallCards(ctx, ent.ID, recallReq)
		require.NoError(t, err)
		assert.Equal(t, 1, result.SuccessCount)
		assert.Equal(t, 0, result.FailCount)
	})
}

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

	enterpriseStore := postgres.NewEnterpriseStore(tx, rdb)
	enterpriseCardAuthStore := postgres.NewEnterpriseCardAuthorizationStore(tx, rdb)

	service := enterprise_card.New(tx, enterpriseStore, enterpriseCardAuthStore)

	t.Run("查询企业卡列表-企业不存在应失败", func(t *testing.T) {
		ctx := createEnterpriseCardTestContext(1, 1)

		req := &dto.EnterpriseCardListReq{
			Page:     1,
			PageSize: 20,
		}

		_, err := service.ListCards(ctx, 99999, req)
		assert.Error(t, err)
	})

	t.Run("查询企业卡列表-空结果", func(t *testing.T) {
		ctx := createEnterpriseCardTestContext(1, 1)

		ent := &model.Enterprise{
			EnterpriseName: "列表测试企业",
			EnterpriseCode: "ENT_LIST_001",
			Status:         constants.StatusEnabled,
		}
		ent.Creator = 1
		ent.Updater = 1
		err := tx.Create(ent).Error
		require.NoError(t, err)

		req := &dto.EnterpriseCardListReq{
			Page:     1,
			PageSize: 20,
		}

		result, err := service.ListCards(ctx, ent.ID, req)
		require.NoError(t, err)
		assert.NotNil(t, result)
		assert.Equal(t, int64(0), result.Total)
	})

	t.Run("查询企业卡列表-有数据", func(t *testing.T) {
		ctx := createEnterpriseCardTestContext(1, 1)

		ent := &model.Enterprise{
			EnterpriseName: "列表数据测试企业",
			EnterpriseCode: "ENT_LIST_002",
			Status:         constants.StatusEnabled,
		}
		ent.Creator = 1
		ent.Updater = 1
		err := tx.Create(ent).Error
		require.NoError(t, err)

		shopID := uint(1)
		card := &model.IotCard{
			ICCID:  "898600006789012345",
			MSISDN: "13800000006",
			Status: 1,
			ShopID: &shopID,
		}
		err = tx.Create(card).Error
		require.NoError(t, err)

		allocReq := &dto.AllocateCardsReq{
			ICCIDs: []string{"898600006789012345"},
		}
		_, err = service.AllocateCards(ctx, ent.ID, allocReq)
		require.NoError(t, err)

		req := &dto.EnterpriseCardListReq{
			Page:     1,
			PageSize: 20,
		}

		result, err := service.ListCards(ctx, ent.ID, req)
		require.NoError(t, err)
		assert.NotNil(t, result)
		assert.Equal(t, int64(1), result.Total)
		assert.Len(t, result.Items, 1)
		assert.Equal(t, "898600006789012345", result.Items[0].ICCID)
	})

	t.Run("查询企业卡列表-按ICCID筛选", func(t *testing.T) {
		ctx := createEnterpriseCardTestContext(1, 1)

		ent := &model.Enterprise{
			EnterpriseName: "筛选测试企业",
			EnterpriseCode: "ENT_LIST_003",
			Status:         constants.StatusEnabled,
		}
		ent.Creator = 1
		ent.Updater = 1
		err := tx.Create(ent).Error
		require.NoError(t, err)

		shopID := uint(1)
		card := &model.IotCard{
			ICCID:  "898600007890123456",
			MSISDN: "13800000007",
			Status: 1,
			ShopID: &shopID,
		}
		err = tx.Create(card).Error
		require.NoError(t, err)

		allocReq := &dto.AllocateCardsReq{
			ICCIDs: []string{"898600007890123456"},
		}
		_, err = service.AllocateCards(ctx, ent.ID, allocReq)
		require.NoError(t, err)

		req := &dto.EnterpriseCardListReq{
			Page:     1,
			PageSize: 20,
			ICCID:    "78901",
		}

		result, err := service.ListCards(ctx, ent.ID, req)
		require.NoError(t, err)
		assert.NotNil(t, result)
		assert.GreaterOrEqual(t, result.Total, int64(1))
	})
}

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

	enterpriseStore := postgres.NewEnterpriseStore(tx, rdb)
	enterpriseCardAuthStore := postgres.NewEnterpriseCardAuthorizationStore(tx, rdb)

	service := enterprise_card.New(tx, enterpriseStore, enterpriseCardAuthStore)

	t.Run("停机-未授权的卡应失败", func(t *testing.T) {
		ctx := createEnterpriseCardTestContext(1, 1)

		ent := &model.Enterprise{
			EnterpriseName: "停机测试企业",
			EnterpriseCode: "ENT_SUSPEND_001",
			Status:         constants.StatusEnabled,
		}
		ent.Creator = 1
		ent.Updater = 1
		err := tx.Create(ent).Error
		require.NoError(t, err)

		shopID := uint(1)
		card := &model.IotCard{
			ICCID:  "898600008901234567",
			MSISDN: "13800000008",
			Status: 1,
			ShopID: &shopID,
		}
		err = tx.Create(card).Error
		require.NoError(t, err)

		err = service.SuspendCard(ctx, ent.ID, card.ID)
		assert.Error(t, err)
	})

	t.Run("停机和复机-成功", func(t *testing.T) {
		ctx := createEnterpriseCardTestContext(1, 1)

		ent := &model.Enterprise{
			EnterpriseName: "停复机测试企业",
			EnterpriseCode: "ENT_SUSPEND_002",
			Status:         constants.StatusEnabled,
		}
		ent.Creator = 1
		ent.Updater = 1
		err := tx.Create(ent).Error
		require.NoError(t, err)

		shopID := uint(1)
		card := &model.IotCard{
			ICCID:         "898600009012345678",
			MSISDN:        "13800000009",
			Status:        1,
			NetworkStatus: 1,
			ShopID:        &shopID,
		}
		err = tx.Create(card).Error
		require.NoError(t, err)

		allocReq := &dto.AllocateCardsReq{
			ICCIDs: []string{"898600009012345678"},
		}
		_, err = service.AllocateCards(ctx, ent.ID, allocReq)
		require.NoError(t, err)

		err = service.SuspendCard(ctx, ent.ID, card.ID)
		require.NoError(t, err)

		var suspendedCard model.IotCard
		tx.First(&suspendedCard, card.ID)
		assert.Equal(t, 0, suspendedCard.NetworkStatus)

		err = service.ResumeCard(ctx, ent.ID, card.ID)
		require.NoError(t, err)

		var resumedCard model.IotCard
		tx.First(&resumedCard, card.ID)
		assert.Equal(t, 1, resumedCard.NetworkStatus)
	})
}
