package channelserver

import (
	"testing"

	"erupe-ce/network/mhfpacket"
)

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

	pkt := &mhfpacket.MsgMhfGetRengokuRankingRank{
		AckHandle: 12345,
	}

	handleMsgMhfGetRengokuRankingRank(session, pkt)

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

func TestRengokuScoreStruct(t *testing.T) {
	score := RengokuScore{
		Name:  "TestPlayer",
		Score: 12345,
	}

	if score.Name != "TestPlayer" {
		t.Errorf("Name = %s, want TestPlayer", score.Name)
	}
	if score.Score != 12345 {
		t.Errorf("Score = %d, want 12345", score.Score)
	}
}

func TestRengokuScoreStruct_DefaultValues(t *testing.T) {
	score := RengokuScore{}

	if score.Name != "" {
		t.Errorf("Default Name should be empty, got %s", score.Name)
	}
	if score.Score != 0 {
		t.Errorf("Default Score should be 0, got %d", score.Score)
	}
}

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

	pkt := &mhfpacket.MsgMhfGetRengokuRankingRank{
		AckHandle: 55555,
	}

	handleMsgMhfGetRengokuRankingRank(session, pkt)

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

func TestRengokuScoreStruct_Fields(t *testing.T) {
	score := RengokuScore{
		Name:  "Hunter",
		Score: 99999,
	}

	if score.Name != "Hunter" {
		t.Errorf("Name = %s, want Hunter", score.Name)
	}
	if score.Score != 99999 {
		t.Errorf("Score = %d, want 99999", score.Score)
	}
}

// TestHandleMsgMhfGetRengokuRankingRank_DifferentAck verifies rengoku ranking
// works with different ack handles.
func TestHandleMsgMhfGetRengokuRankingRank_DifferentAck(t *testing.T) {
	server := createMockServer()

	ackHandles := []uint32{0, 1, 54321, 0xDEADBEEF}
	for _, ack := range ackHandles {
		session := createMockSession(1, server)
		pkt := &mhfpacket.MsgMhfGetRengokuRankingRank{AckHandle: ack}

		handleMsgMhfGetRengokuRankingRank(session, pkt)

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

// --- handleMsgMhfSaveRengokuData tests ---

func TestSaveRengokuData_TooSmall(t *testing.T) {
	server := createMockServer()
	charMock := newMockCharacterRepo()
	server.charRepo = charMock
	server.rengokuRepo = &mockRengokuRepo{}
	session := createMockSession(1, server)

	pkt := &mhfpacket.MsgMhfSaveRengokuData{
		AckHandle:      100,
		RawDataPayload: make([]byte, 10), // too small
	}

	handleMsgMhfSaveRengokuData(session, pkt)

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

func TestSaveRengokuData_TooLarge(t *testing.T) {
	server := createMockServer()
	charMock := newMockCharacterRepo()
	server.charRepo = charMock
	server.rengokuRepo = &mockRengokuRepo{}
	session := createMockSession(1, server)

	pkt := &mhfpacket.MsgMhfSaveRengokuData{
		AckHandle:      100,
		RawDataPayload: make([]byte, 5000), // too large
	}

	handleMsgMhfSaveRengokuData(session, pkt)

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

func TestSaveRengokuData_NormalSave(t *testing.T) {
	server := createMockServer()
	charMock := newMockCharacterRepo()
	server.charRepo = charMock
	server.rengokuRepo = &mockRengokuRepo{}
	session := createMockSession(1, server)

	// Build valid payload (>= rengokuMinPayloadSize=91 bytes)
	payload := make([]byte, 100)
	// Set sentinel to non-zero so it's not rejected
	payload[0] = 0x00
	payload[1] = 0x00
	payload[2] = 0x00
	payload[3] = 0x01
	// Set some skill data so it's not zeroed
	payload[rengokuSkillSlotsStart] = 1

	pkt := &mhfpacket.MsgMhfSaveRengokuData{
		AckHandle:      100,
		RawDataPayload: payload,
	}

	handleMsgMhfSaveRengokuData(session, pkt)

	if charMock.columns["rengokudata"] == nil {
		t.Error("rengokudata should be saved")
	}

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

func TestSaveRengokuData_SkillMerge(t *testing.T) {
	server := createMockServer()
	charMock := newMockCharacterRepo()

	// Set up existing data with skills
	existing := make([]byte, 100)
	existing[0] = 0x00
	existing[1] = 0x00
	existing[2] = 0x00
	existing[3] = 0x01
	existing[rengokuSkillSlotsStart] = 5 // has skill
	existing[rengokuSkillValuesStart] = 3
	charMock.columns["rengokudata"] = existing

	server.charRepo = charMock
	server.rengokuRepo = &mockRengokuRepo{}
	session := createMockSession(1, server)

	// Build payload with zeroed skills but has points (triggers merge)
	payload := make([]byte, 100)
	payload[0] = 0x00
	payload[1] = 0x00
	payload[2] = 0x00
	payload[3] = 0x01
	// Skills are zeroed (default)
	// But points are set
	payload[rengokuPointsStart] = 10

	pkt := &mhfpacket.MsgMhfSaveRengokuData{
		AckHandle:      100,
		RawDataPayload: payload,
	}

	handleMsgMhfSaveRengokuData(session, pkt)

	saved := charMock.columns["rengokudata"]
	if saved == nil {
		t.Fatal("rengokudata should be saved")
	}
	// Skills should be merged from existing
	if saved[rengokuSkillSlotsStart] != 5 {
		t.Errorf("Skill slot should be merged from existing, got %d", saved[rengokuSkillSlotsStart])
	}

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

func TestSaveRengokuData_SentinelRejection(t *testing.T) {
	server := createMockServer()
	charMock := newMockCharacterRepo()

	// Set up existing data with non-zero sentinel
	existing := make([]byte, 100)
	existing[0] = 0x00
	existing[1] = 0x00
	existing[2] = 0x00
	existing[3] = 0x01
	existing[rengokuSkillSlotsStart] = 1
	charMock.columns["rengokudata"] = existing

	server.charRepo = charMock
	server.rengokuRepo = &mockRengokuRepo{}
	session := createMockSession(1, server)

	// Build payload with zero sentinel (should be rejected)
	payload := make([]byte, 100)
	// sentinel is 0 (all zeros)
	payload[rengokuSkillSlotsStart] = 1 // non-zeroed skills to skip merge path

	pkt := &mhfpacket.MsgMhfSaveRengokuData{
		AckHandle:      100,
		RawDataPayload: payload,
	}

	handleMsgMhfSaveRengokuData(session, pkt)

	// Existing data should be preserved (not overwritten)
	if charMock.columns["rengokudata"][3] != 0x01 {
		t.Error("Existing rengoku data should not be overwritten")
	}

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

func TestSaveRengokuData_SaveError(t *testing.T) {
	server := createMockServer()
	charMock := newMockCharacterRepo()
	charMock.saveErr = errNotFound
	server.charRepo = charMock
	server.rengokuRepo = &mockRengokuRepo{}
	session := createMockSession(1, server)

	payload := make([]byte, 100)
	payload[3] = 0x01
	payload[rengokuSkillSlotsStart] = 1

	pkt := &mhfpacket.MsgMhfSaveRengokuData{
		AckHandle:      100,
		RawDataPayload: payload,
	}

	handleMsgMhfSaveRengokuData(session, pkt)

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

// --- handleMsgMhfLoadRengokuData tests ---

func TestLoadRengokuData_WithData(t *testing.T) {
	server := createMockServer()
	charMock := newMockCharacterRepo()
	charMock.columns["rengokudata"] = []byte{0x01, 0x02, 0x03, 0x04}
	server.charRepo = charMock
	session := createMockSession(1, server)

	pkt := &mhfpacket.MsgMhfLoadRengokuData{AckHandle: 100}

	handleMsgMhfLoadRengokuData(session, pkt)

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

func TestLoadRengokuData_EmptyData(t *testing.T) {
	server := createMockServer()
	charMock := newMockCharacterRepo()
	// No rengokudata column set - returns nil/empty
	server.charRepo = charMock
	session := createMockSession(1, server)

	pkt := &mhfpacket.MsgMhfLoadRengokuData{AckHandle: 100}

	handleMsgMhfLoadRengokuData(session, pkt)

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

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

	pkt := &mhfpacket.MsgMhfLoadRengokuData{AckHandle: 100}

	handleMsgMhfLoadRengokuData(session, pkt)

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

// --- handleMsgMhfEnumerateRengokuRanking tests ---

func TestEnumerateRengokuRanking_NoGuild_Leaderboard2(t *testing.T) {
	server := createMockServer()
	guildMock := &mockGuildRepo{} // GetByCharID returns nil
	server.guildRepo = guildMock
	server.rengokuRepo = &mockRengokuRepo{}
	session := createMockSession(1, server)

	pkt := &mhfpacket.MsgMhfEnumerateRengokuRanking{
		AckHandle:   100,
		Leaderboard: 2, // guild leaderboard, requires guild
	}

	handleMsgMhfEnumerateRengokuRanking(session, pkt)

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

func TestEnumerateRengokuRanking_NoGuild_Leaderboard3(t *testing.T) {
	server := createMockServer()
	guildMock := &mockGuildRepo{}
	server.guildRepo = guildMock
	server.rengokuRepo = &mockRengokuRepo{}
	session := createMockSession(1, server)

	pkt := &mhfpacket.MsgMhfEnumerateRengokuRanking{
		AckHandle:   100,
		Leaderboard: 3,
	}

	handleMsgMhfEnumerateRengokuRanking(session, pkt)

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

func TestEnumerateRengokuRanking_WithGuild(t *testing.T) {
	server := createMockServer()
	guild := &Guild{ID: 10, Name: "TestGuild"}
	guildMock := &mockGuildRepo{}
	guildMock.guild = guild
	server.guildRepo = guildMock
	server.rengokuRepo = &mockRengokuRepo{
		ranking: []RengokuScore{
			{Name: "Player1", Score: 100},
			{Name: "Player2", Score: 50},
		},
	}
	session := createMockSession(1, server)
	session.Name = "Player1"

	pkt := &mhfpacket.MsgMhfEnumerateRengokuRanking{
		AckHandle:   100,
		Leaderboard: 2,
	}

	handleMsgMhfEnumerateRengokuRanking(session, pkt)

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

func TestEnumerateRengokuRanking_SoloLeaderboard(t *testing.T) {
	server := createMockServer()
	guildMock := &mockGuildRepo{}
	server.guildRepo = guildMock
	server.rengokuRepo = &mockRengokuRepo{
		ranking: []RengokuScore{
			{Name: "Player1", Score: 200},
		},
	}
	session := createMockSession(1, server)

	pkt := &mhfpacket.MsgMhfEnumerateRengokuRanking{
		AckHandle:   100,
		Leaderboard: 0, // solo, no guild required
	}

	handleMsgMhfEnumerateRengokuRanking(session, pkt)

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

func TestEnumerateRengokuRanking_QueryError(t *testing.T) {
	server := createMockServer()
	guildMock := &mockGuildRepo{}
	server.guildRepo = guildMock
	server.rengokuRepo = &mockRengokuRepo{rankingErr: errNotFound}
	session := createMockSession(1, server)

	pkt := &mhfpacket.MsgMhfEnumerateRengokuRanking{
		AckHandle:   100,
		Leaderboard: 0,
	}

	handleMsgMhfEnumerateRengokuRanking(session, pkt)

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

func TestEnumerateRengokuRanking_Applicant(t *testing.T) {
	server := createMockServer()
	guild := &Guild{ID: 10, Name: "TestGuild"}
	guildMock := &mockGuildRepo{hasAppResult: true}
	guildMock.guild = guild
	server.guildRepo = guildMock
	server.rengokuRepo = &mockRengokuRepo{}
	session := createMockSession(1, server)

	// Leaderboard 6 requires guild, but applicant should be treated as no guild
	pkt := &mhfpacket.MsgMhfEnumerateRengokuRanking{
		AckHandle:   100,
		Leaderboard: 6,
	}

	handleMsgMhfEnumerateRengokuRanking(session, pkt)

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

// --- handleMsgMhfGetRengokuBinary tests ---

func TestGetRengokuBinary_Cached(t *testing.T) {
	server := createMockServer()
	server.rengokuBin = []byte{0x65, 0x63, 0x64, 0x1a, 0xDE, 0xAD}
	session := createMockSession(1, server)

	pkt := &mhfpacket.MsgMhfGetRengokuBinary{AckHandle: 100}
	handleMsgMhfGetRengokuBinary(session, pkt)

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

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

	pkt := &mhfpacket.MsgMhfGetRengokuBinary{AckHandle: 100}
	handleMsgMhfGetRengokuBinary(session, pkt)

	select {
	case <-session.sendPackets:
		// fail ACK was sent — expected
	default:
		t.Error("No response packet queued (expected fail ACK)")
	}
}

// Tests consolidated from handlers_coverage3_test.go

func TestNonTrivialHandlers_RengokuGo(t *testing.T) {
	server := createMockServer()

	t.Run("handleMsgMhfGetRengokuRankingRank", func(t *testing.T) {
		session := createMockSession(1, server)
		handleMsgMhfGetRengokuRankingRank(session, &mhfpacket.MsgMhfGetRengokuRankingRank{AckHandle: 1})
		select {
		case p := <-session.sendPackets:
			if len(p.data) == 0 {
				t.Error("response should have data")
			}
		default:
			t.Error("no response queued")
		}
	})
}
