package entranceserver

import (
	"fmt"
	"strings"
	"testing"

	"go.uber.org/zap"

	cfg "erupe-ce/config"
)

// TestEncodeServerInfo_EmptyClanMemberLimits verifies the crash is FIXED when ClanMemberLimits is empty
// Previously panicked: runtime error: index out of range [-1]
// From erupe.log.1:659922
// After fix: Should handle empty array gracefully with default value (60)
func TestEncodeServerInfo_EmptyClanMemberLimits(t *testing.T) {
	config := &cfg.Config{
		RealClientMode: cfg.Z1,
		Host:           "127.0.0.1",
		Entrance: cfg.Entrance{
			Enabled: true,
			Port:    53310,
			Entries: []cfg.EntranceServerInfo{
				{
					Name:               "TestServer",
					Description:        "Test",
					IP:                 "127.0.0.1",
					Type:               0,
					Recommended:        0,
					AllowedClientFlags: 0xFFFFFFFF,
					Channels: []cfg.EntranceChannelInfo{
						{
							Port:       54001,
							MaxPlayers: 100,
						},
					},
				},
			},
		},
		GameplayOptions: cfg.GameplayOptions{
			ClanMemberLimits: [][]uint8{}, // Empty array - should now use default (60) instead of panicking
		},
	}

	server := &Server{
		logger:      zap.NewNop(),
		erupeConfig: config,
	}

	// Set up defer to catch ANY panic - we should NOT get array bounds panic anymore
	defer func() {
		if r := recover(); r != nil {
			// If panic occurs, it should NOT be from array access
			panicStr := fmt.Sprintf("%v", r)
			if strings.Contains(panicStr, "index out of range") {
				t.Errorf("Array bounds panic NOT fixed! Still getting: %v", r)
			} else {
				// Other panic is acceptable (network, DB, etc) - we only care about array bounds
				t.Logf("Non-array-bounds panic (acceptable): %v", r)
			}
		}
	}()

	// This should NOT panic on array bounds anymore - should use default value 60
	result := encodeServerInfo(config, server, true)
	if len(result) > 0 {
		t.Log("✅ encodeServerInfo handled empty ClanMemberLimits without array bounds panic")
	}
}

// TestClanMemberLimitsBoundsChecking verifies bounds checking logic for ClanMemberLimits
// Tests the specific logic that was fixed without needing full database setup
func TestClanMemberLimitsBoundsChecking(t *testing.T) {
	// Test the bounds checking logic directly
	testCases := []struct {
		name             string
		clanMemberLimits [][]uint8
		expectedValue    uint8
		expectDefault    bool
	}{
		{"empty array", [][]uint8{}, 60, true},
		{"single row with 2 columns", [][]uint8{{1, 50}}, 50, false},
		{"single row with 1 column", [][]uint8{{1}}, 60, true},
		{"multiple rows, last has 2 columns", [][]uint8{{1, 10}, {2, 20}, {3, 60}}, 60, false},
		{"multiple rows, last has 1 column", [][]uint8{{1, 10}, {2, 20}, {3}}, 60, true},
		{"multiple rows with valid data", [][]uint8{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}}, 50, false},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			// Replicate the bounds checking logic from the fix
			var maxClanMembers uint8 = 60
			if len(tc.clanMemberLimits) > 0 {
				lastRow := tc.clanMemberLimits[len(tc.clanMemberLimits)-1]
				if len(lastRow) > 1 {
					maxClanMembers = lastRow[1]
				}
			}

			// Verify correct behavior
			if maxClanMembers != tc.expectedValue {
				t.Errorf("Expected value %d, got %d", tc.expectedValue, maxClanMembers)
			}

			if tc.expectDefault && maxClanMembers != 60 {
				t.Errorf("Expected default value 60, got %d", maxClanMembers)
			}

			t.Logf("✅ %s: Safe bounds access, value = %d", tc.name, maxClanMembers)
		})
	}
}

// TestEncodeServerInfo_WithMockRepo tests encodeServerInfo with a mock server repo
func TestEncodeServerInfo_WithMockRepo(t *testing.T) {
	config := &cfg.Config{
		RealClientMode: cfg.Z1,
		Host:           "127.0.0.1",
		Entrance: cfg.Entrance{
			Enabled: true,
			Port:    53310,
			Entries: []cfg.EntranceServerInfo{
				{
					Name:               "TestServer",
					Description:        "Test",
					IP:                 "127.0.0.1",
					Type:               0,
					Recommended:        0,
					AllowedClientFlags: 0xFFFFFFFF,
					Channels: []cfg.EntranceChannelInfo{
						{
							Port:       54001,
							MaxPlayers: 100,
						},
					},
				},
			},
		},
		GameplayOptions: cfg.GameplayOptions{
			ClanMemberLimits: [][]uint8{{1, 60}},
		},
	}

	server := &Server{
		logger:      zap.NewNop(),
		erupeConfig: config,
		serverRepo:  &mockEntranceServerRepo{currentPlayers: 42},
	}

	result := encodeServerInfo(config, server, true)
	if len(result) == 0 {
		t.Error("encodeServerInfo returned empty result")
	}
}

// TestMakeUsrResp_WithMockRepo tests makeUsrResp with a mock session repo
func TestMakeUsrResp_WithMockRepo(t *testing.T) {
	config := &cfg.Config{
		RealClientMode: cfg.Z1,
	}

	server := &Server{
		logger:      zap.NewNop(),
		erupeConfig: config,
		sessionRepo: &mockEntranceSessionRepo{serverID: 1234},
	}

	// Build a minimal USR request packet:
	// 4 bytes ALL+ prefix, 1 byte 0x00, 2 bytes entry count, then 4 bytes per entry (char ID)
	pkt := []byte{
		'A', 'L', 'L', '+',
		0x00,
		0x00, 0x01, // 1 entry
		0x00, 0x00, 0x00, 0x01, // char_id = 1
	}

	result := makeUsrResp(pkt, server)
	if len(result) == 0 {
		t.Error("makeUsrResp returned empty result")
	}
}

// TestMakeUsrResp_NilSessionRepo tests makeUsrResp when sessionRepo is nil
func TestMakeUsrResp_NilSessionRepo(t *testing.T) {
	config := &cfg.Config{
		RealClientMode: cfg.Z1,
	}

	server := &Server{
		logger:      zap.NewNop(),
		erupeConfig: config,
	}

	pkt := []byte{
		'A', 'L', 'L', '+',
		0x00,
		0x00, 0x01,
		0x00, 0x00, 0x00, 0x01,
	}

	result := makeUsrResp(pkt, server)
	if len(result) == 0 {
		t.Error("makeUsrResp returned empty result")
	}
}

// TestEncodeServerInfo_MissingSecondColumnClanMemberLimits tests accessing [last][1] when [last] is too small
// Previously panicked: runtime error: index out of range [1]
// After fix: Should handle missing column gracefully with default value (60)
func TestEncodeServerInfo_MissingSecondColumnClanMemberLimits(t *testing.T) {
	config := &cfg.Config{
		RealClientMode: cfg.Z1,
		Host:           "127.0.0.1",
		Entrance: cfg.Entrance{
			Enabled: true,
			Port:    53310,
			Entries: []cfg.EntranceServerInfo{
				{
					Name:               "TestServer",
					Description:        "Test",
					IP:                 "127.0.0.1",
					Type:               0,
					Recommended:        0,
					AllowedClientFlags: 0xFFFFFFFF,
					Channels: []cfg.EntranceChannelInfo{
						{
							Port:       54001,
							MaxPlayers: 100,
						},
					},
				},
			},
		},
		GameplayOptions: cfg.GameplayOptions{
			ClanMemberLimits: [][]uint8{
				{1}, // Only 1 element, code used to panic accessing [1]
			},
		},
	}

	server := &Server{
		logger:      zap.NewNop(),
		erupeConfig: config,
	}

	defer func() {
		if r := recover(); r != nil {
			panicStr := fmt.Sprintf("%v", r)
			if strings.Contains(panicStr, "index out of range") {
				t.Errorf("Array bounds panic NOT fixed! Still getting: %v", r)
			} else {
				t.Logf("Non-array-bounds panic (acceptable): %v", r)
			}
		}
	}()

	// This should NOT panic on array bounds anymore - should use default value 60
	result := encodeServerInfo(config, server, true)
	if len(result) > 0 {
		t.Log("✅ encodeServerInfo handled missing ClanMemberLimits column without array bounds panic")
	}
}
