package integration

import (
	"context"
	"encoding/json"
	"fmt"
	"testing"
	"time"

	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	pkggorm "github.com/break/junhong_cmp_fiber/pkg/gorm"
	"github.com/break/junhong_cmp_fiber/pkg/response"
	"github.com/break/junhong_cmp_fiber/tests/testutils/integ"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestStandaloneCardAllocation_AllocateByList(t *testing.T) {
	env := integ.NewIntegrationTestEnv(t)

	// 创建测试数据
	shop := env.CreateTestShop("测试店铺", 1, nil)
	subShop := env.CreateTestShop("测试下级店铺", 2, &shop.ID)
	agentAccount := env.CreateTestAccount("agent_alloc", "password123", constants.UserTypeAgent, &shop.ID, nil)

	cards := []*model.IotCard{
		{ICCID: "ALLOC_TEST001", CardType: "data_card", CarrierID: 1, Status: constants.IotCardStatusInStock},
		{ICCID: "ALLOC_TEST002", CardType: "data_card", CarrierID: 1, Status: constants.IotCardStatusInStock},
		{ICCID: "ALLOC_TEST003", CardType: "data_card", CarrierID: 1, Status: constants.IotCardStatusInStock},
	}
	for _, card := range cards {
		require.NoError(t, env.TX.Create(card).Error)
	}

	t.Run("平台分配卡给一级店铺", func(t *testing.T) {
		reqBody := map[string]interface{}{
			"to_shop_id":     shop.ID,
			"selection_type": "list",
			"iccids":         []string{"ALLOC_TEST001", "ALLOC_TEST002"},
			"remark":         "测试分配",
		}
		bodyBytes, _ := json.Marshal(reqBody)

		resp, err := env.AsSuperAdmin().Request("POST", "/api/admin/iot-cards/standalone/allocate", bodyBytes)
		require.NoError(t, err)
		defer resp.Body.Close()

		var result response.Response
		err = json.NewDecoder(resp.Body).Decode(&result)
		require.NoError(t, err)

		t.Logf("Allocate response: code=%d, message=%s, data=%v", result.Code, result.Message, result.Data)

		assert.Equal(t, 200, resp.StatusCode)
		assert.Equal(t, 0, result.Code, "应返回成功: %s", result.Message)

		if result.Data != nil {
			dataMap := result.Data.(map[string]interface{})
			assert.Equal(t, float64(2), dataMap["total_count"])
			assert.Equal(t, float64(2), dataMap["success_count"])
			assert.Equal(t, float64(0), dataMap["fail_count"])
		}

		ctx := pkggorm.SkipDataPermission(context.Background())
		var updatedCards []model.IotCard
		env.RawDB().WithContext(ctx).Where("iccid IN ?", []string{"ALLOC_TEST001", "ALLOC_TEST002"}).Find(&updatedCards)
		for _, card := range updatedCards {
			assert.Equal(t, shop.ID, *card.ShopID, "卡应分配给目标店铺")
			assert.Equal(t, constants.IotCardStatusDistributed, card.Status, "状态应为已分销")
		}

		var recordCount int64
		env.RawDB().WithContext(ctx).Model(&model.AssetAllocationRecord{}).
			Where("asset_identifier IN ?", []string{"ALLOC_TEST001", "ALLOC_TEST002"}).
			Count(&recordCount)
		assert.Equal(t, int64(2), recordCount, "应创建2条分配记录")
	})

	t.Run("代理分配卡给下级店铺", func(t *testing.T) {
		reqBody := map[string]interface{}{
			"to_shop_id":     subShop.ID,
			"selection_type": "list",
			"iccids":         []string{"ALLOC_TEST001"},
			"remark":         "代理分配测试",
		}
		bodyBytes, _ := json.Marshal(reqBody)

		resp, err := env.AsUser(agentAccount).Request("POST", "/api/admin/iot-cards/standalone/allocate", bodyBytes)
		require.NoError(t, err)
		defer resp.Body.Close()

		var result response.Response
		err = json.NewDecoder(resp.Body).Decode(&result)
		require.NoError(t, err)

		t.Logf("Agent allocate response: code=%d, message=%s", result.Code, result.Message)

		assert.Equal(t, 200, resp.StatusCode)
		assert.Equal(t, 0, result.Code, "代理应能分配给下级: %s", result.Message)
	})

	t.Run("分配不存在的卡应返回空结果", func(t *testing.T) {
		reqBody := map[string]interface{}{
			"to_shop_id":     shop.ID,
			"selection_type": "list",
			"iccids":         []string{"NOT_EXISTS_001", "NOT_EXISTS_002"},
		}
		bodyBytes, _ := json.Marshal(reqBody)

		resp, err := env.AsSuperAdmin().Request("POST", "/api/admin/iot-cards/standalone/allocate", bodyBytes)
		require.NoError(t, err)
		defer resp.Body.Close()

		var result response.Response
		err = json.NewDecoder(resp.Body).Decode(&result)
		require.NoError(t, err)

		assert.Equal(t, 0, result.Code)
		if result.Data != nil {
			dataMap := result.Data.(map[string]interface{})
			assert.Equal(t, float64(0), dataMap["total_count"])
		}
	})
}

func TestStandaloneCardAllocation_Recall(t *testing.T) {
	env := integ.NewIntegrationTestEnv(t)

	// 创建测试数据
	shop := env.CreateTestShop("测试店铺", 1, nil)

	shopID := shop.ID
	cards := []*model.IotCard{
		{ICCID: "ALLOC_TEST101", CardType: "data_card", CarrierID: 1, Status: constants.IotCardStatusDistributed, ShopID: &shopID},
		{ICCID: "ALLOC_TEST102", CardType: "data_card", CarrierID: 1, Status: constants.IotCardStatusDistributed, ShopID: &shopID},
	}
	for _, card := range cards {
		require.NoError(t, env.TX.Create(card).Error)
	}

	t.Run("平台回收卡", func(t *testing.T) {
		reqBody := map[string]interface{}{
			"from_shop_id":   shop.ID,
			"selection_type": "list",
			"iccids":         []string{"ALLOC_TEST101"},
			"remark":         "平台回收测试",
		}
		bodyBytes, _ := json.Marshal(reqBody)

		resp, err := env.AsSuperAdmin().Request("POST", "/api/admin/iot-cards/standalone/recall", bodyBytes)
		require.NoError(t, err)
		defer resp.Body.Close()

		var result response.Response
		err = json.NewDecoder(resp.Body).Decode(&result)
		require.NoError(t, err)

		t.Logf("Recall response: code=%d, message=%s, data=%v", result.Code, result.Message, result.Data)

		assert.Equal(t, 200, resp.StatusCode)
		assert.Equal(t, 0, result.Code, "应返回成功: %s", result.Message)

		ctx := pkggorm.SkipDataPermission(context.Background())
		var recalledCard model.IotCard
		env.RawDB().WithContext(ctx).Where("iccid = ?", "ALLOC_TEST101").First(&recalledCard)
		assert.Nil(t, recalledCard.ShopID, "平台回收后shop_id应为NULL")
		assert.Equal(t, constants.IotCardStatusInStock, recalledCard.Status, "状态应恢复为在库")
	})
}

func TestAssetAllocationRecord_List(t *testing.T) {
	env := integ.NewIntegrationTestEnv(t)

	// 创建测试数据
	shop := env.CreateTestShop("测试店铺", 1, nil)

	var superAdminAccount model.Account
	require.NoError(t, env.RawDB().Where("user_type = ?", constants.UserTypeSuperAdmin).First(&superAdminAccount).Error)

	fromShopID := shop.ID
	records := []*model.AssetAllocationRecord{
		{
			AllocationNo:    fmt.Sprintf("AL%d001", time.Now().UnixNano()),
			AllocationType:  constants.AssetAllocationTypeAllocate,
			AssetType:       constants.AssetTypeIotCard,
			AssetID:         1,
			AssetIdentifier: "ALLOC_TEST_REC001",
			FromOwnerType:   constants.OwnerTypePlatform,
			ToOwnerType:     constants.OwnerTypeShop,
			ToOwnerID:       shop.ID,
			OperatorID:      superAdminAccount.ID,
		},
		{
			AllocationNo:    fmt.Sprintf("RC%d001", time.Now().UnixNano()),
			AllocationType:  constants.AssetAllocationTypeRecall,
			AssetType:       constants.AssetTypeIotCard,
			AssetID:         2,
			AssetIdentifier: "ALLOC_TEST_REC002",
			FromOwnerType:   constants.OwnerTypeShop,
			FromOwnerID:     &fromShopID,
			ToOwnerType:     constants.OwnerTypePlatform,
			ToOwnerID:       0,
			OperatorID:      superAdminAccount.ID,
		},
	}
	for _, record := range records {
		require.NoError(t, env.TX.Create(record).Error)
	}

	t.Run("获取分配记录列表", func(t *testing.T) {
		resp, err := env.AsSuperAdmin().Request("GET", "/api/admin/asset-allocation-records?page=1&page_size=20", nil)
		require.NoError(t, err)
		defer resp.Body.Close()

		assert.Equal(t, 200, resp.StatusCode)

		var result response.Response
		err = json.NewDecoder(resp.Body).Decode(&result)
		require.NoError(t, err)
		assert.Equal(t, 0, result.Code)
	})

	t.Run("按分配类型过滤", func(t *testing.T) {
		resp, err := env.AsSuperAdmin().Request("GET", "/api/admin/asset-allocation-records?allocation_type=allocate", nil)
		require.NoError(t, err)
		defer resp.Body.Close()

		var result response.Response
		err = json.NewDecoder(resp.Body).Decode(&result)
		require.NoError(t, err)
		assert.Equal(t, 0, result.Code)
	})

	t.Run("获取分配记录详情", func(t *testing.T) {
		url := fmt.Sprintf("/api/admin/asset-allocation-records/%d", records[0].ID)
		resp, err := env.AsSuperAdmin().Request("GET", url, nil)
		require.NoError(t, err)
		defer resp.Body.Close()

		assert.Equal(t, 200, resp.StatusCode)

		var result response.Response
		err = json.NewDecoder(resp.Body).Decode(&result)
		require.NoError(t, err)
		assert.Equal(t, 0, result.Code)
	})

	t.Run("未认证请求应返回错误", func(t *testing.T) {
		resp, err := env.ClearAuth().Request("GET", "/api/admin/asset-allocation-records", nil)
		require.NoError(t, err)
		defer resp.Body.Close()

		var result response.Response
		err = json.NewDecoder(resp.Body).Decode(&result)
		require.NoError(t, err)
		assert.NotEqual(t, 0, result.Code, "未认证请求应返回错误码")
	})
}
