package postgres

import (
	"context"
	"fmt"
	"testing"
	"time"

	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/internal/store"
	"github.com/break/junhong_cmp_fiber/tests/testutils"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func uniqueICCIDPrefix() string {
	return fmt.Sprintf("T%d", time.Now().UnixNano()%1000000000)
}

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

	s := NewIotCardStore(tx, rdb)
	ctx := context.Background()

	card := &model.IotCard{
		ICCID:     "89860012345678901234",
		CardType:  "data_card",
		CarrierID: 1,
		Status:    1,
	}

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

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

	s := NewIotCardStore(tx, rdb)
	ctx := context.Background()

	card := &model.IotCard{
		ICCID:     "89860012345678901111",
		CardType:  "data_card",
		CarrierID: 1,
		Status:    1,
	}
	require.NoError(t, s.Create(ctx, card))

	exists, err := s.ExistsByICCID(ctx, "89860012345678901111")
	require.NoError(t, err)
	assert.True(t, exists)

	exists, err = s.ExistsByICCID(ctx, "89860012345678909999")
	require.NoError(t, err)
	assert.False(t, exists)
}

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

	s := NewIotCardStore(tx, rdb)
	ctx := context.Background()

	cards := []*model.IotCard{
		{ICCID: "89860012345678902001", CardType: "data_card", CarrierID: 1, Status: 1},
		{ICCID: "89860012345678902002", CardType: "data_card", CarrierID: 1, Status: 1},
		{ICCID: "89860012345678902003", CardType: "data_card", CarrierID: 1, Status: 1},
	}
	require.NoError(t, s.CreateBatch(ctx, cards))

	result, err := s.ExistsByICCIDBatch(ctx, []string{
		"89860012345678902001",
		"89860012345678902002",
		"89860012345678909999",
	})
	require.NoError(t, err)
	assert.True(t, result["89860012345678902001"])
	assert.True(t, result["89860012345678902002"])
	assert.False(t, result["89860012345678909999"])

	emptyResult, err := s.ExistsByICCIDBatch(ctx, []string{})
	require.NoError(t, err)
	assert.Empty(t, emptyResult)
}

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

	s := NewIotCardStore(tx, rdb)
	ctx := context.Background()

	prefix := uniqueICCIDPrefix()
	standaloneCards := []*model.IotCard{
		{ICCID: prefix + "0001", CardType: "data_card", CarrierID: 1, Status: 1},
		{ICCID: prefix + "0002", CardType: "data_card", CarrierID: 1, Status: 1},
		{ICCID: prefix + "0003", CardType: "data_card", CarrierID: 2, Status: 2},
	}
	require.NoError(t, s.CreateBatch(ctx, standaloneCards))

	boundCard := &model.IotCard{
		ICCID:     prefix + "0004",
		CardType:  "data_card",
		CarrierID: 1,
		Status:    1,
	}
	require.NoError(t, s.Create(ctx, boundCard))

	binding := &model.DeviceSimBinding{
		DeviceID:   1,
		IotCardID:  boundCard.ID,
		BindStatus: 1,
	}
	require.NoError(t, tx.Create(binding).Error)

	t.Run("查询所有单卡", func(t *testing.T) {
		filters := map[string]interface{}{"iccid": prefix}
		cards, total, err := s.ListStandalone(ctx, &store.QueryOptions{Page: 1, PageSize: 20}, filters)
		require.NoError(t, err)
		assert.Equal(t, int64(3), total)
		assert.Len(t, cards, 3)

		for _, card := range cards {
			assert.NotEqual(t, boundCard.ID, card.ID, "已绑定的卡不应出现在单卡列表中")
		}
	})

	t.Run("按运营商ID过滤", func(t *testing.T) {
		filters := map[string]interface{}{"carrier_id": uint(1), "iccid": prefix}
		cards, total, err := s.ListStandalone(ctx, &store.QueryOptions{Page: 1, PageSize: 20}, filters)
		require.NoError(t, err)
		assert.Equal(t, int64(2), total)
		for _, card := range cards {
			assert.Equal(t, uint(1), card.CarrierID)
		}
	})

	t.Run("按状态过滤", func(t *testing.T) {
		filters := map[string]interface{}{"status": 2, "iccid": prefix}
		cards, total, err := s.ListStandalone(ctx, &store.QueryOptions{Page: 1, PageSize: 20}, filters)
		require.NoError(t, err)
		assert.Equal(t, int64(1), total)
		assert.Len(t, cards, 1)
		assert.Equal(t, 2, cards[0].Status)
	})

	t.Run("按ICCID模糊查询", func(t *testing.T) {
		filters := map[string]interface{}{"iccid": prefix + "0001"}
		cards, total, err := s.ListStandalone(ctx, &store.QueryOptions{Page: 1, PageSize: 20}, filters)
		require.NoError(t, err)
		assert.Equal(t, int64(1), total)
		assert.Contains(t, cards[0].ICCID, prefix+"0001")
	})

	t.Run("分页查询", func(t *testing.T) {
		filters := map[string]interface{}{"iccid": prefix}
		cards, total, err := s.ListStandalone(ctx, &store.QueryOptions{Page: 1, PageSize: 2}, filters)
		require.NoError(t, err)
		assert.Equal(t, int64(3), total)
		assert.Len(t, cards, 2)

		cards2, _, err := s.ListStandalone(ctx, &store.QueryOptions{Page: 2, PageSize: 2}, filters)
		require.NoError(t, err)
		assert.Len(t, cards2, 1)
	})

	t.Run("默认分页选项", func(t *testing.T) {
		filters := map[string]interface{}{"iccid": prefix}
		cards, total, err := s.ListStandalone(ctx, nil, filters)
		require.NoError(t, err)
		assert.Equal(t, int64(3), total)
		assert.Len(t, cards, 3)
	})
}

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

	s := NewIotCardStore(tx, rdb)
	ctx := context.Background()

	prefix := uniqueICCIDPrefix()
	batchPrefix := "B" + prefix
	msisdnPrefix := "199" + prefix[1:8]
	shopID := uint(time.Now().UnixNano() % 1000000)

	cards := []*model.IotCard{
		{ICCID: prefix + "A001", CardType: "data_card", CarrierID: 1, Status: 1, ShopID: &shopID, BatchNo: batchPrefix + "01", MSISDN: msisdnPrefix + "01"},
		{ICCID: prefix + "A002", CardType: "data_card", CarrierID: 1, Status: 1, ShopID: nil, BatchNo: batchPrefix + "01", MSISDN: msisdnPrefix + "02"},
		{ICCID: prefix + "A003", CardType: "data_card", CarrierID: 1, Status: 1, ShopID: nil, BatchNo: batchPrefix + "02", MSISDN: msisdnPrefix + "03"},
	}
	require.NoError(t, s.CreateBatch(ctx, cards))

	t.Run("按店铺ID过滤", func(t *testing.T) {
		filters := map[string]interface{}{"shop_id": shopID, "iccid": prefix}
		result, total, err := s.ListStandalone(ctx, nil, filters)
		require.NoError(t, err)
		assert.Equal(t, int64(1), total)
		assert.Equal(t, shopID, *result[0].ShopID)
	})

	t.Run("按批次号过滤", func(t *testing.T) {
		filters := map[string]interface{}{"batch_no": batchPrefix + "01", "iccid": prefix}
		_, total, err := s.ListStandalone(ctx, nil, filters)
		require.NoError(t, err)
		assert.Equal(t, int64(2), total)
	})

	t.Run("按MSISDN模糊查询", func(t *testing.T) {
		filters := map[string]interface{}{"msisdn": msisdnPrefix + "01"}
		result, total, err := s.ListStandalone(ctx, nil, filters)
		require.NoError(t, err)
		assert.Equal(t, int64(1), total)
		assert.Contains(t, result[0].MSISDN, msisdnPrefix+"01")
	})

	t.Run("已分销过滤-true", func(t *testing.T) {
		filters := map[string]interface{}{"is_distributed": true, "iccid": prefix}
		result, total, err := s.ListStandalone(ctx, nil, filters)
		require.NoError(t, err)
		assert.Equal(t, int64(1), total)
		assert.NotNil(t, result[0].ShopID)
	})

	t.Run("已分销过滤-false", func(t *testing.T) {
		filters := map[string]interface{}{"is_distributed": false, "iccid": prefix}
		result, total, err := s.ListStandalone(ctx, nil, filters)
		require.NoError(t, err)
		assert.Equal(t, int64(2), total)
		for _, card := range result {
			assert.Nil(t, card.ShopID)
		}
	})

	t.Run("ICCID范围查询", func(t *testing.T) {
		filters := map[string]interface{}{
			"iccid_start": prefix + "A001",
			"iccid_end":   prefix + "A002",
		}
		_, total, err := s.ListStandalone(ctx, nil, filters)
		require.NoError(t, err)
		assert.Equal(t, int64(2), total)
	})
}

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

	s := NewIotCardStore(tx, rdb)
	ctx := context.Background()

	cards := []*model.IotCard{
		{ICCID: "89860012345678905001", CardType: "data_card", CarrierID: 1, Status: 1},
		{ICCID: "89860012345678905002", CardType: "data_card", CarrierID: 1, Status: 1},
		{ICCID: "89860012345678905003", CardType: "data_card", CarrierID: 1, Status: 1},
	}
	require.NoError(t, s.CreateBatch(ctx, cards))

	t.Run("查询存在的ICCID", func(t *testing.T) {
		result, err := s.GetByICCIDs(ctx, []string{"89860012345678905001", "89860012345678905002"})
		require.NoError(t, err)
		assert.Len(t, result, 2)
	})

	t.Run("查询不存在的ICCID", func(t *testing.T) {
		result, err := s.GetByICCIDs(ctx, []string{"89860012345678909999"})
		require.NoError(t, err)
		assert.Len(t, result, 0)
	})

	t.Run("空列表返回nil", func(t *testing.T) {
		result, err := s.GetByICCIDs(ctx, []string{})
		require.NoError(t, err)
		assert.Nil(t, result)
	})
}

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

	s := NewIotCardStore(tx, rdb)
	ctx := context.Background()

	shopID := uint(100)
	cards := []*model.IotCard{
		{ICCID: "89860012345678906001", CardType: "data_card", CarrierID: 1, Status: 1, ShopID: nil},
		{ICCID: "89860012345678906002", CardType: "data_card", CarrierID: 1, Status: 1, ShopID: nil},
		{ICCID: "89860012345678906003", CardType: "data_card", CarrierID: 1, Status: 1, ShopID: &shopID},
		{ICCID: "89860012345678906004", CardType: "data_card", CarrierID: 1, Status: 1, ShopID: &shopID},
	}
	require.NoError(t, s.CreateBatch(ctx, cards))

	t.Run("平台查询未分配的卡", func(t *testing.T) {
		result, err := s.GetStandaloneByICCIDRange(ctx, "89860012345678906001", "89860012345678906004", nil)
		require.NoError(t, err)
		assert.Len(t, result, 2)
		for _, card := range result {
			assert.Nil(t, card.ShopID)
		}
	})

	t.Run("店铺查询自己的卡", func(t *testing.T) {
		result, err := s.GetStandaloneByICCIDRange(ctx, "89860012345678906001", "89860012345678906004", &shopID)
		require.NoError(t, err)
		assert.Len(t, result, 2)
		for _, card := range result {
			assert.Equal(t, shopID, *card.ShopID)
		}
	})
}

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

	s := NewIotCardStore(tx, rdb)
	ctx := context.Background()

	shopID := uint(100)
	cards := []*model.IotCard{
		{ICCID: "89860012345678907001", CardType: "data_card", CarrierID: 1, Status: 1, ShopID: nil, BatchNo: "BATCH001"},
		{ICCID: "89860012345678907002", CardType: "data_card", CarrierID: 2, Status: 1, ShopID: nil, BatchNo: "BATCH002"},
		{ICCID: "89860012345678907003", CardType: "data_card", CarrierID: 1, Status: 2, ShopID: &shopID, BatchNo: "BATCH001"},
	}
	require.NoError(t, s.CreateBatch(ctx, cards))

	t.Run("按运营商过滤", func(t *testing.T) {
		filters := map[string]any{"carrier_id": uint(1)}
		result, err := s.GetStandaloneByFilters(ctx, filters, nil)
		require.NoError(t, err)
		assert.Len(t, result, 1)
		assert.Equal(t, uint(1), result[0].CarrierID)
	})

	t.Run("按批次号过滤", func(t *testing.T) {
		filters := map[string]any{"batch_no": "BATCH001"}
		result, err := s.GetStandaloneByFilters(ctx, filters, &shopID)
		require.NoError(t, err)
		assert.Len(t, result, 1)
		assert.Equal(t, "BATCH001", result[0].BatchNo)
	})
}

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

	s := NewIotCardStore(tx, rdb)
	ctx := context.Background()

	cards := []*model.IotCard{
		{ICCID: "89860012345678908001", CardType: "data_card", CarrierID: 1, Status: 1},
		{ICCID: "89860012345678908002", CardType: "data_card", CarrierID: 1, Status: 1},
	}
	require.NoError(t, s.CreateBatch(ctx, cards))

	newShopID := uint(200)
	cardIDs := []uint{cards[0].ID, cards[1].ID}

	err := s.BatchUpdateShopIDAndStatus(ctx, cardIDs, &newShopID, 2)
	require.NoError(t, err)

	var updatedCards []*model.IotCard
	require.NoError(t, tx.Where("id IN ?", cardIDs).Find(&updatedCards).Error)
	for _, card := range updatedCards {
		assert.Equal(t, newShopID, *card.ShopID)
		assert.Equal(t, 2, card.Status)
	}

	t.Run("空列表不报错", func(t *testing.T) {
		err := s.BatchUpdateShopIDAndStatus(ctx, []uint{}, nil, 1)
		require.NoError(t, err)
	})
}

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

	s := NewIotCardStore(tx, rdb)
	ctx := context.Background()

	cards := []*model.IotCard{
		{ICCID: "89860012345678909001", CardType: "data_card", CarrierID: 1, Status: 1},
		{ICCID: "89860012345678909002", CardType: "data_card", CarrierID: 1, Status: 1},
		{ICCID: "89860012345678909003", CardType: "data_card", CarrierID: 1, Status: 1},
	}
	require.NoError(t, s.CreateBatch(ctx, cards))

	binding := &model.DeviceSimBinding{
		DeviceID:   1,
		IotCardID:  cards[0].ID,
		BindStatus: 1,
	}
	require.NoError(t, tx.Create(binding).Error)

	cardIDs := []uint{cards[0].ID, cards[1].ID, cards[2].ID}
	boundIDs, err := s.GetBoundCardIDs(ctx, cardIDs)
	require.NoError(t, err)
	assert.Len(t, boundIDs, 1)
	assert.Contains(t, boundIDs, cards[0].ID)

	t.Run("空列表返回nil", func(t *testing.T) {
		result, err := s.GetBoundCardIDs(ctx, []uint{})
		require.NoError(t, err)
		assert.Nil(t, result)
	})
}

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

	s := NewIotCardStore(tx, rdb)
	ctx := context.Background()

	cards := []*model.IotCard{
		{ICCID: "89860012345678910001", CardType: "data_card", CarrierID: 1, Status: 1},
		{ICCID: "89860012345678910002", CardType: "data_card", CarrierID: 1, Status: 1},
	}
	require.NoError(t, s.CreateBatch(ctx, cards))

	t.Run("设置系列ID", func(t *testing.T) {
		seriesID := uint(100)
		cardIDs := []uint{cards[0].ID, cards[1].ID}

		err := s.BatchUpdateSeriesID(ctx, cardIDs, &seriesID)
		require.NoError(t, err)

		var updatedCards []*model.IotCard
		require.NoError(t, tx.Where("id IN ?", cardIDs).Find(&updatedCards).Error)
		for _, card := range updatedCards {
			require.NotNil(t, card.SeriesID)
			assert.Equal(t, seriesID, *card.SeriesID)
		}
	})

	t.Run("清除系列ID", func(t *testing.T) {
		cardIDs := []uint{cards[0].ID}

		err := s.BatchUpdateSeriesID(ctx, cardIDs, nil)
		require.NoError(t, err)

		var updatedCard model.IotCard
		require.NoError(t, tx.First(&updatedCard, cards[0].ID).Error)
		assert.Nil(t, updatedCard.SeriesID)
	})

	t.Run("空列表不报错", func(t *testing.T) {
		err := s.BatchUpdateSeriesID(ctx, []uint{}, nil)
		require.NoError(t, err)
	})
}

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

	s := NewIotCardStore(tx, rdb)
	ctx := context.Background()

	seriesID := uint(200)
	cards := []*model.IotCard{
		{ICCID: "89860012345678911001", CardType: "data_card", CarrierID: 1, Status: 1, SeriesID: &seriesID},
		{ICCID: "89860012345678911002", CardType: "data_card", CarrierID: 1, Status: 1, SeriesID: &seriesID},
		{ICCID: "89860012345678911003", CardType: "data_card", CarrierID: 1, Status: 1, SeriesID: nil},
	}
	require.NoError(t, s.CreateBatch(ctx, cards))

	result, err := s.ListBySeriesID(ctx, seriesID)
	require.NoError(t, err)
	assert.Len(t, result, 2)
	for _, card := range result {
		assert.Equal(t, seriesID, *card.SeriesID)
	}
}

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

	s := NewIotCardStore(tx, rdb)
	ctx := context.Background()

	prefix := uniqueICCIDPrefix()
	seriesID := uint(300)
	cards := []*model.IotCard{
		{ICCID: prefix + "S001", CardType: "data_card", CarrierID: 1, Status: 1, SeriesID: &seriesID},
		{ICCID: prefix + "S002", CardType: "data_card", CarrierID: 1, Status: 1, SeriesID: &seriesID},
		{ICCID: prefix + "S003", CardType: "data_card", CarrierID: 1, Status: 1, SeriesID: nil},
	}
	require.NoError(t, s.CreateBatch(ctx, cards))

	filters := map[string]interface{}{
		"series_id": seriesID,
		"iccid":     prefix,
	}
	result, total, err := s.ListStandalone(ctx, nil, filters)
	require.NoError(t, err)
	assert.Equal(t, int64(2), total)
	assert.Len(t, result, 2)
	for _, card := range result {
		assert.Equal(t, seriesID, *card.SeriesID)
	}
}
