package channelserver

import (
	"erupe-ce/common/byteframe"
	"erupe-ce/common/mhfitem"
	"erupe-ce/common/token"
	cfg "erupe-ce/config"
	"erupe-ce/network/mhfpacket"
	"testing"

	"github.com/jmoiron/sqlx"
)

// ackResponse holds parsed fields from a queued MsgSysAck packet.
type ackResponse struct {
	AckHandle        uint32
	IsBufferResponse bool
	ErrorCode        uint8
	PayloadSize      uint
	Payload          []byte
}

// readAck drains one packet from the session's sendPackets channel and
// parses the MsgSysAck wire format that QueueSendMHF produces.
func readAck(t *testing.T, session *Session) ackResponse {
	t.Helper()
	select {
	case p := <-session.sendPackets:
		bf := byteframe.NewByteFrameFromBytes(p.data)
		_ = bf.ReadUint16() // opcode
		ack := ackResponse{}
		ack.AckHandle = bf.ReadUint32()
		ack.IsBufferResponse = bf.ReadBool()
		ack.ErrorCode = bf.ReadUint8()
		size := uint(bf.ReadUint16())
		if size == 0xFFFF {
			size = uint(bf.ReadUint32())
		}
		ack.PayloadSize = size
		if ack.IsBufferResponse {
			ack.Payload = bf.ReadBytes(size)
		} else {
			ack.Payload = bf.ReadBytes(4)
		}
		return ack
	default:
		t.Fatal("No response packet queued")
		return ackResponse{}
	}
}

// setupHouseTest creates DB, server, session, and a character with user_binary row.
func setupHouseTest(t *testing.T) (*sqlx.DB, *Server, *Session, uint32) {
	t.Helper()
	db := SetupTestDB(t)
	server := createMockServer()
	server.erupeConfig.RealClientMode = cfg.ZZ
	SetTestDB(server, db)

	userID := CreateTestUser(t, db, "house_test_user")
	charID := CreateTestCharacter(t, db, userID, "HousePlayer")

	_, err := db.Exec(`INSERT INTO user_binary (id) VALUES ($1) ON CONFLICT DO NOTHING`, charID)
	if err != nil {
		t.Fatalf("Failed to create user_binary row: %v", err)
	}

	session := createMockSession(charID, server)
	return db, server, session, charID
}

// createTestEquipment creates properly initialized test equipment
func createTestEquipment(itemIDs []uint16, warehouseIDs []uint32) []mhfitem.MHFEquipment {
	var equip []mhfitem.MHFEquipment
	for i, itemID := range itemIDs {
		e := mhfitem.MHFEquipment{
			ItemID:      itemID,
			WarehouseID: warehouseIDs[i],
			Decorations: make([]mhfitem.MHFItem, 3),
			Sigils:      make([]mhfitem.MHFSigil, 3),
		}
		// Initialize Sigils Effects arrays
		for j := 0; j < 3; j++ {
			e.Sigils[j].Effects = make([]mhfitem.MHFSigilEffect, 3)
		}
		equip = append(equip, e)
	}
	return equip
}

// =============================================================================
// Unit Tests — guard paths, no database
// =============================================================================

func TestUpdateInterior_PayloadTooLarge(t *testing.T) {
	server := createMockServer()
	session := createMockSession(1, server)

	pkt := &mhfpacket.MsgMhfUpdateInterior{
		AckHandle:    1,
		InteriorData: make([]byte, 65), // > 64 triggers guard
	}
	handleMsgMhfUpdateInterior(session, pkt)

	ack := readAck(t, session)
	if ack.ErrorCode != 0 {
		t.Errorf("expected success ACK (guard returns succeed), got error code %d", ack.ErrorCode)
	}
}

func TestUpdateMyhouseInfo_PayloadTooLarge(t *testing.T) {
	server := createMockServer()
	session := createMockSession(1, server)

	pkt := &mhfpacket.MsgMhfUpdateMyhouseInfo{
		AckHandle: 2,
		Data:      make([]byte, 513), // > 512 triggers guard
	}
	handleMsgMhfUpdateMyhouseInfo(session, pkt)

	ack := readAck(t, session)
	if ack.ErrorCode != 0 {
		t.Errorf("expected success ACK on oversized payload, got error code %d", ack.ErrorCode)
	}
}

func TestSaveDecoMyset_PayloadTooShort(t *testing.T) {
	server := createMockServer()
	session := createMockSession(1, server)

	pkt := &mhfpacket.MsgMhfSaveDecoMyset{
		AckHandle:      3,
		RawDataPayload: []byte{0x00, 0x01}, // < 3 bytes
	}
	handleMsgMhfSaveDecoMyset(session, pkt)

	ack := readAck(t, session)
	if ack.ErrorCode != 0 {
		t.Errorf("expected success ACK on short payload, got error code %d", ack.ErrorCode)
	}
}

func TestUpdateWarehouse_BoxIndexTooHigh(t *testing.T) {
	server := createMockServer()
	session := createMockSession(1, server)

	pkt := &mhfpacket.MsgMhfUpdateWarehouse{
		AckHandle: 4,
		BoxIndex:  11, // > 10 triggers fail
	}
	handleMsgMhfUpdateWarehouse(session, pkt)

	ack := readAck(t, session)
	if ack.ErrorCode != 1 {
		t.Errorf("expected fail ACK for out-of-bounds box index, got error code %d", ack.ErrorCode)
	}
}

func TestEnumerateHouse_Method5_EmptyResult(t *testing.T) {
	server := createMockServer()
	server.erupeConfig.RealClientMode = cfg.ZZ
	session := createMockSession(1, server)

	pkt := &mhfpacket.MsgMhfEnumerateHouse{
		AckHandle: 5,
		Method:    5, // Recent visitors — always returns empty
	}
	handleMsgMhfEnumerateHouse(session, pkt)

	ack := readAck(t, session)
	if ack.ErrorCode != 0 {
		t.Fatalf("expected success, got error code %d", ack.ErrorCode)
	}
	if !ack.IsBufferResponse {
		t.Fatal("expected buffer response")
	}
	// First 2 bytes = count, should be 0
	bf := byteframe.NewByteFrameFromBytes(ack.Payload)
	count := bf.ReadUint16()
	if count != 0 {
		t.Errorf("expected 0 houses for method 5, got %d", count)
	}
}

func TestResetTitle_NoOp(t *testing.T) {
	// handleMsgMhfResetTitle is an empty function — just verify no panic
	defer func() {
		if r := recover(); r != nil {
			t.Errorf("handleMsgMhfResetTitle panicked: %v", r)
		}
	}()
	handleMsgMhfResetTitle(nil, nil)
}

func TestOperateWarehouse_RenameBoxIndexTooHigh(t *testing.T) {
	// Operation 2 = Rename. BoxIndex > 9 should skip the rename.
	// This needs a DB for initializeWarehouse, so the full test is the
	// integration test TestOperateWarehouse_Op2_RenameBoxIndexTooHigh below.
}

// =============================================================================
// Integration Tests — real PostgreSQL via SetupTestDB
// =============================================================================

func TestUpdateInterior_SavesData(t *testing.T) {
	_, _, session, charID := setupHouseTest(t)

	interiorData := []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A}
	pkt := &mhfpacket.MsgMhfUpdateInterior{
		AckHandle:    10,
		InteriorData: interiorData,
	}
	handleMsgMhfUpdateInterior(session, pkt)

	ack := readAck(t, session)
	if ack.ErrorCode != 0 {
		t.Fatalf("expected success, got error code %d", ack.ErrorCode)
	}

	// Verify data was persisted
	_, _, furniture, _, _, _, _, err := session.server.houseRepo.GetHouseContents(charID)
	if err != nil {
		t.Fatalf("GetHouseContents failed: %v", err)
	}
	if len(furniture) < len(interiorData) {
		t.Fatalf("furniture data too short: got %d bytes", len(furniture))
	}
	for i, b := range interiorData {
		if furniture[i] != b {
			t.Errorf("furniture[%d] = %#x, want %#x", i, furniture[i], b)
		}
	}
}

func TestUpdateHouse_SetsStateAndPassword(t *testing.T) {
	_, _, session, charID := setupHouseTest(t)

	pkt := &mhfpacket.MsgMhfUpdateHouse{
		AckHandle: 11,
		State:     3,
		Password:  "secret",
	}
	handleMsgMhfUpdateHouse(session, pkt)

	ack := readAck(t, session)
	if ack.ErrorCode != 0 {
		t.Fatalf("expected success, got error code %d", ack.ErrorCode)
	}

	state, password, err := session.server.houseRepo.GetHouseAccess(charID)
	if err != nil {
		t.Fatalf("GetHouseAccess failed: %v", err)
	}
	if state != 3 {
		t.Errorf("state = %d, want 3", state)
	}
	if password != "secret" {
		t.Errorf("password = %q, want %q", password, "secret")
	}
}

func TestEnumerateHouse_Method4_ByCharID(t *testing.T) {
	_, _, session, charID := setupHouseTest(t)

	pkt := &mhfpacket.MsgMhfEnumerateHouse{
		AckHandle: 12,
		Method:    4,
		CharID:    charID,
	}
	handleMsgMhfEnumerateHouse(session, pkt)

	ack := readAck(t, session)
	if ack.ErrorCode != 0 {
		t.Fatalf("expected success, got error code %d", ack.ErrorCode)
	}
	bf := byteframe.NewByteFrameFromBytes(ack.Payload)
	count := bf.ReadUint16()
	if count != 1 {
		t.Errorf("expected 1 house for charID lookup, got %d", count)
	}
}

func TestEnumerateHouse_Method3_ByName(t *testing.T) {
	_, _, session, _ := setupHouseTest(t)

	pkt := &mhfpacket.MsgMhfEnumerateHouse{
		AckHandle: 13,
		Method:    3,
		Name:      "HousePlayer",
	}
	handleMsgMhfEnumerateHouse(session, pkt)

	ack := readAck(t, session)
	if ack.ErrorCode != 0 {
		t.Fatalf("expected success, got error code %d", ack.ErrorCode)
	}
	bf := byteframe.NewByteFrameFromBytes(ack.Payload)
	count := bf.ReadUint16()
	if count < 1 {
		t.Errorf("expected at least 1 house for name search, got %d", count)
	}
}

func TestLoadHouse_OwnHouse_Destination9(t *testing.T) {
	_, _, session, charID := setupHouseTest(t)

	// Set some interior data first
	interior := make([]byte, 20)
	interior[0] = 0xAB
	_ = session.server.houseRepo.UpdateInterior(charID, interior)

	pkt := &mhfpacket.MsgMhfLoadHouse{
		AckHandle:   14,
		CharID:      charID,
		Destination: 9, // Own house — bypasses access control
	}
	handleMsgMhfLoadHouse(session, pkt)

	ack := readAck(t, session)
	if ack.ErrorCode != 0 {
		t.Fatalf("expected success loading own house, got error code %d", ack.ErrorCode)
	}
	if !ack.IsBufferResponse {
		t.Fatal("expected buffer response")
	}
	if len(ack.Payload) == 0 {
		t.Error("expected non-empty house data")
	}
}

func TestLoadHouse_WrongPassword_Fails(t *testing.T) {
	_, _, session, charID := setupHouseTest(t)

	// Set a password on the house
	_ = session.server.houseRepo.UpdateHouseState(charID, 2, "correct")

	pkt := &mhfpacket.MsgMhfLoadHouse{
		AckHandle:   15,
		CharID:      charID,
		Destination: 3, // Others house
		CheckPass:   true,
		Password:    "wrong",
	}
	handleMsgMhfLoadHouse(session, pkt)

	ack := readAck(t, session)
	if ack.ErrorCode != 1 {
		t.Errorf("expected fail ACK for wrong password, got error code %d", ack.ErrorCode)
	}
}

func TestLoadHouse_CorrectPassword_Succeeds(t *testing.T) {
	_, _, session, charID := setupHouseTest(t)

	_ = session.server.houseRepo.UpdateHouseState(charID, 2, "correct")

	pkt := &mhfpacket.MsgMhfLoadHouse{
		AckHandle:   16,
		CharID:      charID,
		Destination: 3,
		CheckPass:   true,
		Password:    "correct",
	}
	handleMsgMhfLoadHouse(session, pkt)

	ack := readAck(t, session)
	if ack.ErrorCode != 0 {
		t.Errorf("expected success for correct password, got error code %d", ack.ErrorCode)
	}
	if !ack.IsBufferResponse {
		t.Fatal("expected buffer response for house data")
	}
}

func TestGetMyhouseInfo_NoData(t *testing.T) {
	_, _, session, _ := setupHouseTest(t)

	pkt := &mhfpacket.MsgMhfGetMyhouseInfo{AckHandle: 17}
	handleMsgMhfGetMyhouseInfo(session, pkt)

	ack := readAck(t, session)
	if ack.ErrorCode != 0 {
		t.Fatalf("expected success, got error code %d", ack.ErrorCode)
	}
	// When no mission data exists, handler returns 9-byte default
	if len(ack.Payload) != 9 {
		t.Errorf("expected 9-byte default payload, got %d bytes", len(ack.Payload))
	}
}

func TestGetMyhouseInfo_WithData(t *testing.T) {
	_, _, session, charID := setupHouseTest(t)

	missionData := make([]byte, 50)
	missionData[0] = 0xDE
	missionData[1] = 0xAD
	_ = session.server.houseRepo.UpdateMission(charID, missionData)

	pkt := &mhfpacket.MsgMhfGetMyhouseInfo{AckHandle: 18}
	handleMsgMhfGetMyhouseInfo(session, pkt)

	ack := readAck(t, session)
	if ack.ErrorCode != 0 {
		t.Fatalf("expected success, got error code %d", ack.ErrorCode)
	}
	if len(ack.Payload) != 50 {
		t.Fatalf("expected 50-byte payload, got %d bytes", len(ack.Payload))
	}
	if ack.Payload[0] != 0xDE || ack.Payload[1] != 0xAD {
		t.Errorf("payload mismatch: got %#x %#x, want 0xDE 0xAD", ack.Payload[0], ack.Payload[1])
	}
}

func TestUpdateMyhouseInfo_SavesData(t *testing.T) {
	_, _, session, charID := setupHouseTest(t)

	missionData := make([]byte, 100)
	missionData[0] = 0xCA
	missionData[1] = 0xFE

	pkt := &mhfpacket.MsgMhfUpdateMyhouseInfo{
		AckHandle: 19,
		Data:      missionData,
	}
	handleMsgMhfUpdateMyhouseInfo(session, pkt)

	ack := readAck(t, session)
	if ack.ErrorCode != 0 {
		t.Fatalf("expected success, got error code %d", ack.ErrorCode)
	}

	// Verify via repository
	data, err := session.server.houseRepo.GetMission(charID)
	if err != nil {
		t.Fatalf("GetMission failed: %v", err)
	}
	if len(data) != 100 {
		t.Fatalf("mission data length = %d, want 100", len(data))
	}
	if data[0] != 0xCA || data[1] != 0xFE {
		t.Errorf("mission data mismatch: got %#x %#x, want 0xCA 0xFE", data[0], data[1])
	}
}

func TestEnumerateTitle_Empty(t *testing.T) {
	_, _, session, _ := setupHouseTest(t)

	pkt := &mhfpacket.MsgMhfEnumerateTitle{AckHandle: 20}
	handleMsgMhfEnumerateTitle(session, pkt)

	ack := readAck(t, session)
	if ack.ErrorCode != 0 {
		t.Fatalf("expected success, got error code %d", ack.ErrorCode)
	}
	bf := byteframe.NewByteFrameFromBytes(ack.Payload)
	count := bf.ReadUint16()
	if count != 0 {
		t.Errorf("expected 0 titles, got %d", count)
	}
}

func TestAcquireTitle_AndEnumerate(t *testing.T) {
	_, _, session, _ := setupHouseTest(t)

	// Acquire two titles
	acquirePkt := &mhfpacket.MsgMhfAcquireTitle{
		AckHandle: 21,
		TitleIDs:  []uint16{100, 200},
	}
	handleMsgMhfAcquireTitle(session, acquirePkt)
	ack := readAck(t, session)
	if ack.ErrorCode != 0 {
		t.Fatalf("acquire failed: error code %d", ack.ErrorCode)
	}

	// Enumerate
	enumPkt := &mhfpacket.MsgMhfEnumerateTitle{AckHandle: 22}
	handleMsgMhfEnumerateTitle(session, enumPkt)
	ack = readAck(t, session)
	if ack.ErrorCode != 0 {
		t.Fatalf("enumerate failed: error code %d", ack.ErrorCode)
	}

	bf := byteframe.NewByteFrameFromBytes(ack.Payload)
	count := bf.ReadUint16()
	if count != 2 {
		t.Errorf("expected 2 titles, got %d", count)
	}

	// Read title IDs
	_ = bf.ReadUint16() // unk
	ids := make(map[uint16]bool)
	for i := 0; i < int(count); i++ {
		id := bf.ReadUint16()
		ids[id] = true
		_ = bf.ReadUint16() // unk
		_ = bf.ReadUint32() // acquired timestamp
		_ = bf.ReadUint32() // updated timestamp
	}
	if !ids[100] || !ids[200] {
		t.Errorf("expected title IDs 100 and 200, got %v", ids)
	}
}

func TestAcquireTitle_Duplicate(t *testing.T) {
	_, _, session, charID := setupHouseTest(t)

	// Acquire title 300
	pkt1 := &mhfpacket.MsgMhfAcquireTitle{AckHandle: 23, TitleIDs: []uint16{300}}
	handleMsgMhfAcquireTitle(session, pkt1)
	_ = readAck(t, session)

	// Acquire same title again
	pkt2 := &mhfpacket.MsgMhfAcquireTitle{AckHandle: 24, TitleIDs: []uint16{300}}
	handleMsgMhfAcquireTitle(session, pkt2)
	_ = readAck(t, session)

	// Should still have exactly 1 title (upsert)
	titles, err := session.server.houseRepo.GetTitles(charID)
	if err != nil {
		t.Fatalf("GetTitles failed: %v", err)
	}
	if len(titles) != 1 {
		t.Errorf("expected 1 title after duplicate acquire, got %d", len(titles))
	}
}

func TestOperateWarehouse_Op0_GetBoxNames(t *testing.T) {
	_, _, session, charID := setupHouseTest(t)

	// Initialize warehouse and rename a box
	_ = session.server.houseRepo.InitializeWarehouse(charID)
	_ = session.server.houseRepo.RenameWarehouseBox(charID, 0, 0, "MyItems")

	pkt := &mhfpacket.MsgMhfOperateWarehouse{
		AckHandle: 25,
		Operation: 0,
	}
	handleMsgMhfOperateWarehouse(session, pkt)

	ack := readAck(t, session)
	if ack.ErrorCode != 0 {
		t.Fatalf("expected success, got error code %d", ack.ErrorCode)
	}
	if !ack.IsBufferResponse {
		t.Fatal("expected buffer response")
	}
	// Response format: op(1) + renewal(4) + usages(2) + count(1) + entries
	if len(ack.Payload) < 8 {
		t.Fatalf("payload too short: %d bytes", len(ack.Payload))
	}
	bf := byteframe.NewByteFrameFromBytes(ack.Payload)
	op := bf.ReadUint8()
	if op != 0 {
		t.Errorf("op = %d, want 0", op)
	}
}

func TestOperateWarehouse_Op3_GetUsageLimit(t *testing.T) {
	_, _, session, _ := setupHouseTest(t)

	pkt := &mhfpacket.MsgMhfOperateWarehouse{
		AckHandle: 26,
		Operation: 3,
	}
	handleMsgMhfOperateWarehouse(session, pkt)

	ack := readAck(t, session)
	if ack.ErrorCode != 0 {
		t.Fatalf("expected success, got error code %d", ack.ErrorCode)
	}
	// Response: op(1) + renewal_time(4) + usages(2) = 7 bytes
	bf := byteframe.NewByteFrameFromBytes(ack.Payload)
	op := bf.ReadUint8()
	if op != 3 {
		t.Errorf("op = %d, want 3", op)
	}
	renewalTime := bf.ReadUint32()
	usages := bf.ReadUint16()
	if renewalTime != 0 {
		t.Errorf("renewal time = %d, want 0", renewalTime)
	}
	if usages != 10000 {
		t.Errorf("usages = %d, want 10000", usages)
	}
}

func TestOperateWarehouse_Op2_RenameBoxIndexTooHigh(t *testing.T) {
	_, _, session, _ := setupHouseTest(t)

	pkt := &mhfpacket.MsgMhfOperateWarehouse{
		AckHandle: 27,
		Operation: 2,
		BoxIndex:  10, // > 9, rename should be skipped
		Name:      "ShouldNotRename",
	}
	handleMsgMhfOperateWarehouse(session, pkt)

	ack := readAck(t, session)
	if ack.ErrorCode != 0 {
		t.Fatalf("expected success ACK even with skipped rename, got error code %d", ack.ErrorCode)
	}
}

func TestEnumerateWarehouse_EmptyBox(t *testing.T) {
	_, _, session, _ := setupHouseTest(t)

	pkt := &mhfpacket.MsgMhfEnumerateWarehouse{
		AckHandle: 28,
		BoxType:   0, // Items
		BoxIndex:  0,
	}
	handleMsgMhfEnumerateWarehouse(session, pkt)

	ack := readAck(t, session)
	if ack.ErrorCode != 0 {
		t.Fatalf("expected success, got error code %d", ack.ErrorCode)
	}
	if !ack.IsBufferResponse {
		t.Fatal("expected buffer response")
	}
	// Empty box returns serialized empty list: count(2) + unk(2) = 4 bytes minimum
	if len(ack.Payload) < 4 {
		t.Errorf("expected at least 4-byte payload for empty box, got %d", len(ack.Payload))
	}
}

func TestUpdateWarehouse_Items(t *testing.T) {
	_, _, session, charID := setupHouseTest(t)

	items := []mhfitem.MHFItemStack{
		{Item: mhfitem.MHFItem{ItemID: 42}, Quantity: 10, WarehouseID: token.RNG.Uint32()},
		{Item: mhfitem.MHFItem{ItemID: 99}, Quantity: 5, WarehouseID: token.RNG.Uint32()},
	}
	pkt := &mhfpacket.MsgMhfUpdateWarehouse{
		AckHandle:    29,
		BoxType:      0,
		BoxIndex:     0,
		UpdatedItems: items,
	}
	handleMsgMhfUpdateWarehouse(session, pkt)

	ack := readAck(t, session)
	if ack.ErrorCode != 0 {
		t.Fatalf("expected success, got error code %d", ack.ErrorCode)
	}

	// Read back via enumerate
	session2 := createMockSession(charID, session.server)
	enumPkt := &mhfpacket.MsgMhfEnumerateWarehouse{
		AckHandle: 30,
		BoxType:   0,
		BoxIndex:  0,
	}
	handleMsgMhfEnumerateWarehouse(session2, enumPkt)

	ack2 := readAck(t, session2)
	if ack2.ErrorCode != 0 {
		t.Fatalf("enumerate failed: error code %d", ack2.ErrorCode)
	}
	// Parse the serialized items
	bf := byteframe.NewByteFrameFromBytes(ack2.Payload)
	count := bf.ReadUint16()
	if count != 2 {
		t.Errorf("expected 2 items in warehouse, got %d", count)
	}
}

func TestLoadDecoMyset_Default(t *testing.T) {
	_, _, session, _ := setupHouseTest(t)

	pkt := &mhfpacket.MsgMhfLoadDecoMyset{AckHandle: 31}
	handleMsgMhfLoadDecoMyset(session, pkt)

	ack := readAck(t, session)
	if ack.ErrorCode != 0 {
		t.Fatalf("expected success, got error code %d", ack.ErrorCode)
	}
	if !ack.IsBufferResponse {
		t.Fatal("expected buffer response")
	}
	// G10+ mode returns {0x01, 0x00}
	if len(ack.Payload) < 2 {
		t.Fatalf("expected at least 2-byte payload, got %d", len(ack.Payload))
	}
	if ack.Payload[0] != 0x01 || ack.Payload[1] != 0x00 {
		t.Errorf("expected default {0x01, 0x00}, got {%#x, %#x}", ack.Payload[0], ack.Payload[1])
	}
}

// =============================================================================
// Existing pure-logic tests and benchmarks (unchanged)
// =============================================================================

// TestWarehouseItemSerialization verifies warehouse item serialization
func TestWarehouseItemSerialization(t *testing.T) {
	tests := []struct {
		name  string
		items []mhfitem.MHFItemStack
	}{
		{
			name:  "empty_warehouse",
			items: []mhfitem.MHFItemStack{},
		},
		{
			name: "single_item",
			items: []mhfitem.MHFItemStack{
				{Item: mhfitem.MHFItem{ItemID: 1}, Quantity: 10},
			},
		},
		{
			name: "multiple_items",
			items: []mhfitem.MHFItemStack{
				{Item: mhfitem.MHFItem{ItemID: 1}, Quantity: 10},
				{Item: mhfitem.MHFItem{ItemID: 2}, Quantity: 20},
				{Item: mhfitem.MHFItem{ItemID: 3}, Quantity: 30},
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Serialize
			serialized := mhfitem.SerializeWarehouseItems(tt.items)

			// Basic validation
			if serialized == nil {
				t.Error("serialization returned nil")
			}

			// Verify we can work with the serialized data
			if serialized == nil {
				t.Error("invalid serialized length")
			}
		})
	}
}

// TestWarehouseEquipmentSerialization verifies warehouse equipment serialization
func TestWarehouseEquipmentSerialization(t *testing.T) {
	tests := []struct {
		name      string
		equipment []mhfitem.MHFEquipment
	}{
		{
			name:      "empty_equipment",
			equipment: []mhfitem.MHFEquipment{},
		},
		{
			name:      "single_equipment",
			equipment: createTestEquipment([]uint16{100}, []uint32{1}),
		},
		{
			name:      "multiple_equipment",
			equipment: createTestEquipment([]uint16{100, 101, 102}, []uint32{1, 2, 3}),
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Serialize
			serialized := mhfitem.SerializeWarehouseEquipment(tt.equipment, cfg.ZZ)

			// Basic validation
			if serialized == nil {
				t.Error("serialization returned nil")
			}

			// Verify we can work with the serialized data
			if serialized == nil {
				t.Error("invalid serialized length")
			}
		})
	}
}

// TestWarehouseItemDiff verifies the item diff calculation
func TestWarehouseItemDiff(t *testing.T) {
	tests := []struct {
		name     string
		oldItems []mhfitem.MHFItemStack
		newItems []mhfitem.MHFItemStack
		wantDiff bool
	}{
		{
			name:     "no_changes",
			oldItems: []mhfitem.MHFItemStack{{Item: mhfitem.MHFItem{ItemID: 1}, Quantity: 10}},
			newItems: []mhfitem.MHFItemStack{{Item: mhfitem.MHFItem{ItemID: 1}, Quantity: 10}},
			wantDiff: false,
		},
		{
			name:     "quantity_changed",
			oldItems: []mhfitem.MHFItemStack{{Item: mhfitem.MHFItem{ItemID: 1}, Quantity: 10}},
			newItems: []mhfitem.MHFItemStack{{Item: mhfitem.MHFItem{ItemID: 1}, Quantity: 15}},
			wantDiff: true,
		},
		{
			name:     "item_added",
			oldItems: []mhfitem.MHFItemStack{{Item: mhfitem.MHFItem{ItemID: 1}, Quantity: 10}},
			newItems: []mhfitem.MHFItemStack{
				{Item: mhfitem.MHFItem{ItemID: 1}, Quantity: 10},
				{Item: mhfitem.MHFItem{ItemID: 2}, Quantity: 5},
			},
			wantDiff: true,
		},
		{
			name: "item_removed",
			oldItems: []mhfitem.MHFItemStack{
				{Item: mhfitem.MHFItem{ItemID: 1}, Quantity: 10},
				{Item: mhfitem.MHFItem{ItemID: 2}, Quantity: 5},
			},
			newItems: []mhfitem.MHFItemStack{{Item: mhfitem.MHFItem{ItemID: 1}, Quantity: 10}},
			wantDiff: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			diff := mhfitem.DiffItemStacks(tt.oldItems, tt.newItems)

			// Verify that diff returns a valid result (not nil)
			if diff == nil {
				t.Error("diff should not be nil")
			}

			// The diff function returns items where Quantity > 0
			// So with no changes (all same quantity), diff should have same items
			if tt.name == "no_changes" {
				if len(diff) == 0 {
					t.Error("no_changes should return items")
				}
			}
		})
	}
}

// TestWarehouseEquipmentMerge verifies equipment merging logic
func TestWarehouseEquipmentMerge(t *testing.T) {
	tests := []struct {
		name       string
		oldEquip   []mhfitem.MHFEquipment
		newEquip   []mhfitem.MHFEquipment
		wantMerged int
	}{
		{
			name:       "merge_empty",
			oldEquip:   []mhfitem.MHFEquipment{},
			newEquip:   []mhfitem.MHFEquipment{},
			wantMerged: 0,
		},
		{
			name: "add_new_equipment",
			oldEquip: []mhfitem.MHFEquipment{
				{ItemID: 100, WarehouseID: 1},
			},
			newEquip: []mhfitem.MHFEquipment{
				{ItemID: 101, WarehouseID: 0}, // New item, no warehouse ID yet
			},
			wantMerged: 2, // Old + new
		},
		{
			name: "update_existing_equipment",
			oldEquip: []mhfitem.MHFEquipment{
				{ItemID: 100, WarehouseID: 1},
			},
			newEquip: []mhfitem.MHFEquipment{
				{ItemID: 101, WarehouseID: 1}, // Update existing
			},
			wantMerged: 1, // Updated in place
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Simulate the merge logic from handleMsgMhfUpdateWarehouse
			var finalEquip []mhfitem.MHFEquipment
			oEquips := tt.oldEquip

			for _, uEquip := range tt.newEquip {
				exists := false
				for i := range oEquips {
					if oEquips[i].WarehouseID == uEquip.WarehouseID && uEquip.WarehouseID != 0 {
						exists = true
						oEquips[i].ItemID = uEquip.ItemID
						break
					}
				}
				if !exists {
					// Generate new warehouse ID
					uEquip.WarehouseID = token.RNG.Uint32()
					finalEquip = append(finalEquip, uEquip)
				}
			}

			for _, oEquip := range oEquips {
				if oEquip.ItemID > 0 {
					finalEquip = append(finalEquip, oEquip)
				}
			}

			// Verify merge result count
			if len(finalEquip) != tt.wantMerged {
				t.Errorf("expected %d merged equipment, got %d", tt.wantMerged, len(finalEquip))
			}
		})
	}
}

// TestWarehouseIDGeneration verifies warehouse ID uniqueness
func TestWarehouseIDGeneration(t *testing.T) {
	// Generate multiple warehouse IDs and verify they're unique
	idCount := 100
	ids := make(map[uint32]bool)

	for i := 0; i < idCount; i++ {
		id := token.RNG.Uint32()
		if id == 0 {
			t.Error("generated warehouse ID is 0 (invalid)")
		}
		if ids[id] {
			// While collisions are possible with random IDs,
			// they should be extremely rare
			t.Logf("Warning: duplicate warehouse ID generated: %d", id)
		}
		ids[id] = true
	}

	if len(ids) < idCount*90/100 {
		t.Errorf("too many duplicate IDs: got %d unique out of %d", len(ids), idCount)
	}
}

// TestWarehouseItemRemoval verifies item removal logic
func TestWarehouseItemRemoval(t *testing.T) {
	tests := []struct {
		name       string
		items      []mhfitem.MHFItemStack
		removeID   uint16
		wantRemain int
	}{
		{
			name: "remove_existing",
			items: []mhfitem.MHFItemStack{
				{Item: mhfitem.MHFItem{ItemID: 1}, Quantity: 10},
				{Item: mhfitem.MHFItem{ItemID: 2}, Quantity: 20},
			},
			removeID:   1,
			wantRemain: 1,
		},
		{
			name: "remove_non_existing",
			items: []mhfitem.MHFItemStack{
				{Item: mhfitem.MHFItem{ItemID: 1}, Quantity: 10},
			},
			removeID:   999,
			wantRemain: 1,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			var remaining []mhfitem.MHFItemStack
			for _, item := range tt.items {
				if item.Item.ItemID != tt.removeID {
					remaining = append(remaining, item)
				}
			}

			if len(remaining) != tt.wantRemain {
				t.Errorf("expected %d remaining items, got %d", tt.wantRemain, len(remaining))
			}
		})
	}
}

// TestWarehouseEquipmentRemoval verifies equipment removal logic
func TestWarehouseEquipmentRemoval(t *testing.T) {
	tests := []struct {
		name       string
		equipment  []mhfitem.MHFEquipment
		setZeroID  uint32
		wantActive int
	}{
		{
			name: "remove_by_setting_zero",
			equipment: []mhfitem.MHFEquipment{
				{ItemID: 100, WarehouseID: 1},
				{ItemID: 101, WarehouseID: 2},
			},
			setZeroID:  1,
			wantActive: 1,
		},
		{
			name: "all_active",
			equipment: []mhfitem.MHFEquipment{
				{ItemID: 100, WarehouseID: 1},
				{ItemID: 101, WarehouseID: 2},
			},
			setZeroID:  999,
			wantActive: 2,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Simulate removal by setting ItemID to 0
			equipment := make([]mhfitem.MHFEquipment, len(tt.equipment))
			copy(equipment, tt.equipment)

			for i := range equipment {
				if equipment[i].WarehouseID == tt.setZeroID {
					equipment[i].ItemID = 0
				}
			}

			// Count active equipment (ItemID > 0)
			activeCount := 0
			for _, eq := range equipment {
				if eq.ItemID > 0 {
					activeCount++
				}
			}

			if activeCount != tt.wantActive {
				t.Errorf("expected %d active equipment, got %d", tt.wantActive, activeCount)
			}
		})
	}
}

// TestWarehouseBoxIndexValidation verifies box index bounds
func TestWarehouseBoxIndexValidation(t *testing.T) {
	tests := []struct {
		name     string
		boxIndex uint8
		isValid  bool
	}{
		{
			name:     "box_0",
			boxIndex: 0,
			isValid:  true,
		},
		{
			name:     "box_1",
			boxIndex: 1,
			isValid:  true,
		},
		{
			name:     "box_9",
			boxIndex: 9,
			isValid:  true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Verify box index is within reasonable bounds
			if tt.isValid && tt.boxIndex > 100 {
				t.Error("box index unreasonably high")
			}
		})
	}
}

// TestWarehouseErrorRecovery verifies error handling doesn't corrupt state
func TestWarehouseErrorRecovery(t *testing.T) {
	t.Run("database_error_handling", func(t *testing.T) {
		// After our fix, database errors should:
		// 1. Be logged with s.logger.Error()
		// 2. Send doAckSimpleFail()
		// 3. Return immediately
		// 4. NOT send doAckSimpleSucceed() (the bug we fixed)

		// This test documents the expected behavior
	})

	t.Run("serialization_error_handling", func(t *testing.T) {
		// Test that serialization errors are handled gracefully
		emptyItems := []mhfitem.MHFItemStack{}
		serialized := mhfitem.SerializeWarehouseItems(emptyItems)

		// Should handle empty gracefully
		if serialized == nil {
			t.Error("serialization of empty items should not return nil")
		}
	})
}

// BenchmarkWarehouseSerialization benchmarks warehouse serialization performance
func BenchmarkWarehouseSerialization(b *testing.B) {
	items := []mhfitem.MHFItemStack{
		{Item: mhfitem.MHFItem{ItemID: 1}, Quantity: 10},
		{Item: mhfitem.MHFItem{ItemID: 2}, Quantity: 20},
		{Item: mhfitem.MHFItem{ItemID: 3}, Quantity: 30},
		{Item: mhfitem.MHFItem{ItemID: 4}, Quantity: 40},
		{Item: mhfitem.MHFItem{ItemID: 5}, Quantity: 50},
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = mhfitem.SerializeWarehouseItems(items)
	}
}

// BenchmarkWarehouseEquipmentMerge benchmarks equipment merge performance
func BenchmarkWarehouseEquipmentMerge(b *testing.B) {
	oldEquip := make([]mhfitem.MHFEquipment, 50)
	for i := range oldEquip {
		oldEquip[i] = mhfitem.MHFEquipment{
			ItemID:      uint16(100 + i),
			WarehouseID: uint32(i + 1),
		}
	}

	newEquip := make([]mhfitem.MHFEquipment, 10)
	for i := range newEquip {
		newEquip[i] = mhfitem.MHFEquipment{
			ItemID:      uint16(200 + i),
			WarehouseID: uint32(i + 1),
		}
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		var finalEquip []mhfitem.MHFEquipment
		oEquips := oldEquip

		for _, uEquip := range newEquip {
			exists := false
			for j := range oEquips {
				if oEquips[j].WarehouseID == uEquip.WarehouseID {
					exists = true
					oEquips[j].ItemID = uEquip.ItemID
					break
				}
			}
			if !exists {
				finalEquip = append(finalEquip, uEquip)
			}
		}

		for _, oEquip := range oEquips {
			if oEquip.ItemID > 0 {
				finalEquip = append(finalEquip, oEquip)
			}
		}
		_ = finalEquip // Use finalEquip to avoid unused variable warning
	}
}

func TestHandleMsgMhfEnumerateHouse_Method3_SearchByName(t *testing.T) {
	srv := createMockServer()
	srv.erupeConfig.RealClientMode = cfg.ZZ
	srv.houseRepo = newMockHouseRepoForItems()
	s := createMockSession(100, srv)

	pkt := &mhfpacket.MsgMhfEnumerateHouse{AckHandle: 1, Method: 3, Name: "TestHouse"}
	handleMsgMhfEnumerateHouse(s, pkt)
	<-s.sendPackets
}

func TestHandleMsgMhfEnumerateHouse_Method4_ByCharID(t *testing.T) {
	srv := createMockServer()
	srv.erupeConfig.RealClientMode = cfg.ZZ
	srv.houseRepo = newMockHouseRepoForItems()
	s := createMockSession(100, srv)

	pkt := &mhfpacket.MsgMhfEnumerateHouse{AckHandle: 1, Method: 4, CharID: 200}
	handleMsgMhfEnumerateHouse(s, pkt)
	<-s.sendPackets
}

func TestHandleMsgMhfEnumerateHouse_Method5_RecentVisitors(t *testing.T) {
	srv := createMockServer()
	srv.houseRepo = newMockHouseRepoForItems()
	s := createMockSession(100, srv)

	pkt := &mhfpacket.MsgMhfEnumerateHouse{AckHandle: 1, Method: 5}
	handleMsgMhfEnumerateHouse(s, pkt)
	<-s.sendPackets
}

func TestHandleMsgMhfEnumerateHouse_Method1_Friends(t *testing.T) {
	srv := createMockServer()
	srv.houseRepo = newMockHouseRepoForItems()
	charRepo := newMockCharacterRepo()
	charRepo.strings["friends"] = ""
	srv.charRepo = charRepo
	s := createMockSession(100, srv)

	pkt := &mhfpacket.MsgMhfEnumerateHouse{AckHandle: 1, Method: 1}
	handleMsgMhfEnumerateHouse(s, pkt)
	<-s.sendPackets
}

func TestHandleMsgMhfEnumerateHouse_Method2_GuildMembers(t *testing.T) {
	srv := createMockServer()
	srv.houseRepo = newMockHouseRepoForItems()
	guild := &Guild{ID: 1}
	srv.guildRepo = &mockGuildRepo{guild: guild}
	s := createMockSession(100, srv)

	pkt := &mhfpacket.MsgMhfEnumerateHouse{AckHandle: 1, Method: 2}
	handleMsgMhfEnumerateHouse(s, pkt)
	<-s.sendPackets
}

func TestHandleMsgMhfEnumerateHouse_Method2_NoGuild(t *testing.T) {
	srv := createMockServer()
	srv.houseRepo = newMockHouseRepoForItems()
	srv.guildRepo = &mockGuildRepo{getErr: errNotFound}
	s := createMockSession(100, srv)

	pkt := &mhfpacket.MsgMhfEnumerateHouse{AckHandle: 1, Method: 2}
	handleMsgMhfEnumerateHouse(s, pkt)
	<-s.sendPackets
}

func TestHandleMsgMhfSaveDecoMyset_ShortPayload(t *testing.T) {
	srv := createMockServer()
	srv.charRepo = newMockCharacterRepo()
	s := createMockSession(100, srv)

	pkt := &mhfpacket.MsgMhfSaveDecoMyset{AckHandle: 1, RawDataPayload: []byte{0x00, 0x01}}
	handleMsgMhfSaveDecoMyset(s, pkt)
	<-s.sendPackets
}

func TestHandleMsgMhfSaveDecoMyset_WithData(t *testing.T) {
	srv := createMockServer()
	charRepo := newMockCharacterRepo()
	// Pre-populate with version byte + 0 sets
	charRepo.columns["decomyset"] = []byte{0x01, 0x00}
	srv.charRepo = charRepo
	srv.erupeConfig.RealClientMode = cfg.ZZ

	s := createMockSession(100, srv)

	// Build payload: version byte + 1 set with index 0 + 76 bytes of data
	payload := make([]byte, 3+2+76)
	payload[0] = 0x01 // version
	payload[1] = 0x01 // count
	payload[2] = 0x00 // padding

	pkt := &mhfpacket.MsgMhfSaveDecoMyset{AckHandle: 1, RawDataPayload: payload}
	handleMsgMhfSaveDecoMyset(s, pkt)
	<-s.sendPackets
}

func TestHandleMsgMhfInfoTournament_Type2(t *testing.T) {
	srv := createMockServer()
	s := createMockSession(100, srv)

	pkt := &mhfpacket.MsgMhfInfoTournament{AckHandle: 1, QueryType: 2}
	handleMsgMhfInfoTournament(s, pkt)
	<-s.sendPackets
}

func TestHandleMsgMhfUpdateInterior_Normal(t *testing.T) {
	srv := createMockServer()
	srv.houseRepo = newMockHouseRepoForItems()
	s := createMockSession(100, srv)

	pkt := &mhfpacket.MsgMhfUpdateInterior{AckHandle: 1, InteriorData: make([]byte, 20)}
	handleMsgMhfUpdateInterior(s, pkt)
	<-s.sendPackets
}

func TestHandleMsgMhfUpdateInterior_TooLarge(t *testing.T) {
	srv := createMockServer()
	srv.houseRepo = newMockHouseRepoForItems()
	s := createMockSession(100, srv)

	pkt := &mhfpacket.MsgMhfUpdateInterior{AckHandle: 1, InteriorData: make([]byte, 100)}
	handleMsgMhfUpdateInterior(s, pkt)
	<-s.sendPackets
}

func TestHandleMsgMhfUpdateMyhouseInfo_Normal(t *testing.T) {
	srv := createMockServer()
	srv.houseRepo = newMockHouseRepoForItems()
	s := createMockSession(100, srv)

	pkt := &mhfpacket.MsgMhfUpdateMyhouseInfo{AckHandle: 1, Data: make([]byte, 9)}
	handleMsgMhfUpdateMyhouseInfo(s, pkt)
	<-s.sendPackets
}

func TestHandleMsgMhfUpdateMyhouseInfo_TooLarge(t *testing.T) {
	srv := createMockServer()
	srv.houseRepo = newMockHouseRepoForItems()
	s := createMockSession(100, srv)

	pkt := &mhfpacket.MsgMhfUpdateMyhouseInfo{AckHandle: 1, Data: make([]byte, 600)}
	handleMsgMhfUpdateMyhouseInfo(s, pkt)
	<-s.sendPackets
}

func TestHandleMsgMhfGetMyhouseInfo(t *testing.T) {
	srv := createMockServer()
	srv.houseRepo = newMockHouseRepoForItems()
	s := createMockSession(100, srv)

	pkt := &mhfpacket.MsgMhfGetMyhouseInfo{AckHandle: 1}
	handleMsgMhfGetMyhouseInfo(s, pkt)
	<-s.sendPackets
}

func TestHandleMsgMhfEnumerateTitle(t *testing.T) {
	srv := createMockServer()
	srv.houseRepo = newMockHouseRepoForItems()
	s := createMockSession(100, srv)

	pkt := &mhfpacket.MsgMhfEnumerateTitle{AckHandle: 1}
	handleMsgMhfEnumerateTitle(s, pkt)
	<-s.sendPackets
}

func TestHandleMsgMhfAcquireTitle(t *testing.T) {
	srv := createMockServer()
	srv.houseRepo = newMockHouseRepoForItems()
	s := createMockSession(100, srv)

	pkt := &mhfpacket.MsgMhfAcquireTitle{AckHandle: 1, TitleIDs: []uint16{1, 2, 3}}
	handleMsgMhfAcquireTitle(s, pkt)
	<-s.sendPackets
}

func TestHandleMsgMhfUpdateHouse(t *testing.T) {
	srv := createMockServer()
	srv.houseRepo = newMockHouseRepoForItems()
	s := createMockSession(100, srv)

	pkt := &mhfpacket.MsgMhfUpdateHouse{AckHandle: 1, State: 2, Password: "1234"}
	handleMsgMhfUpdateHouse(s, pkt)
	<-s.sendPackets
}

func TestHandleMsgMhfOperateWarehouse_Op0(t *testing.T) {
	srv := createMockServer()
	srv.houseRepo = newMockHouseRepoForItems()
	s := createMockSession(100, srv)

	pkt := &mhfpacket.MsgMhfOperateWarehouse{AckHandle: 1, Operation: 0}
	handleMsgMhfOperateWarehouse(s, pkt)
	<-s.sendPackets
}

func TestHandleMsgMhfOperateWarehouse_Op1(t *testing.T) {
	srv := createMockServer()
	srv.houseRepo = newMockHouseRepoForItems()
	s := createMockSession(100, srv)

	pkt := &mhfpacket.MsgMhfOperateWarehouse{AckHandle: 1, Operation: 1}
	handleMsgMhfOperateWarehouse(s, pkt)
	<-s.sendPackets
}

func TestHandleMsgMhfOperateWarehouse_Op2_Rename(t *testing.T) {
	srv := createMockServer()
	srv.houseRepo = newMockHouseRepoForItems()
	s := createMockSession(100, srv)

	pkt := &mhfpacket.MsgMhfOperateWarehouse{AckHandle: 1, Operation: 2, BoxType: 0, BoxIndex: 1, Name: "MyBox"}
	handleMsgMhfOperateWarehouse(s, pkt)
	<-s.sendPackets
}

func TestHandleMsgMhfOperateWarehouse_Op3(t *testing.T) {
	srv := createMockServer()
	srv.houseRepo = newMockHouseRepoForItems()
	s := createMockSession(100, srv)

	pkt := &mhfpacket.MsgMhfOperateWarehouse{AckHandle: 1, Operation: 3}
	handleMsgMhfOperateWarehouse(s, pkt)
	<-s.sendPackets
}

func TestHandleMsgMhfOperateWarehouse_Op4(t *testing.T) {
	srv := createMockServer()
	srv.houseRepo = newMockHouseRepoForItems()
	s := createMockSession(100, srv)

	pkt := &mhfpacket.MsgMhfOperateWarehouse{AckHandle: 1, Operation: 4}
	handleMsgMhfOperateWarehouse(s, pkt)
	<-s.sendPackets
}

// --- handleMsgMhfLoadHouse tests ---

func TestLoadHouse_OwnHouse(t *testing.T) {
	server := createMockServer()
	server.houseRepo = newMockHouseRepoForItems()
	server.charRepo = newMockCharacterRepo()
	session := createMockSession(1, server)

	pkt := &mhfpacket.MsgMhfLoadHouse{AckHandle: 100, CharID: 1, Destination: 9}
	handleMsgMhfLoadHouse(session, pkt)

	select {
	case <-session.sendPackets:
	default:
		t.Error("No response packet queued")
	}
}

func TestLoadHouse_OthersHouse(t *testing.T) {
	server := createMockServer()
	server.houseRepo = newMockHouseRepoForItems()
	server.charRepo = newMockCharacterRepo()
	session := createMockSession(1, server)

	pkt := &mhfpacket.MsgMhfLoadHouse{AckHandle: 100, CharID: 2, Destination: 3}
	handleMsgMhfLoadHouse(session, pkt)

	select {
	case <-session.sendPackets:
	default:
		t.Error("No response packet queued")
	}
}

func TestLoadHouse_Bookshelf(t *testing.T) {
	server := createMockServer()
	server.houseRepo = newMockHouseRepoForItems()
	server.charRepo = newMockCharacterRepo()
	session := createMockSession(1, server)

	pkt := &mhfpacket.MsgMhfLoadHouse{AckHandle: 100, CharID: 1, Destination: 4}
	handleMsgMhfLoadHouse(session, pkt)

	select {
	case <-session.sendPackets:
	default:
		t.Error("No response packet queued")
	}
}

func TestLoadHouse_Gallery(t *testing.T) {
	server := createMockServer()
	server.houseRepo = newMockHouseRepoForItems()
	session := createMockSession(1, server)

	pkt := &mhfpacket.MsgMhfLoadHouse{AckHandle: 100, CharID: 1, Destination: 5}
	handleMsgMhfLoadHouse(session, pkt)

	select {
	case <-session.sendPackets:
	default:
		t.Error("No response packet queued")
	}
}

func TestLoadHouse_Tore(t *testing.T) {
	server := createMockServer()
	server.houseRepo = newMockHouseRepoForItems()
	session := createMockSession(1, server)

	pkt := &mhfpacket.MsgMhfLoadHouse{AckHandle: 100, CharID: 1, Destination: 8}
	handleMsgMhfLoadHouse(session, pkt)

	select {
	case <-session.sendPackets:
	default:
		t.Error("No response packet queued")
	}
}

func TestLoadHouse_Garden(t *testing.T) {
	server := createMockServer()
	server.houseRepo = newMockHouseRepoForItems()
	server.goocooRepo = newMockGoocooRepo()
	session := createMockSession(1, server)

	pkt := &mhfpacket.MsgMhfLoadHouse{AckHandle: 100, CharID: 1, Destination: 10}
	handleMsgMhfLoadHouse(session, pkt)

	select {
	case <-session.sendPackets:
	default:
		t.Error("No response packet queued")
	}
}

func TestLoadHouse_UnknownDestination(t *testing.T) {
	server := createMockServer()
	server.houseRepo = newMockHouseRepoForItems()
	session := createMockSession(1, server)

	pkt := &mhfpacket.MsgMhfLoadHouse{AckHandle: 100, CharID: 1, Destination: 99}
	handleMsgMhfLoadHouse(session, pkt)

	select {
	case <-session.sendPackets:
	default:
		t.Error("No response packet queued")
	}
}

// --- handleMsgMhfLoadDecoMyset tests ---

func TestLoadDecoMyset(t *testing.T) {
	server := createMockServer()
	charMock := newMockCharacterRepo()
	server.charRepo = charMock
	session := createMockSession(1, server)

	pkt := &mhfpacket.MsgMhfLoadDecoMyset{AckHandle: 100}
	handleMsgMhfLoadDecoMyset(session, pkt)

	select {
	case p := <-session.sendPackets:
		if len(p.data) == 0 {
			t.Error("Response should have default data")
		}
	default:
		t.Error("No response packet queued")
	}
}

// --- handleMsgMhfUpdateWarehouse tests ---

func TestUpdateWarehouse_EmptyItems(t *testing.T) {
	server := createMockServer()
	server.houseRepo = newMockHouseRepoForItems()
	session := createMockSession(1, server)

	pkt := &mhfpacket.MsgMhfUpdateWarehouse{
		AckHandle:    100,
		BoxType:      0,
		BoxIndex:     0,
		UpdatedItems: []mhfitem.MHFItemStack{},
	}
	handleMsgMhfUpdateWarehouse(session, pkt)

	select {
	case <-session.sendPackets:
	default:
		t.Error("No response packet queued")
	}
}

func TestUpdateWarehouse_EmptyEquipment(t *testing.T) {
	server := createMockServer()
	server.houseRepo = newMockHouseRepoForItems()
	session := createMockSession(1, server)

	pkt := &mhfpacket.MsgMhfUpdateWarehouse{
		AckHandle:        100,
		BoxType:          1,
		BoxIndex:         0,
		UpdatedEquipment: []mhfitem.MHFEquipment{},
	}
	handleMsgMhfUpdateWarehouse(session, pkt)

	select {
	case <-session.sendPackets:
	default:
		t.Error("No response packet queued")
	}
}

func TestUpdateWarehouse_InvalidIndex(t *testing.T) {
	server := createMockServer()
	server.houseRepo = newMockHouseRepoForItems()
	session := createMockSession(1, server)

	pkt := &mhfpacket.MsgMhfUpdateWarehouse{
		AckHandle: 100,
		BoxType:   0,
		BoxIndex:  15, // > 10
	}
	handleMsgMhfUpdateWarehouse(session, pkt)

	select {
	case <-session.sendPackets:
	default:
		t.Error("No response packet queued")
	}
}

func TestHandleMsgMhfEnumerateWarehouse_Items(t *testing.T) {
	srv := createMockServer()
	srv.houseRepo = newMockHouseRepoForItems()
	s := createMockSession(100, srv)

	pkt := &mhfpacket.MsgMhfEnumerateWarehouse{AckHandle: 1, BoxType: 0, BoxIndex: 0}
	handleMsgMhfEnumerateWarehouse(s, pkt)
	<-s.sendPackets
}

func TestHandleMsgMhfEnumerateWarehouse_Equipment(t *testing.T) {
	srv := createMockServer()
	srv.houseRepo = newMockHouseRepoForItems()
	s := createMockSession(100, srv)

	pkt := &mhfpacket.MsgMhfEnumerateWarehouse{AckHandle: 1, BoxType: 1, BoxIndex: 0}
	handleMsgMhfEnumerateWarehouse(s, pkt)
	<-s.sendPackets
}
