package device

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

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

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

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

	deviceStore := postgres.NewDeviceStore(tx, rdb)
	deviceSimBindingStore := postgres.NewDeviceSimBindingStore(tx, rdb)
	iotCardStore := postgres.NewIotCardStore(tx, rdb)
	shopStore := postgres.NewShopStore(tx, rdb)
	assetAllocationRecordStore := postgres.NewAssetAllocationRecordStore(tx, rdb)
	seriesAllocationStore := postgres.NewShopSeriesAllocationStore(tx)
	packageSeriesStore := postgres.NewPackageSeriesStore(tx)

	svc := New(tx, deviceStore, deviceSimBindingStore, iotCardStore, shopStore, assetAllocationRecordStore, seriesAllocationStore, packageSeriesStore)
	ctx := context.Background()

	shop := &model.Shop{
		ShopName: "测试店铺",
		ShopCode: fmt.Sprintf("SHOP%d", time.Now().UnixNano()%1000000),
		Level:    1,
		Status:   1,
	}
	require.NoError(t, tx.Create(shop).Error)

	series := &model.PackageSeries{
		SeriesCode: fmt.Sprintf("SERIES%d", time.Now().UnixNano()%1000000),
		SeriesName: "测试系列",
		Status:     1,
	}
	require.NoError(t, tx.Create(series).Error)

	allocation := &model.ShopSeriesAllocation{
		ShopID:   shop.ID,
		SeriesID: series.ID,
		Status:   1,
	}
	require.NoError(t, tx.Create(allocation).Error)

	prefix := uniqueTestDeviceNoPrefix()
	devices := []*model.Device{
		{DeviceNo: prefix + "001", DeviceName: "测试设备1", Status: 1, ShopID: &shop.ID},
		{DeviceNo: prefix + "002", DeviceName: "测试设备2", Status: 1, ShopID: &shop.ID},
		{DeviceNo: prefix + "003", DeviceName: "测试设备3", Status: 1, ShopID: nil},
	}
	require.NoError(t, deviceStore.CreateBatch(ctx, devices))

	t.Run("成功设置系列绑定", func(t *testing.T) {
		req := &dto.BatchSetDeviceSeriesBindngRequest{
			DeviceIDs: []uint{devices[0].ID, devices[1].ID},
			SeriesID:  allocation.SeriesID,
		}

		resp, err := svc.BatchSetSeriesBinding(ctx, req, nil)
		require.NoError(t, err)
		assert.Equal(t, 2, resp.SuccessCount)
		assert.Equal(t, 0, resp.FailCount)

		var updatedDevices []*model.Device
		require.NoError(t, tx.Where("id IN ?", req.DeviceIDs).Find(&updatedDevices).Error)
		for _, device := range updatedDevices {
			require.NotNil(t, device.SeriesID)
			assert.Equal(t, allocation.SeriesID, *device.SeriesID)
		}
	})

	t.Run("设备不属于套餐系列分配的店铺", func(t *testing.T) {
		req := &dto.BatchSetDeviceSeriesBindngRequest{
			DeviceIDs: []uint{devices[2].ID},
			SeriesID:  allocation.SeriesID,
		}

		resp, err := svc.BatchSetSeriesBinding(ctx, req, nil)
		require.NoError(t, err)
		assert.Equal(t, 0, resp.SuccessCount)
		assert.Equal(t, 1, resp.FailCount)
		assert.Equal(t, "设备不属于套餐系列分配的店铺", resp.FailedItems[0].Reason)
	})

	t.Run("设备不存在", func(t *testing.T) {
		req := &dto.BatchSetDeviceSeriesBindngRequest{
			DeviceIDs: []uint{99999},
			SeriesID:  allocation.SeriesID,
		}

		resp, err := svc.BatchSetSeriesBinding(ctx, req, nil)
		require.NoError(t, err)
		assert.Equal(t, 0, resp.SuccessCount)
		assert.Equal(t, 1, resp.FailCount)
		assert.Equal(t, "设备不存在", resp.FailedItems[0].Reason)
	})

	t.Run("清除系列绑定", func(t *testing.T) {
		req := &dto.BatchSetDeviceSeriesBindngRequest{
			DeviceIDs: []uint{devices[0].ID},
			SeriesID:  0,
		}

		resp, err := svc.BatchSetSeriesBinding(ctx, req, nil)
		require.NoError(t, err)
		assert.Equal(t, 1, resp.SuccessCount)

		var updatedDevice model.Device
		require.NoError(t, tx.First(&updatedDevice, devices[0].ID).Error)
		assert.Nil(t, updatedDevice.SeriesID)
	})

	t.Run("代理用户只能操作自己店铺的设备", func(t *testing.T) {
		otherShopID := uint(99999)
		req := &dto.BatchSetDeviceSeriesBindngRequest{
			DeviceIDs: []uint{devices[1].ID},
			SeriesID:  0,
		}

		resp, err := svc.BatchSetSeriesBinding(ctx, req, &otherShopID)
		require.NoError(t, err)
		assert.Equal(t, 0, resp.SuccessCount)
		assert.Equal(t, 1, resp.FailCount)
		assert.Equal(t, "无权操作此设备", resp.FailedItems[0].Reason)
	})

	t.Run("套餐系列分配不存在", func(t *testing.T) {
		req := &dto.BatchSetDeviceSeriesBindngRequest{
			DeviceIDs: []uint{devices[1].ID},
			SeriesID:  99999,
		}

		_, err := svc.BatchSetSeriesBinding(ctx, req, nil)
		require.Error(t, err)
	})
}
