package postgres

import (
	"context"
	"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 TestOrderStore_Create(t *testing.T) {
	tx := testutils.NewTestTransaction(t)
	rdb := testutils.GetTestRedis(t)
	testutils.CleanTestRedisKeys(t, rdb)
	s := NewOrderStore(tx, rdb)
	ctx := context.Background()

	cardID := uint(1001)
	order := &model.Order{
		OrderNo:       s.GenerateOrderNo(),
		OrderType:     model.OrderTypeSingleCard,
		BuyerType:     model.BuyerTypePersonal,
		BuyerID:       100,
		IotCardID:     &cardID,
		TotalAmount:   9900,
		PaymentStatus: model.PaymentStatusPending,
	}
	items := []*model.OrderItem{
		{
			PackageID:   1,
			PackageName: "测试套餐1",
			Quantity:    1,
			UnitPrice:   5000,
			Amount:      5000,
		},
		{
			PackageID:   2,
			PackageName: "测试套餐2",
			Quantity:    1,
			UnitPrice:   4900,
			Amount:      4900,
		},
	}

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

	for _, item := range items {
		assert.NotZero(t, item.ID)
		assert.Equal(t, order.ID, item.OrderID)
	}
}

func TestOrderStore_GetByID(t *testing.T) {
	tx := testutils.NewTestTransaction(t)
	rdb := testutils.GetTestRedis(t)
	testutils.CleanTestRedisKeys(t, rdb)
	s := NewOrderStore(tx, rdb)
	ctx := context.Background()

	order := &model.Order{
		OrderNo:       s.GenerateOrderNo(),
		OrderType:     model.OrderTypeSingleCard,
		BuyerType:     model.BuyerTypeAgent,
		BuyerID:       200,
		TotalAmount:   19900,
		PaymentStatus: model.PaymentStatusPending,
	}
	require.NoError(t, s.Create(ctx, order, nil))

	t.Run("查询存在的订单", func(t *testing.T) {
		result, err := s.GetByID(ctx, order.ID)
		require.NoError(t, err)
		assert.Equal(t, order.OrderNo, result.OrderNo)
		assert.Equal(t, order.BuyerType, result.BuyerType)
		assert.Equal(t, order.TotalAmount, result.TotalAmount)
	})

	t.Run("查询不存在的订单", func(t *testing.T) {
		_, err := s.GetByID(ctx, 99999)
		require.Error(t, err)
	})
}

func TestOrderStore_GetByIDWithItems(t *testing.T) {
	tx := testutils.NewTestTransaction(t)
	rdb := testutils.GetTestRedis(t)
	testutils.CleanTestRedisKeys(t, rdb)
	s := NewOrderStore(tx, rdb)
	ctx := context.Background()

	deviceID := uint(2001)
	order := &model.Order{
		OrderNo:       s.GenerateOrderNo(),
		OrderType:     model.OrderTypeDevice,
		BuyerType:     model.BuyerTypePersonal,
		BuyerID:       300,
		DeviceID:      &deviceID,
		TotalAmount:   29900,
		PaymentStatus: model.PaymentStatusPending,
	}
	items := []*model.OrderItem{
		{PackageID: 10, PackageName: "设备套餐A", Quantity: 1, UnitPrice: 15000, Amount: 15000},
		{PackageID: 11, PackageName: "设备套餐B", Quantity: 1, UnitPrice: 14900, Amount: 14900},
	}
	require.NoError(t, s.Create(ctx, order, items))

	resultOrder, resultItems, err := s.GetByIDWithItems(ctx, order.ID)
	require.NoError(t, err)
	assert.Equal(t, order.OrderNo, resultOrder.OrderNo)
	assert.Len(t, resultItems, 2)
}

func TestOrderStore_GetByOrderNo(t *testing.T) {
	tx := testutils.NewTestTransaction(t)
	rdb := testutils.GetTestRedis(t)
	testutils.CleanTestRedisKeys(t, rdb)
	s := NewOrderStore(tx, rdb)
	ctx := context.Background()

	orderNo := s.GenerateOrderNo()
	order := &model.Order{
		OrderNo:       orderNo,
		OrderType:     model.OrderTypeSingleCard,
		BuyerType:     model.BuyerTypeAgent,
		BuyerID:       400,
		TotalAmount:   5000,
		PaymentStatus: model.PaymentStatusPending,
	}
	require.NoError(t, s.Create(ctx, order, nil))

	t.Run("查询存在的订单号", func(t *testing.T) {
		result, err := s.GetByOrderNo(ctx, orderNo)
		require.NoError(t, err)
		assert.Equal(t, order.ID, result.ID)
	})

	t.Run("查询不存在的订单号", func(t *testing.T) {
		_, err := s.GetByOrderNo(ctx, "NOT_EXISTS_ORDER_NO")
		require.Error(t, err)
	})
}

func TestOrderStore_Update(t *testing.T) {
	tx := testutils.NewTestTransaction(t)
	rdb := testutils.GetTestRedis(t)
	testutils.CleanTestRedisKeys(t, rdb)
	s := NewOrderStore(tx, rdb)
	ctx := context.Background()

	order := &model.Order{
		OrderNo:       s.GenerateOrderNo(),
		OrderType:     model.OrderTypeSingleCard,
		BuyerType:     model.BuyerTypePersonal,
		BuyerID:       500,
		TotalAmount:   10000,
		PaymentStatus: model.PaymentStatusPending,
	}
	require.NoError(t, s.Create(ctx, order, nil))

	order.PaymentMethod = model.PaymentMethodWallet
	order.PaymentStatus = model.PaymentStatusPaid
	now := time.Now()
	order.PaidAt = &now
	err := s.Update(ctx, order)
	require.NoError(t, err)

	updated, err := s.GetByID(ctx, order.ID)
	require.NoError(t, err)
	assert.Equal(t, model.PaymentMethodWallet, updated.PaymentMethod)
	assert.Equal(t, model.PaymentStatusPaid, updated.PaymentStatus)
	assert.NotNil(t, updated.PaidAt)
}

func TestOrderStore_UpdatePaymentStatus(t *testing.T) {
	tx := testutils.NewTestTransaction(t)
	rdb := testutils.GetTestRedis(t)
	testutils.CleanTestRedisKeys(t, rdb)
	s := NewOrderStore(tx, rdb)
	ctx := context.Background()

	order := &model.Order{
		OrderNo:       s.GenerateOrderNo(),
		OrderType:     model.OrderTypeSingleCard,
		BuyerType:     model.BuyerTypeAgent,
		BuyerID:       600,
		TotalAmount:   8000,
		PaymentStatus: model.PaymentStatusPending,
	}
	require.NoError(t, s.Create(ctx, order, nil))

	now := time.Now()
	err := s.UpdatePaymentStatus(ctx, order.ID, model.PaymentStatusPaid, &now)
	require.NoError(t, err)

	updated, err := s.GetByID(ctx, order.ID)
	require.NoError(t, err)
	assert.Equal(t, model.PaymentStatusPaid, updated.PaymentStatus)
	assert.NotNil(t, updated.PaidAt)
}

func TestOrderStore_List(t *testing.T) {
	tx := testutils.NewTestTransaction(t)
	rdb := testutils.GetTestRedis(t)
	testutils.CleanTestRedisKeys(t, rdb)
	s := NewOrderStore(tx, rdb)
	ctx := context.Background()

	orders := []*model.Order{
		{OrderNo: s.GenerateOrderNo(), OrderType: model.OrderTypeSingleCard, BuyerType: model.BuyerTypePersonal, BuyerID: 700, TotalAmount: 1000, PaymentStatus: model.PaymentStatusPending},
		{OrderNo: s.GenerateOrderNo(), OrderType: model.OrderTypeDevice, BuyerType: model.BuyerTypeAgent, BuyerID: 701, TotalAmount: 2000, PaymentStatus: model.PaymentStatusPaid},
		{OrderNo: s.GenerateOrderNo(), OrderType: model.OrderTypeSingleCard, BuyerType: model.BuyerTypeAgent, BuyerID: 701, TotalAmount: 3000, PaymentStatus: model.PaymentStatusCancelled},
	}
	for _, o := range orders {
		require.NoError(t, s.Create(ctx, o, nil))
	}

	t.Run("查询所有订单", func(t *testing.T) {
		result, total, err := s.List(ctx, &store.QueryOptions{Page: 1, PageSize: 20}, nil)
		require.NoError(t, err)
		assert.GreaterOrEqual(t, total, int64(3))
		assert.GreaterOrEqual(t, len(result), 3)
	})

	t.Run("按支付状态过滤", func(t *testing.T) {
		filters := map[string]any{"payment_status": model.PaymentStatusPending}
		result, total, err := s.List(ctx, &store.QueryOptions{Page: 1, PageSize: 20}, filters)
		require.NoError(t, err)
		assert.GreaterOrEqual(t, total, int64(1))
		for _, o := range result {
			assert.Equal(t, model.PaymentStatusPending, o.PaymentStatus)
		}
	})

	t.Run("按订单类型过滤", func(t *testing.T) {
		filters := map[string]any{"order_type": model.OrderTypeDevice}
		result, total, err := s.List(ctx, &store.QueryOptions{Page: 1, PageSize: 20}, filters)
		require.NoError(t, err)
		assert.GreaterOrEqual(t, total, int64(1))
		for _, o := range result {
			assert.Equal(t, model.OrderTypeDevice, o.OrderType)
		}
	})

	t.Run("按买家过滤", func(t *testing.T) {
		filters := map[string]any{"buyer_type": model.BuyerTypeAgent, "buyer_id": uint(701)}
		result, total, err := s.List(ctx, &store.QueryOptions{Page: 1, PageSize: 20}, filters)
		require.NoError(t, err)
		assert.GreaterOrEqual(t, total, int64(2))
		for _, o := range result {
			assert.Equal(t, model.BuyerTypeAgent, o.BuyerType)
			assert.Equal(t, uint(701), o.BuyerID)
		}
	})

	t.Run("分页查询", func(t *testing.T) {
		result, total, err := s.List(ctx, &store.QueryOptions{Page: 1, PageSize: 2}, nil)
		require.NoError(t, err)
		assert.GreaterOrEqual(t, total, int64(3))
		assert.LessOrEqual(t, len(result), 2)
	})

	t.Run("默认分页选项", func(t *testing.T) {
		result, _, err := s.List(ctx, nil, nil)
		require.NoError(t, err)
		assert.NotNil(t, result)
	})
}

func TestOrderStore_GenerateOrderNo(t *testing.T) {
	tx := testutils.NewTestTransaction(t)
	rdb := testutils.GetTestRedis(t)
	s := NewOrderStore(tx, rdb)

	orderNo1 := s.GenerateOrderNo()
	orderNo2 := s.GenerateOrderNo()

	assert.True(t, len(orderNo1) > 0)
	assert.True(t, len(orderNo1) <= 30)
	assert.Contains(t, orderNo1, "ORD")
	assert.NotEqual(t, orderNo1, orderNo2)
}
