package testutil

import (
	"encoding/json"
	"fmt"
	"net/http"
	"testing"
	"time"

	"wails-app/internal/dao"
	"wails-app/internal/global"
	"wails-app/internal/model/common/response"
	dt_svc "wails-app/internal/modules/digital-ticket/service"
)

// TestVehicleReEntry 同一车辆重复入场应被拦截
func TestVehicleReEntry(t *testing.T) {
	env := InitTestEnv(t)
	createVehicle(t, env, "JingR00001", "RFID_REENTRY", 1)
	entryResp, entryBody := env.DoPost("/vehicle/entry", map[string]interface{}{
		"plate_number": "JingR00001", "rfid_tag": "RFID_REENTRY",
		"parking_lot_id": 1, "parking_space_id": 1, "entry_image": "test.jpg",
	})
	AssertResponse(t, entryResp, entryBody, http.StatusOK, response.SUCCESS)

	_, entryBody2 := env.DoPost("/vehicle/entry", map[string]interface{}{
		"plate_number": "JingR00001", "rfid_tag": "RFID_REENTRY",
		"parking_lot_id": 1, "parking_space_id": 2, "entry_image": "test2.jpg",
	})
	var result struct { Code int `json:"code"`; Msg string `json:"msg"` }
	json.Unmarshal(entryBody2, &result)
	if result.Code == response.SUCCESS {
		t.Fatalf("BUG: Vehicle re-entry was NOT blocked! code=%d msg=%s", result.Code, result.Msg)
	}
	fmt.Println("PASS: Re-entry attempt correctly rejected")
}

// TestExitWithoutEntry 未入场车辆尝试出场应返回错误
func TestExitWithoutEntry(t *testing.T) {
	env := InitTestEnv(t)
	_, exitPreviewBody := env.DoPost("/vehicle/exit/preview", map[string]interface{}{
		"plate_number": "JingZ99999", "rfid_tag": "RFID_NOENTRY",
	})
	var result struct { Code int `json:"code"`; Msg string `json:"msg"` }
	json.Unmarshal(exitPreviewBody, &result)
	if result.Code == response.SUCCESS {
		t.Fatalf("BUG: Exit preview succeeded for non-entered vehicle!")
	}
	fmt.Println("PASS: Exit without entry correctly rejected")
}

// TestPaymentInsufficientAmount 支付金额不足时的行为
func TestPaymentInsufficientAmount(t *testing.T) {
	env := InitTestEnv(t)
	createVehicle(t, env, "JingP00001", "RFID_PAY", 1)
	_, _ = env.DoPost("/vehicle/entry", map[string]interface{}{
		"plate_number": "JingP00001", "rfid_tag": "RFID_PAY",
		"parking_lot_id": 1, "parking_space_id": 1, "entry_image": "test.jpg",
	})
	entryTime := time.Now().Add(-3 * time.Hour)
	global.GVA_DB.Model(&dao.VehicleRecord{}).Where("plate_number = ?", "JingP00001").Update("entry_time", entryTime)
	_, exitConfirmBody := env.DoPost("/vehicle/exit/confirm", map[string]interface{}{
		"plate_number": "JingP00001", "rfid_tag": "RFID_PAY",
		"payment_method": "cash", "paid_amount": 0.0,
	})
	var result struct { Code int `json:"code"`; Msg string `json:"msg"` }
	json.Unmarshal(exitConfirmBody, &result)
	fmt.Printf("INFO: Insufficient payment response: code=%d msg=%s\n", result.Code, result.Msg)
}

// TestVIPExpired 过期 VIP 应恢复普通计费
func TestVIPExpired(t *testing.T) {
	env := InitTestEnv(t)
	ownerResp, ownerBody := env.DoPost("/owner/create", map[string]interface{}{
		"owner_name": "VIP Expired Test", "owner_surname": "VIP",
		"owner_phone": "13900000001", "is_vip": true,
		"vip_expire_time": time.Now().Add(-24*time.Hour).Format(time.RFC3339),
	})
	AssertResponse(t, ownerResp, ownerBody, http.StatusOK, response.SUCCESS)

	var owner dao.Owner
	env.DB.Where("owner_phone = ?", "13900000001").First(&owner)
	createVehicleWithOwner(t, env, "JingV00001", "RFID_VIP_EXP", owner.ID)

	_, _ = env.DoPost("/vehicle/entry", map[string]interface{}{
		"plate_number": "JingV00001", "rfid_tag": "RFID_VIP_EXP",
		"parking_lot_id": 1, "parking_space_id": 1, "entry_image": "test.jpg",
	})
	entryTime := time.Now().Add(-3 * time.Hour)
	global.GVA_DB.Model(&dao.VehicleRecord{}).Where("plate_number = ?", "JingV00001").Update("entry_time", entryTime)

	exitResp, exitBody := env.DoPost("/vehicle/exit/preview", map[string]interface{}{
		"plate_number": "JingV00001", "rfid_tag": "RFID_VIP_EXP",
	})
	AssertResponse(t, exitResp, exitBody, http.StatusOK, response.SUCCESS)
	var result struct {
		Data struct {
			Fee float64 `json:"fee"`
		} `json:"data"`
	}
	json.Unmarshal(exitBody, &result)
	if result.Data.Fee == 0 {
		t.Fatalf("BUG: Expired VIP still getting free parking! Fee should be > 0")
	}
	fmt.Printf("PASS: Expired VIP charged correctly, fee=%.2f\n", result.Data.Fee)
}

// TestFreeDuration 免费时长内不应收费
func TestFreeDuration(t *testing.T) {
	env := InitTestEnv(t)
	createVehicle(t, env, "JingF00001", "RFID_FREE", 1)
	_, _ = env.DoPost("/vehicle/entry", map[string]interface{}{
		"plate_number": "JingF00001", "rfid_tag": "RFID_FREE",
		"parking_lot_id": 1, "parking_space_id": 1, "entry_image": "test.jpg",
	})
	entryTime := time.Now().Add(-10 * time.Minute)
	global.GVA_DB.Model(&dao.VehicleRecord{}).Where("plate_number = ?", "JingF00001").Update("entry_time", entryTime)
	exitResp, exitBody := env.DoPost("/vehicle/exit/preview", map[string]interface{}{
		"plate_number": "JingF00001", "rfid_tag": "RFID_FREE",
	})
	AssertResponse(t, exitResp, exitBody, http.StatusOK, response.SUCCESS)
	var preview struct { Fee float64 `json:"fee"` }
	json.Unmarshal(exitBody, &preview)
	if preview.Fee != 0 {
		t.Fatalf("BUG: Vehicle within free duration should have zero fee, got %.2f", preview.Fee)
	}
	fmt.Println("PASS: Free duration correctly applied")
}

// TestDailyCap 超过每日封顶费用应封顶
func TestDailyCap(t *testing.T) {
	env := InitTestEnv(t)
	createVehicle(t, env, "JingD00001", "RFID_CAP", 1)
	_, _ = env.DoPost("/vehicle/entry", map[string]interface{}{
		"plate_number": "JingD00001", "rfid_tag": "RFID_CAP",
		"parking_lot_id": 1, "parking_space_id": 1, "entry_image": "test.jpg",
	})
	entryTime := time.Now().Add(-24 * time.Hour)
	global.GVA_DB.Model(&dao.VehicleRecord{}).Where("plate_number = ?", "JingD00001").Update("entry_time", entryTime)
	exitResp, exitBody := env.DoPost("/vehicle/exit/preview", map[string]interface{}{
		"plate_number": "JingD00001", "rfid_tag": "RFID_CAP",
	})
	AssertResponse(t, exitResp, exitBody, http.StatusOK, response.SUCCESS)
	var preview struct { Fee float64 `json:"fee"` }
	json.Unmarshal(exitBody, &preview)
	if preview.Fee > 50.0 {
		t.Fatalf("BUG: Fee exceeds daily cap! Expected <= 50, got %.2f", preview.Fee)
	}
	fmt.Printf("PASS: Daily cap applied correctly, fee=%.2f\n", preview.Fee)
}

// TestNoFeeConfig 无收费配置时默认计费
func TestNoFeeConfig(t *testing.T) {
	env := InitTestEnv(t)
	var normalType dao.VehicleType
	env.DB.Where("is_system = ? AND name != ?", true, "临时车").First(&normalType)
	if normalType.ID == 0 {
		normalType = dao.VehicleType{Name: "特殊车", Remarks: "无收费配置"}
		env.DB.Create(&normalType)
	}
	env.DB.Where("vehicle_type_id = ?", normalType.ID).Delete(&dao.FeeConfig{})
	createVehicleWithOwner(t, env, "JingN00001", "RFID_NOFEE", normalType.ID)
	_, _ = env.DoPost("/vehicle/entry", map[string]interface{}{
		"plate_number": "JingN00001", "rfid_tag": "RFID_NOFEE",
		"parking_lot_id": 1, "parking_space_id": 1, "entry_image": "test.jpg",
	})
	entryTime := time.Now().Add(-1 * time.Hour)
	global.GVA_DB.Model(&dao.VehicleRecord{}).Where("plate_number = ?", "JingN00001").Update("entry_time", entryTime)
	exitResp, exitBody := env.DoPost("/vehicle/exit/preview", map[string]interface{}{
		"plate_number": "JingN00001", "rfid_tag": "RFID_NOFEE",
	})
	AssertResponse(t, exitResp, exitBody, http.StatusOK, response.SUCCESS)
	var result struct {
		Data struct {
			Fee float64 `json:"fee"`
		} `json:"data"`
	}
	json.Unmarshal(exitBody, &result)
	if result.Data.Fee <= 0 {
		t.Fatalf("BUG: No fee config should use default rate, got fee=%.2f", result.Data.Fee)
	}
	fmt.Printf("PASS: Default fee applied correctly, fee=%.2f\n", result.Data.Fee)
}

// TestShortlistByPlateOrRFID 黑白名单按车牌/RFID查询
func TestShortlistByPlateOrRFID(t *testing.T) {
	env := InitTestEnv(t)
	createVehicle(t, env, "JingS00001", "RFID_FILTER", 1)
	var vehicle dao.Vehicle
	env.DB.Where("plate_number = ?", "JingS00001").First(&vehicle)
	_, _ = env.DoPost("/shortlist/createShortlist", dao.Shortlist{
		ListType: "黑名单", VehicleId: int(vehicle.ID), ExpirationTime: nil,
	})

	resp1, body1 := env.DoPost("/shortlist/queryShortlistList", map[string]interface{}{
		"page": 1, "pageSize": 10, "list_type": "", "plate_number": "JingS00001",
	})
	AssertResponse(t, resp1, body1, http.StatusOK, response.SUCCESS)
	list1 := ParsePageList(body1)
	if len(list1) < 1 { t.Fatalf("Expected at least 1 shortlist record by plate number") }
	fmt.Printf("PASS: Shortlist query by plate number returned %d records\n", len(list1))

	resp2, body2 := env.DoPost("/shortlist/queryShortlistList", map[string]interface{}{
		"page": 1, "pageSize": 10, "list_type": "", "rfid_tag": "RFID_FILTER",
	})
	AssertResponse(t, resp2, body2, http.StatusOK, response.SUCCESS)
	list2 := ParsePageList(body2)
	if len(list2) < 1 { t.Fatalf("Expected at least 1 shortlist record by RFID") }
	fmt.Printf("PASS: Shortlist query by RFID returned %d records\n", len(list2))
}

// TestShortlistUpdate 黑白名单更新
func TestShortlistUpdate(t *testing.T) {
	env := InitTestEnv(t)
	createVehicle(t, env, "JingU00001", "RFID_UPDATE", 1)
	var vehicle dao.Vehicle
	env.DB.Where("plate_number = ?", "JingU00001").First(&vehicle)
	_, _ = env.DoPost("/shortlist/createShortlist", dao.Shortlist{
		ListType: "白名单", VehicleId: int(vehicle.ID), ExpirationTime: nil,
	})
	queryResp, queryBody := env.DoGet("/shortlist/queryAllShortlists", nil)
	AssertResponse(t, queryResp, queryBody, http.StatusOK, response.SUCCESS)
	var result struct { Data []map[string]interface{} `json:"data"` }
	json.Unmarshal(queryBody, &result)
	if len(result.Data) == 0 { t.Fatalf("No shortlist found") }
	idVal, ok := result.Data[0]["ID"]; if !ok { idVal = result.Data[0]["id"] }
	id := int(idVal.(float64))
	updateResp, updateBody := env.DoPut("/shortlist/updateShortlist", map[string]interface{}{
		"ID": id, "list_type": "黑名单", "vehicle_id": vehicle.ID,
		"expiration_time": time.Now().Add(30*24*time.Hour).Format(time.RFC3339),
	})
	AssertResponse(t, updateResp, updateBody, http.StatusOK, response.SUCCESS)
	updatedResp, updatedBody := env.DoGet("/shortlist/queryAllShortlists", nil)
	AssertResponse(t, updatedResp, updatedBody, http.StatusOK, response.SUCCESS)
	fmt.Println("PASS: Shortlist update successful")
}

// TestShortlistDelete 黑白名单删除
func TestShortlistDelete(t *testing.T) {
	env := InitTestEnv(t)
	createVehicle(t, env, "JingDel001", "RFID_DELETE", 1)
	var vehicle dao.Vehicle
	env.DB.Where("plate_number = ?", "JingDel001").First(&vehicle)
	_, _ = env.DoPost("/shortlist/createShortlist", dao.Shortlist{
		ListType: "黑名单", VehicleId: int(vehicle.ID), ExpirationTime: nil,
	})
	queryResp, queryBody := env.DoGet("/shortlist/queryAllShortlists", nil)
	AssertResponse(t, queryResp, queryBody, http.StatusOK, response.SUCCESS)
	var result struct { Data []map[string]interface{} `json:"data"` }
	json.Unmarshal(queryBody, &result)
	if len(result.Data) == 0 { t.Fatalf("No shortlist found") }
	idVal, ok := result.Data[0]["ID"]; if !ok { idVal = result.Data[0]["id"] }
	id := int(idVal.(float64))
	deleteResp, deleteBody := env.DoDelete(fmt.Sprintf("/shortlist/deleteShortlist?id=%d", id))
	AssertResponse(t, deleteResp, deleteBody, http.StatusOK, response.SUCCESS)
	fmt.Println("PASS: Shortlist delete successful")
}

// TestOwnerVehicleRelationship 车主关联车辆查询
func TestOwnerVehicleRelationship(t *testing.T) {
	env := InitTestEnv(t)
	ownerResp, ownerBody := env.DoPost("/owner/create", map[string]interface{}{
		"owner_name": "Rel Test Owner", "owner_surname": "REL",
		"owner_phone": "13911111111", "is_vip": false, "vip_expire_time": nil,
	})
	AssertResponse(t, ownerResp, ownerBody, http.StatusOK, response.SUCCESS)
	var owner dao.Owner
	env.DB.Where("owner_phone = ?", "13911111111").First(&owner)
	createVehicleWithOwner(t, env, "JingO00001", "RFID_OWNER", owner.ID)
	ownerListResp, ownerListBody := env.DoGet("/owner/list", map[string]string{"page": "1", "page_size": "10"})
	AssertResponse(t, ownerListResp, ownerListBody, http.StatusOK, response.SUCCESS)
	list := ParsePageList(ownerListBody)
	found := false
	for _, item := range list {
		m, ok := item.(map[string]interface{})
		if !ok { continue }
		if m["owner_phone"] == "13911111111" { found = true; break }
	}
	if !found { t.Fatalf("Owner not found in list") }
	fmt.Println("PASS: Owner-vehicle relationship verified")
}

// TestParkingLotCapacity 停车场容量检查
func TestParkingLotCapacity(t *testing.T) {
	env := InitTestEnv(t)
	lotResp, lotBody := env.DoPost("/parking/lot/create", map[string]interface{}{
		"lot_code": "TEST_EMPTY", "lot_name": "Empty Lot",
		"capacity": 0, "description": "Zero capacity test",
	})
	AssertResponse(t, lotResp, lotBody, http.StatusOK, response.SUCCESS)
	createVehicle(t, env, "JingC00001", "RFID_FULL", 1)
	_, entryBody := env.DoPost("/vehicle/entry", map[string]interface{}{
		"plate_number": "JingC00001", "rfid_tag": "RFID_FULL",
		"parking_lot_id": 1, "parking_space_id": 1, "entry_image": "test.jpg",
	})
	var result struct { Code int `json:"code"`; Msg string `json:"msg"` }
	json.Unmarshal(entryBody, &result)
	fmt.Printf("INFO: Entry into zero-capacity lot: code=%d msg=%s\n", result.Code, result.Msg)
}

// TestBoothCRUD 岗亭CRUD
func TestBoothCRUD(t *testing.T) {
	env := InitTestEnv(t)
	boothResp, boothBody := env.DoPost("/parking/booth/create", map[string]interface{}{
		"booth_code": "BT001", "booth_name": "Test Booth",
		"parking_lot_id": 1, "description": "Automated test booth",
	})
	AssertResponse(t, boothResp, boothBody, http.StatusOK, response.SUCCESS)
	listResp, listBody := env.DoGet("/parking/booth/list", map[string]string{"page": "1", "page_size": "10"})
	AssertResponse(t, listResp, listBody, http.StatusOK, response.SUCCESS)
	list := ParsePageList(listBody)
	if len(list) < 1 { t.Fatalf("Booth list is empty") }
	fmt.Printf("PASS: Booth CRUD successful, total %d booths\n", len(list))
}

// TestChannelCRUD 通道CRUD
func TestChannelCRUD(t *testing.T) {
	env := InitTestEnv(t)
	channelResp, channelBody := env.DoPost("/parking/channel/create", map[string]interface{}{
		"channel_code": "CH-IN-TEST", "channel_name": "Test Entrance",
		"direction": "in", "parking_lot_id": 1, "booth_id": 1,
		"allow_temporary": true, "description": "Automated test channel",
	})
	AssertResponse(t, channelResp, channelBody, http.StatusOK, response.SUCCESS)
	listResp, listBody := env.DoGet("/parking/channel/list", map[string]string{"page": "1", "page_size": "10"})
	AssertResponse(t, listResp, listBody, http.StatusOK, response.SUCCESS)
	list := ParsePageList(listBody)
	if len(list) < 1 { t.Fatalf("Channel list is empty") }
	fmt.Printf("PASS: Channel CRUD successful, total %d channels\n", len(list))
}

// TestDeviceCRUD 设备CRUD
func TestDeviceCRUD(t *testing.T) {
	env := InitTestEnv(t)
	deviceResp, deviceBody := env.DoPost("/parking/device/create", map[string]interface{}{
		"device_code": "DEV001", "device_name": "Test Device",
		"device_type": "TCP", "connect_type": "tcp", "ip_address": "127.0.0.1",
		"port": 9000, "is_active": true, "parking_lot_id": 1, "channel_id": 1,
		"description": "Automated test device",
	})
	AssertResponse(t, deviceResp, deviceBody, http.StatusOK, response.SUCCESS)
	listResp, listBody := env.DoGet("/parking/device/list", map[string]string{"page": "1", "page_size": "10"})
	AssertResponse(t, listResp, listBody, http.StatusOK, response.SUCCESS)
	list := ParsePageList(listBody)
	if len(list) < 1 { t.Fatalf("Device list is empty") }
	fmt.Printf("PASS: Device CRUD successful, total %d devices\n", len(list))
}

// TestVehicleTypeCRUD 自定义车辆类型CRUD
func TestVehicleTypeCRUD(t *testing.T) {
	env := InitTestEnv(t)
	typeResp, typeBody := env.DoPost("/vehicle/type/create", map[string]interface{}{
		"name": "新能源车", "remarks": "测试新能源车辆类型", "is_system": false,
	})
	AssertResponse(t, typeResp, typeBody, http.StatusOK, response.SUCCESS)
	listResp, listBody := env.DoGet("/vehicle/type/all", nil)
	AssertResponse(t, listResp, listBody, http.StatusOK, response.SUCCESS)
	list := ParseDataList(listBody)
	if len(list) < 1 { t.Fatalf("Vehicle type list is empty") }
	fmt.Printf("PASS: Vehicle type CRUD successful, total %d types\n", len(list))
}

// ===================== Helper functions =====================


// TestVehicleUpdate 车辆信息更新
func TestVehicleUpdate(t *testing.T) {
	env := InitTestEnv(t)
	createVehicle(t, env, "JingUPD001", "RFID_UPDATE_TEST", 1)
	var v dao.Vehicle
	env.DB.Where("plate_number = ?", "JingUPD001").First(&v)
	resp, body := env.DoPut("/vehicle/update", map[string]interface{}{
		"ID": v.ID, "plate_number": "JingUPD001", "vehicle_type_id": 1,
		"vehicle_brand": "UpdatedBrand",
		"vehicle_color": "Blue", "rfid_tag": "RFID_UPDATE_TEST",
	})
	AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
	fmt.Println("PASS: Vehicle update successful")
}

// TestVehicleDelete 车辆删除
func TestVehicleDelete(t *testing.T) {
	env := InitTestEnv(t)
	createVehicle(t, env, "JingDEL001", "RFID_DELETE_TEST", 1)
	var v dao.Vehicle
	env.DB.Where("plate_number = ?", "JingDEL001").First(&v)
	resp, body := env.DoDelete(fmt.Sprintf("/vehicle/delete?id=%d", v.ID))
	AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
	fmt.Println("PASS: Vehicle delete successful")
}

// TestVehicleList 车辆分页列表
func TestVehicleList(t *testing.T) {
	env := InitTestEnv(t)
	createVehicle(t, env, "JingLST001", "RFID_LIST_TEST", 1)
	resp, body := env.DoGet("/vehicle/list", map[string]string{"page": "1", "page_size": "10"})
	AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
	list := ParsePageList(body)
	if len(list) < 1 { t.Fatalf("Vehicle list is empty") }
	fmt.Printf("PASS: Vehicle list returned %d records\n", len(list))
}

// TestOwnerUpdate 业主信息更新
func TestOwnerUpdate(t *testing.T) {
	env := InitTestEnv(t)
	ownerResp, ownerBody := env.DoPost("/owner/create", map[string]interface{}{
		"owner_name": "Update Test", "owner_surname": "Upd",
		"owner_phone": "13900000999", "is_vip": false,
	})
	AssertResponse(t, ownerResp, ownerBody, http.StatusOK, response.SUCCESS)
	var o dao.Owner
	env.DB.Where("owner_phone = ?", "13900000999").First(&o)
	updResp, updBody := env.DoPut("/owner/update", map[string]interface{}{
		"ID": o.ID, "owner_name": "Updated Name", "owner_surname": "Upd2",
		"owner_phone": "13900000999", "is_vip": true,
	})
	AssertResponse(t, updResp, updBody, http.StatusOK, response.SUCCESS)
	fmt.Println("PASS: Owner update successful")
}

// TestOwnerGetByPhone 按手机号查询业主
func TestOwnerGetByPhone(t *testing.T) {
	env := InitTestEnv(t)
	_, _ = env.DoPost("/owner/create", map[string]interface{}{
		"owner_name": "Phone Query", "owner_surname": "Ph",
		"owner_phone": "13900000888", "is_vip": false,
	})
	resp, body := env.DoGet("/owner/get-by-phone", map[string]string{"phone": "13900000888"})
	AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
	fmt.Println("PASS: Owner query by phone successful")
}

// TestFeeConfigCreate 收费配置创建
func TestFeeConfigCreate(t *testing.T) {
	env := InitTestEnv(t)
	// 创建一个新的车辆类型并为其创建收费配置
	typeResp, typeBody := env.DoPost("/vehicle/type/create", map[string]interface{}{
		"name": "FeeConfigTest", "remarks": "Fee config test type", "is_system": false,
	})
	AssertResponse(t, typeResp, typeBody, http.StatusOK, response.SUCCESS)
	var vt2 dao.VehicleType
	env.DB.Where("name = ?", "FeeConfigTest").First(&vt2)
	resp, body := env.DoPost("/vehicle/fee-config/create", map[string]interface{}{
		"vehicle_type_id": vt2.ID, "start_time": 0, "start_fee": 0,
		"unit_time": 60, "unit_fee": 10, "daily_max_fee": 80,
	})
	AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
	fmt.Println("PASS: Fee config create successful")
}

// TestParkingLotUpdate 停车场信息更新
func TestParkingLotUpdate(t *testing.T) {
	env := InitTestEnv(t)
	var lot dao.ParkingLot
	env.DB.Where("lot_code = ?", "TEST001").First(&lot)
	resp, body := env.DoPut("/parking/lot/update", map[string]interface{}{
		"ID": lot.ID, "lot_code": "TEST001", "lot_name": "Updated Lot",
		"capacity": 200, "description": "Updated desc",
	})
	AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
	fmt.Println("PASS: Parking lot update successful")
}


// ===================== 月租车管理模块测试 =====================

// TestMonthlyCardCreate 办理月卡
func TestMonthlyCardCreate(t *testing.T) {
	env := InitTestEnv(t)
	createVehicle(t, env, "JingMC001", "RFID_MC001", 1)
	var v dao.Vehicle
	env.DB.Where("plate_number = ?", "JingMC001").First(&v)

	resp, body := env.DoPost("/monthly-card/create", map[string]interface{}{
		"vehicle_id": v.ID, "card_type": "month", "fee": 300.0, "remark": "test month card",
	})
	AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)

	var card dao.MonthlyCard
	env.DB.Where("vehicle_id = ?", v.ID).First(&card)
	if card.ID == 0 { t.Fatalf("Monthly card not created in DB") }
	if card.PaymentStatus != "paid" { t.Fatalf("Expected paid, got %s", card.PaymentStatus) }

	// Verify shortlist (whitelist) was created
	var sl dao.Shortlist
	env.DB.Where("vehicle_id = ? AND list_type = ?", v.ID, "白名单").First(&sl)
	if sl.ID == 0 { t.Fatalf("Shortlist not created") }

	fmt.Println("PASS: Monthly card created successfully")
}

// TestMonthlyCardRenew 月卡续费
func TestMonthlyCardRenew(t *testing.T) {
	env := InitTestEnv(t)
	createVehicle(t, env, "JingMC002", "RFID_MC002", 1)
	var v dao.Vehicle
	env.DB.Where("plate_number = ?", "JingMC002").First(&v)
	_, _ = env.DoPost("/monthly-card/create", map[string]interface{}{
		"vehicle_id": v.ID, "card_type": "month", "fee": 300.0,
	})
	var card dao.MonthlyCard
	env.DB.Where("vehicle_id = ?", v.ID).First(&card)
	origEnd := card.EndDate

	resp, body := env.DoPost("/monthly-card/renew", map[string]interface{}{
		"card_id": card.ID, "card_type": "month", "fee": 300.0,
	})
	AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)

	env.DB.Where("vehicle_id = ?", v.ID).First(&card)
	expectedEnd := origEnd.AddDate(0, 0, 30)
	if !card.EndDate.Equal(expectedEnd) && !card.EndDate.After(expectedEnd.Add(-time.Hour*23)) {
		t.Fatalf("EndDate not extended correctly: orig=%v, new=%v", origEnd, card.EndDate)
	}
	fmt.Println("PASS: Monthly card renewed successfully")
}

// TestMonthlyCardRefund 月卡退卡
func TestMonthlyCardRefund(t *testing.T) {
	env := InitTestEnv(t)
	createVehicle(t, env, "JingMC003", "RFID_MC003", 1)
	var v dao.Vehicle
	env.DB.Where("plate_number = ?", "JingMC003").First(&v)
	_, _ = env.DoPost("/monthly-card/create", map[string]interface{}{
		"vehicle_id": v.ID, "card_type": "month", "fee": 300.0,
	})
	var card dao.MonthlyCard
	env.DB.Where("vehicle_id = ?", v.ID).First(&card)

	resp, body := env.DoDelete(fmt.Sprintf("/monthly-card/refund?id=%d", card.ID))
	AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)

	env.DB.First(&card, card.ID)
	if card.PaymentStatus != "refunded" { t.Fatalf("Expected refunded, got %s", card.PaymentStatus) }

	var count int64
	env.DB.Model(&dao.Shortlist{}).Where("vehicle_id = ? AND list_type = ?", v.ID, "白名单").Count(&count)
	if count != 0 { t.Fatalf("Shortlist should be removed after refund, found %d", count) }

	fmt.Println("PASS: Monthly card refunded successfully")
}

// TestMonthlyCardList 月卡列表查询
func TestMonthlyCardList(t *testing.T) {
	env := InitTestEnv(t)
	createVehicle(t, env, "JingMC004", "RFID_MC004", 1)
	var v dao.Vehicle
	env.DB.Where("plate_number = ?", "JingMC004").First(&v)
	_, _ = env.DoPost("/monthly-card/create", map[string]interface{}{
		"vehicle_id": v.ID, "card_type": "quarter", "fee": 800.0,
	})

	resp, body := env.DoGet("/monthly-card/list", map[string]string{"page": "1", "page_size": "10"})
	AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
	list := ParsePageList(body)
	if len(list) < 1 { t.Fatalf("Monthly card list is empty") }

	resp, body2 := env.DoGet("/monthly-card/list", map[string]string{
		"page": "1", "page_size": "10", "card_type": "quarter",
	})
	AssertResponse(t, resp, body2, http.StatusOK, response.SUCCESS)
	list2 := ParsePageList(body2)
	if len(list2) < 1 { t.Fatalf("Filtered list by card_type is empty") }

	fmt.Printf("PASS: Monthly card list returned %d records\\n", len(list))
}

// TestMonthlyCardDuplicate 重复办卡拦截
func TestMonthlyCardDuplicate(t *testing.T) {
	env := InitTestEnv(t)
	createVehicle(t, env, "JingMC005", "RFID_MC005", 1)
	var v dao.Vehicle
	env.DB.Where("plate_number = ?", "JingMC005").First(&v)

	_, _ = env.DoPost("/monthly-card/create", map[string]interface{}{
		"vehicle_id": v.ID, "card_type": "month", "fee": 300.0,
	})

	_, body2 := env.DoPost("/monthly-card/create", map[string]interface{}{
		"vehicle_id": v.ID, "card_type": "year", "fee": 3000.0,
	})
	var result struct { Code int `json:"code"`; Msg string `json:"msg"` }
	json.Unmarshal(body2, &result)
	if result.Code == response.SUCCESS {
		t.Fatalf("BUG: Duplicate monthly card creation was NOT blocked!")
	}
	fmt.Printf("PASS: Duplicate card creation correctly rejected: %s\\n", result.Msg)
}

// TestMonthlyCardNonexistentRefund 不存在的月卡退卡
func TestMonthlyCardNonexistentRefund(t *testing.T) {
	env := InitTestEnv(t)
	_, body := env.DoDelete("/monthly-card/refund?id=99999")
	// Should fail with error code
	var result struct { Code int `json:"code"`; Msg string `json:"msg"` }
	json.Unmarshal(body, &result)
	if result.Code == response.SUCCESS {
		t.Fatalf("BUG: Refunding non-existent card was NOT blocked!")
	}
	// HTTP status may vary, just check code != 0
	fmt.Printf("PASS: Non-existent card refund correctly rejected: code=%d msg=%s\\n", result.Code, result.Msg)
}

// TestMonthlyCardDifferentTypes 不同套餐类型
func TestMonthlyCardDifferentTypes(t *testing.T) {
	env := InitTestEnv(t)
	createVehicle(t, env, "JingMC006", "RFID_MC006", 1)
	var v dao.Vehicle
	env.DB.Where("plate_number = ?", "JingMC006").First(&v)

	resp, body := env.DoPost("/monthly-card/create", map[string]interface{}{
		"vehicle_id": v.ID, "card_type": "year", "fee": 3000.0,
	})
	AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
	var card dao.MonthlyCard
	env.DB.Where("vehicle_id = ?", v.ID).First(&card)

	duration := card.EndDate.Sub(card.StartDate).Hours() / 24
	if duration < 360 || duration > 370 {
		t.Fatalf("Year card should be ~365 days, got %.0f days", duration)
	}
	fmt.Printf("PASS: Year card duration = %.0f days\\n", duration)
}


// ===================== 数字票模块测试 =====================

func createTicket(t *testing.T, env *TestEnv, plate, trigger string) (string, uint) {
	t.Helper()
	createVehicle(t, env, plate, "RFID_"+plate, 1)
	var v dao.Vehicle
	env.DB.Where("plate_number = ?", plate).First(&v)
	rec := dao.VehicleRecord{PlateNumber: plate, RFIDTag: "RFID_" + plate, EntryTime: time.Now(), ParkingLotID: 1}
	env.DB.Create(&rec)
	var svc = new(dt_svc.TicketService)
	ticket, err := svc.Create(plate, trigger, rec.ID)
	if err != nil {
		t.Fatalf("Failed to create ticket: %v", err)
	}
	return ticket.TicketNo, ticket.ID
}

// TestDigitalTicketDetail DT01: 查询数字票详情
func TestDigitalTicketDetail(t *testing.T) {
	env := InitTestEnv(t)
	ticketNo, _ := createTicket(t, env, "DT001", "manual")
	resp, body := env.DoGet("/digital-ticket/"+ticketNo, nil)
	AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
	data := ParseDataMap(body)
	if data["state"] != "pending_payment" {
		t.Fatalf("Expected state=pending_payment, got %v", data["state"])
	}
	if data["ticket_no"] != ticketNo {
		t.Fatalf("TicketNo mismatch")
	}
	if data["trigger_mode"] != "manual" {
		t.Fatalf("TriggerMode mismatch")
	}
	fmt.Println("PASS: Digital ticket detail query successful")
}

// TestDigitalTicketPay DT02: 支付数字票
// BUG: paid_amount 字段在 digital_ticket 表中不存在，PayTicket API 会报 SQL 错误
// 预期: 该测试应失败，暴露后端缺陷
func TestDigitalTicketPay(t *testing.T) {
	env := InitTestEnv(t)
	ticketNo, _ := createTicket(t, env, "DT002", "plate_recognition")
	resp, body := env.DoPost("/digital-ticket/"+ticketNo+"/pay", map[string]interface{}{
		"payment_method": "cash", "payment_order_no": "PO002", "paid_amount": 20.0,
	})
	AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
	fmt.Println("PASS: Pay API works correctly")
}

// TestDigitalTicketExit DT03: 离场验证
func TestDigitalTicketExit(t *testing.T) {
	env := InitTestEnv(t)
	ticketNo, _ := createTicket(t, env, "DT003", "rfid")
	// 支付
	_, _ = env.DoPost("/digital-ticket/"+ticketNo+"/pay", map[string]interface{}{
		"payment_method": "cash", "payment_order_no": "PO003", "paid_amount": 15.0,
	})
	// 支付成功后离场
	resp, body := env.DoPost("/digital-ticket/"+ticketNo+"/exit", nil)
	AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
	fmt.Println("PASS: Pay → Exit flow works correctly")
}

// TestDigitalTicketList DT04: 数字票列表
func TestDigitalTicketList(t *testing.T) {
	env := InitTestEnv(t)
	createTicket(t, env, "DT004", "manual")
	resp, body := env.DoGet("/digital-ticket/list", map[string]string{"page": "1", "page_size": "10"})
	AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
	list := ParsePageList(body)
	if len(list) < 1 {
		t.Fatalf("Ticket list is empty")
	}
	fmt.Printf("PASS: Digital ticket list returned %d records\n", len(list))
}

// TestDigitalTicketFullFlow DT05: 完整流程
// BUG: Pay API 因 paid_amount 缺失而失败，完整流程无法通过 pay API 完成
// 测试验证: exit API 本身在状态正确时可正常工作
func TestDigitalTicketFullFlow(t *testing.T) {
	env := InitTestEnv(t)
	ticketNo, _ := createTicket(t, env, "DT005", "manual")
	// Pay
	resp1, body1 := env.DoPost("/digital-ticket/"+ticketNo+"/pay", map[string]interface{}{
		"payment_method": "cash", "payment_order_no": "PO005", "paid_amount": 25.0,
	})
	AssertResponse(t, resp1, body1, http.StatusOK, response.SUCCESS)
	// Exit
	resp2, body2 := env.DoPost("/digital-ticket/"+ticketNo+"/exit", nil)
	AssertResponse(t, resp2, body2, http.StatusOK, response.SUCCESS)
	// Verify state
	_, getBody := env.DoGet("/digital-ticket/"+ticketNo, nil)
	data := ParseDataMap(getBody)
	if data["state"] != "exited" {
		t.Fatalf("Expected exited, got state=%v", data["state"])
	}
	fmt.Println("PASS: Full flow (pay → exit) works correctly")
}

// TestDigitalTicketInvalidExit DT06: 未支付直接离场被拒
func TestDigitalTicketInvalidExit(t *testing.T) {
	env := InitTestEnv(t)
	ticketNo, _ := createTicket(t, env, "DT006", "manual")
	_, body := env.DoPost("/digital-ticket/"+ticketNo+"/exit", nil)
	var result struct{ Code int `json:"code"`; Msg string `json:"msg"` }
	json.Unmarshal(body, &result)
	if result.Code == response.SUCCESS {
		t.Fatalf("BUG: Exit without payment was NOT blocked!")
	}
	fmt.Printf("PASS: Exit without payment correctly rejected: %s\n", result.Msg)
}

// TestDigitalTicketNonexistent DT08: 不存在的票号
func TestDigitalTicketNonexistent(t *testing.T) {
	env := InitTestEnv(t)
	_, body := env.DoGet("/digital-ticket/FAKE", nil)
	var result struct{ Code int `json:"code"`; Msg string `json:"msg"` }
	json.Unmarshal(body, &result)
	if result.Code == response.SUCCESS {
		t.Fatalf("BUG: Non-existent ticket returned success!")
	}
	fmt.Printf("PASS: Non-existent ticket correctly rejected: %s\n", result.Msg)
}

// TestDigitalTicketListFilter DT09+10: 列表过滤
func TestDigitalTicketListFilter(t *testing.T) {
	env := InitTestEnv(t)
	createTicket(t, env, "DTFILTER", "manual")
	resp, body := env.DoGet("/digital-ticket/list", map[string]string{
		"page": "1", "page_size": "10", "plate_number": "DTFILTER",
	})
	AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
	list := ParsePageList(body)
	if len(list) < 1 {
		t.Fatalf("Filter by plate returned empty")
	}
	fmt.Printf("PASS: Ticket list filter returned %d records\n", len(list))
}

// TestDigitalTicketEmptyList DT13: 空列表
func TestDigitalTicketListPagination(t *testing.T) {
	env := InitTestEnv(t)
	createTicket(t, env, "DTPAGE", "manual")
	resp, body := env.DoGet("/digital-ticket/list", map[string]string{"page": "1", "page_size": "5"})
	AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)
	list := ParsePageList(body)
	if len(list) < 1 {
		t.Fatalf("Paginated list is empty")
	}
	fmt.Printf("PASS: Ticket list pagination returned %d records\n", len(list))
}

func createVehicleWithOwner(t *testing.T, env *TestEnv, plateNumber, rfidTag string, ownerID uint) {
	t.Helper()
	_, body := env.DoPost("/vehicle/create", map[string]interface{}{
		"plate_number": plateNumber, "vehicle_type_id": 1,
		"vehicle_brand": "TestBrand", "vehicle_color": "TestColor",
		"rfid_tag": rfidTag, "owner_id": ownerID,
	})
	var result struct { Code int `json:"code"`; Msg string `json:"msg"` }
	json.Unmarshal(body, &result)
	if result.Code != response.SUCCESS {
		t.Fatalf("Failed to create vehicle with owner: %s", result.Msg)
	}
}

// ===================== 交接班对账 (Shift) 测试 =====================
//
// 注意：由于 TestEnv 是全局缓存的，所有测试共享同一个数据库和操作员，
// 每个 shift 测试必须清除前序测试遗留的当班记录。

// closeActiveShift 清除操作员活动中当班（DB直接操作，不经过 API）
func closeActiveShift(env *TestEnv) {
	env.DB.Model(&dao.ShiftRecord{}).
		Where("operator_id = ? AND status = ?", env.UserID, "active").
		Update("status", "closed")
}

// TestShiftStart SH01: 正常接班
func TestShiftStart(t *testing.T) {
	env := InitTestEnv(t)
	closeActiveShift(env) // 清理前序状态

	resp, body := env.DoPost("/shift/start", map[string]interface{}{
		"starting_cash": 100.0,
	})
	AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)

	// 验证 DB 中创建了当班记录
	var rec dao.ShiftRecord
	env.DB.Where("operator_id = ? AND status = ?", env.UserID, "active").First(&rec)
	if rec.ID == 0 {
		t.Fatalf("Shift record not created in DB")
	}
	if rec.StartingCash != 100.0 {
		t.Fatalf("Expected starting_cash=100, got %.2f", rec.StartingCash)
	}
	fmt.Printf("PASS: Shift start successful, recordID=%d\n", rec.ID)
}

// TestShiftEnd SH02: 正常交班（含当班现金收款统计与差异计算）
func TestShiftEnd(t *testing.T) {
	env := InitTestEnv(t)
	closeActiveShift(env)

	// 1. 接班
	startResp, startBody := env.DoPost("/shift/start", map[string]interface{}{
		"starting_cash": 100.0,
	})
	AssertResponse(t, startResp, startBody, http.StatusOK, response.SUCCESS)

	// 2. 伪造一笔现金收款记录（模拟当班期间收款50元）
	now := time.Now()
	env.DB.Exec(`INSERT INTO payment_record
		(record_id, payment_method, amount, paid_amount, change_amount, operator_id, paid_at, created_at, updated_at)
		VALUES (9999, 'cash', 50.0, 50.0, 0, ?, ?, ?, ?)`,
		env.UserID, now, now, now)

	// 3. 交班 — 接班100 + 当班收款50 = 应交150，实交150 → 差异0
	resp, body := env.DoPost("/shift/end", map[string]interface{}{
		"actual_cash": 150.0,
		"remark":      "test shift end",
	})
	AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)

	// 4. 验证 DB 记录
	var rec dao.ShiftRecord
	env.DB.Where("operator_id = ? AND status = ?", env.UserID, "closed").Order("id DESC").First(&rec)
	if rec.ID == 0 {
		t.Fatalf("Shift record not updated in DB")
	}
	if rec.CollectedCash != 50.0 {
		t.Fatalf("Expected collected_cash=50, got %.2f", rec.CollectedCash)
	}
	if rec.ExpectedTotal != 150.0 {
		t.Fatalf("Expected expected_total=150, got %.2f", rec.ExpectedTotal)
	}
	if rec.Difference != 0.0 {
		t.Fatalf("Expected difference=0, got %.2f", rec.Difference)
	}
	if rec.Remark != "test shift end" {
		t.Fatalf("Remark mismatch: expected 'test shift end', got '%s'", rec.Remark)
	}
	fmt.Printf("PASS: Shift end successful, collected=%.2f expected=%.2f diff=%.2f\n",
		rec.CollectedCash, rec.ExpectedTotal, rec.Difference)
}

// TestShiftDoubleStart SH03: 重复接班被拦截
func TestShiftDoubleStart(t *testing.T) {
	env := InitTestEnv(t)
	closeActiveShift(env)

	// 第一次接班 — 成功
	startResp, startBody := env.DoPost("/shift/start", map[string]interface{}{
		"starting_cash": 100.0,
	})
	AssertResponse(t, startResp, startBody, http.StatusOK, response.SUCCESS)

	// 第二次接班 — 应被拦截
	_, body := env.DoPost("/shift/start", map[string]interface{}{
		"starting_cash": 200.0,
	})
	var result struct {
		Code int    `json:"code"`
		Msg  string `json:"msg"`
	}
	json.Unmarshal(body, &result)
	if result.Code == response.SUCCESS {
		t.Fatalf("BUG: Double shift start was NOT blocked! code=%d msg=%s", result.Code, result.Msg)
	}
	fmt.Printf("PASS: Double start correctly rejected: msg=%s\n", result.Msg)
}

// TestShiftEndWithoutStart SH04: 未接班直接交班失败
func TestShiftEndWithoutStart(t *testing.T) {
	env := InitTestEnv(t)
	closeActiveShift(env) // 确保没有当班记录

	resp, body := env.DoPost("/shift/end", map[string]interface{}{
		"actual_cash": 100.0,
		"remark":      "no start",
	})
	// 期望返回错误（无当班记录）
	AssertResponse(t, resp, body, http.StatusOK, response.ERROR)

	var result struct {
		Code int    `json:"code"`
		Msg  string `json:"msg"`
	}
	json.Unmarshal(body, &result)
	fmt.Printf("PASS: End without start correctly rejected: code=%d msg=%s\n", result.Code, result.Msg)
}

// TestShiftCurrent SH05: 查询当前当班信息
func TestShiftCurrent(t *testing.T) {
	env := InitTestEnv(t)
	closeActiveShift(env)

	// 先接班
	_, _ = env.DoPost("/shift/start", map[string]interface{}{
		"starting_cash": 200.0,
	})

	// 查询当班
	resp, body := env.DoGet("/shift/current", nil)
	AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)

	var result struct {
		Code int                        `json:"code"`
		Data map[string]interface{}     `json:"data"`
		Msg  string                     `json:"msg"`
	}
	json.Unmarshal(body, &result)

	if result.Data == nil {
		t.Fatalf("Shift current data is nil")
	}
	startCash, ok := result.Data["starting_cash"].(float64)
	if !ok || startCash != 200.0 {
		t.Fatalf("Expected starting_cash=200, got %v", result.Data["starting_cash"])
	}
	fmt.Printf("PASS: Shift current query successful, starting_cash=%.2f\n", startCash)
}

// TestShiftList SH06: 交接班记录列表
func TestShiftList(t *testing.T) {
	env := InitTestEnv(t)
	closeActiveShift(env)

	// 接班 → 交班 → 产生一条历史记录
	_, _ = env.DoPost("/shift/start", map[string]interface{}{"starting_cash": 50.0})
	_, _ = env.DoPost("/shift/end", map[string]interface{}{"actual_cash": 50.0, "remark": "list test"})

	// 查询列表
	resp, body := env.DoGet("/shift/list", map[string]string{
		"page":      "1",
		"page_size": "10",
	})
	AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)

	list := ParsePageList(body)
	if len(list) == 0 {
		t.Fatalf("Shift list is empty after creating a record")
	}
	fmt.Printf("PASS: Shift list returned %d records\n", len(list))
}

// ===================== 收入报表 (Revenue Report) 测试 =====================

// createParkingPayment 辅助函数：创建一笔车辆进出+支付记录（用于报表测试）
func createParkingPayment(t *testing.T, env *TestEnv, plateNumber, rfidTag string, amount float64, method string) {
	t.Helper()

	createVehicle(t, env, plateNumber, rfidTag, 1)

	entryResp, entryBody := env.DoPost("/vehicle/entry", map[string]interface{}{
		"plate_number":     plateNumber,
		"rfid_tag":         rfidTag,
		"parking_lot_id":   1,
		"parking_space_id": 1,
		"entry_image":      "test_revenue_entry.jpg",
	})
	AssertResponse(t, entryResp, entryBody, http.StatusOK, response.SUCCESS)

	// 修改入场时间为数小时前，以确保产生费用
	entryTime := time.Now().Add(-3 * time.Hour)
	env.DB.Model(&dao.VehicleRecord{}).Where("plate_number = ?", plateNumber).Update("entry_time", entryTime)

	exitResp, exitBody := env.DoPost("/vehicle/exit/confirm", map[string]interface{}{
		"plate_number":   plateNumber,
		"rfid_tag":       rfidTag,
		"payment_method": method,
		"paid_amount":    amount,
	})
	AssertResponse(t, exitResp, exitBody, http.StatusOK, response.SUCCESS)
}

// TestRevenueReportBasic RR01: 基础收入报表查询（无参数）
// BUG: 后端 RevenueReport 当 start_date/end_date 为空时，SQL 条件为
//   paid_at >= '' AND paid_at <= ' 23:59:59'
// 导致所有记录被过滤，data 返回 null。
func TestRevenueReportBasic(t *testing.T) {
	env := InitTestEnv(t)

	createParkingPayment(t, env, "JingREV001", "RFID_REV001", 15.0, "cash")

	resp, body := env.DoGet("/report/revenue", nil)
	// HTTP 层面是 200
	if resp.StatusCode != http.StatusOK {
		t.Fatalf("Expected 200, got %d", resp.StatusCode)
	}

	var result struct {
		Code int              `json:"code"`
		Data json.RawMessage  `json:"data"`
		Msg  string           `json:"msg"`
	}
	json.Unmarshal(body, &result)

	// BUG: 无日期参数时 data 应为有数据，但后端返回 null
	if result.Code != response.SUCCESS {
		t.Fatalf("Revenue report failed: code=%d msg=%s", result.Code, result.Msg)
	}
	if result.Data == nil || string(result.Data) == "null" {
		t.Fatalf("BUG: Revenue report with no date params returns null data! "+
			"Cause: SQL condition 'paid_at >= \"\" AND paid_at <= \" 23:59:59\"' filters out all records. "+
			"Fix: when start/end_date are empty, omit the WHERE clause or default to today.")
	}
	fmt.Printf("PASS: Revenue report basic query successful, data=%s\n", string(result.Data))
}

// TestRevenueReportDateRange RR02: 指定日期范围的收入报表
func TestRevenueReportDateRange(t *testing.T) {
	env := InitTestEnv(t)

	createParkingPayment(t, env, "JingREV002", "RFID_REV002", 20.0, "cash")

	today := time.Now().Format("2006-01-02")
	resp, body := env.DoGet("/report/revenue", map[string]string{
		"start_date": today,
		"end_date":   today,
	})
	AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)

	var result struct {
		Code int              `json:"code"`
		Data json.RawMessage  `json:"data"`
		Msg  string           `json:"msg"`
	}
	json.Unmarshal(body, &result)
	if len(result.Data) == 0 || string(result.Data) == "null" {
		t.Fatalf("Revenue report with date range returned empty")
	}
	fmt.Printf("PASS: Revenue report with date range successful, data=%s\n", string(result.Data))
}

// TestRevenueReportWithLotFilter RR03: 指定停车场的收入报表
func TestRevenueReportWithLotFilter(t *testing.T) {
	env := InitTestEnv(t)

	createParkingPayment(t, env, "JingREV003", "RFID_REV003", 25.0, "cash")

	today := time.Now().Format("2006-01-02")
	resp, body := env.DoGet("/report/revenue", map[string]string{
		"parking_lot_id": "1",
		"start_date":     today,
		"end_date":       today,
	})
	AssertResponse(t, resp, body, http.StatusOK, response.SUCCESS)

	var result struct {
		Code int              `json:"code"`
		Data json.RawMessage  `json:"data"`
		Msg  string           `json:"msg"`
	}
	json.Unmarshal(body, &result)
	if len(result.Data) == 0 || string(result.Data) == "null" {
		t.Fatalf("Revenue report with lot filter returned empty")
	}
	fmt.Printf("PASS: Revenue report with lot filter successful, data=%s\n", string(result.Data))
}

