package utils

import (
	"testing"

	"github.com/AFASystems/presence/internal/pkg/model"
)

func TestParseADFast(t *testing.T) {
	tests := []struct {
		name     string
		input    []byte
		expected [][2]int
	}{
		{
			name:     "Empty input",
			input:    []byte{},
			expected: [][2]int{},
		},
		{
			name:     "Single AD structure",
			input:    []byte{0x02, 0x01, 0x06},
			expected: [][2]int{{0, 2}},
		},
		{
			name:     "Multiple AD structures",
			input:    []byte{0x02, 0x01, 0x06, 0x03, 0x02, 0x01, 0x02},
			expected: [][2]int{{0, 2}, {3, 6}},
		},
		{
			name:     "Complex AD structures",
			input:    []byte{0x02, 0x01, 0x06, 0x1A, 0xFF, 0x4C, 0x00, 0x02, 0x15, 0xE2, 0xC5, 0x6D, 0xB5, 0xDF, 0xFB, 0x48, 0xD2, 0xB0, 0x60, 0xD0, 0xF5, 0xA7, 0x10, 0x96, 0xE0, 0x00, 0x00, 0x00, 0x00, 0xC5},
			expected: [][2]int{{0, 2}, {2, 28}},
		},
		{
			name:     "Zero length AD structure",
			input:    []byte{0x00, 0x01, 0x06, 0x03, 0x02, 0x01, 0x02},
			expected: [][2]int{{2, 5}},
		},
		{
			name:     "AD structure exceeding bounds",
			input:    []byte{0x05, 0x01, 0x06},
			expected: [][2]int{},
		},
		{
			name:     "Incomplete AD structure",
			input:    []byte{0x03, 0x01},
			expected: [][2]int{},
		},
		{
			name:     "Valid then invalid structure",
			input:    []byte{0x02, 0x01, 0x06, 0xFF, 0x01, 0x06},
			expected: [][2]int{{0, 2}},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := ParseADFast(tt.input)
			if len(result) != len(tt.expected) {
				t.Errorf("ParseADFast() length = %v, expected %v", len(result), len(tt.expected))
				return
			}

			for i, r := range result {
				if r[0] != tt.expected[i][0] || r[1] != tt.expected[i][1] {
					t.Errorf("ParseADFast()[%d] = %v, expected %v", i, r, tt.expected[i])
				}
			}
		})
	}
}

func TestRemoveFlagBytes(t *testing.T) {
	tests := []struct {
		name     string
		input    []byte
		expected []byte
	}{
		{
			name:     "Empty input",
			input:    []byte{},
			expected: []byte{},
		},
		{
			name:     "Single byte input",
			input:    []byte{0x01},
			expected: []byte{0x01},
		},
		{
			name:     "No flag bytes",
			input:    []byte{0x02, 0x01, 0x06, 0x03, 0x02, 0x01},
			expected: []byte{0x02, 0x01, 0x06, 0x03, 0x02, 0x01},
		},
		{
			name:     "With flag bytes",
			input:    []byte{0x02, 0x01, 0x06, 0x1A, 0xFF, 0x4C, 0x00, 0x02},
			expected: []byte{0x1A, 0xFF, 0x4C, 0x00, 0x02},
		},
		{
			name:     "Flag type is 0x01",
			input:    []byte{0x02, 0x01, 0x06, 0x05, 0x01, 0x02, 0x03, 0x04},
			expected: []byte{0x05, 0x01, 0x02, 0x03, 0x04},
		},
		{
			name:     "Flag type is not 0x01",
			input:    []byte{0x02, 0x02, 0x06, 0x05, 0x01, 0x02, 0x03, 0x04},
			expected: []byte{0x02, 0x02, 0x06, 0x05, 0x01, 0x02, 0x03, 0x04},
		},
		{
			name:     "Length exceeds bounds",
			input:    []byte{0xFF, 0x01, 0x06},
			expected: []byte{0xFF, 0x01, 0x06},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := RemoveFlagBytes(tt.input)
			if len(result) != len(tt.expected) {
				t.Errorf("RemoveFlagBytes() length = %v, expected %v", len(result), len(tt.expected))
				return
			}

			for i, b := range result {
				if b != tt.expected[i] {
					t.Errorf("RemoveFlagBytes()[%d] = %v, expected %v", i, b, tt.expected[i])
				}
			}
		})
	}
}

func TestIsValidADStructure(t *testing.T) {
	tests := []struct {
		name     string
		data     []byte
		expected bool
	}{
		{
			name:     "Empty data",
			data:     []byte{},
			expected: false,
		},
		{
			name:     "Single byte",
			data:     []byte{0x01},
			expected: false,
		},
		{
			name:     "Valid minimal structure",
			data:     []byte{0x01, 0x01},
			expected: true,
		},
		{
			name:     "Valid structure",
			data:     []byte{0x02, 0x01, 0x06},
			expected: true,
		},
		{
			name:     "Zero length",
			data:     []byte{0x00, 0x01, 0x06},
			expected: false,
		},
		{
			name:     "Length exceeds data",
			data:     []byte{0x05, 0x01, 0x06},
			expected: false,
		},
		{
			name:     "Length exactly matches",
			data:     []byte{0x02, 0x01, 0x06},
			expected: true,
		},
		{
			name:     "Large valid structure",
			data:     []byte{0x1F, 0xFF, 0x4C, 0x00, 0x02, 0x15, 0xE2, 0xC5, 0x6D, 0xB5, 0xDF, 0xFB, 0x48, 0xD2, 0xB0, 0x60, 0xD0, 0xF5, 0xA7, 0x10, 0x96, 0xE0, 0x00, 0x00, 0x00, 0x00, 0xC5, 0x01, 0x02, 0x03, 0x04, 0x05},
			expected: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := isValidADStructure(tt.data)
			if result != tt.expected {
				t.Errorf("isValidADStructure() = %v, expected %v", result, tt.expected)
			}
		})
	}
}

func TestCheckIngics(t *testing.T) {
	tests := []struct {
		name     string
		ad       []byte
		expected bool
	}{
		{
			name:     "Valid Ingics beacon",
			ad:       []byte{0x08, 0xFF, 0x59, 0x00, 0x80, 0xBC, 0x12, 0x34, 0x01},
			expected: true,
		},
		{
			name:     "Invalid - too short",
			ad:       []byte{0x05, 0xFF, 0x59, 0x00},
			expected: false,
		},
		{
			name:     "Invalid - wrong manufacturer ID",
			ad:       []byte{0x08, 0xFF, 0x59, 0x01, 0x80, 0xBC, 0x12, 0x34, 0x01},
			expected: false,
		},
		{
			name:     "Invalid - wrong type",
			ad:       []byte{0x08, 0xFE, 0x59, 0x00, 0x80, 0xBC, 0x12, 0x34, 0x01},
			expected: false,
		},
		{
			name:     "Valid with minimum length",
			ad:       []byte{0x06, 0xFF, 0x59, 0x00, 0x80, 0xBC},
			expected: true,
		},
		{
			name:     "Empty data",
			ad:       []byte{},
			expected: false,
		},
		{
			name:     "Partial match only",
			ad:       []byte{0x06, 0xFF, 0x59, 0x00, 0x80},
			expected: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := checkIngics(tt.ad)
			if result != tt.expected {
				t.Errorf("checkIngics() = %v, expected %v", result, tt.expected)
			}
		})
	}
}

func TestParseIngicsState(t *testing.T) {
	tests := []struct {
		name     string
		ad       []byte
		expected model.BeaconEvent
	}{
		{
			name: "Valid Ingics data",
			ad:   []byte{0x08, 0xFF, 0x59, 0x00, 0x80, 0xBC, 0x34, 0x12, 0x05},
			expected: model.BeaconEvent{
				Battery: 0x1234, // 4660 in little endian
				Event:   0x05,
				Type:    "Ingics",
			},
		},
		{
			name: "Zero battery",
			ad:   []byte{0x08, 0xFF, 0x59, 0x00, 0x80, 0xBC, 0x00, 0x00, 0x00},
			expected: model.BeaconEvent{
				Battery: 0,
				Event:   0,
				Type:    "Ingics",
			},
		},
		{
			name: "Max battery value",
			ad:   []byte{0x08, 0xFF, 0x59, 0x00, 0x80, 0xBC, 0xFF, 0xFF, 0xFF},
			expected: model.BeaconEvent{
				Battery: 0xFFFF,
				Event:   0xFF,
				Type:    "Ingics",
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := parseIngicsState(tt.ad)
			if result.Battery != tt.expected.Battery {
				t.Errorf("parseIngicsState() Battery = %v, expected %v", result.Battery, tt.expected.Battery)
			}
			if result.Event != tt.expected.Event {
				t.Errorf("parseIngicsState() Event = %v, expected %v", result.Event, tt.expected.Event)
			}
			if result.Type != tt.expected.Type {
				t.Errorf("parseIngicsState() Type = %v, expected %v", result.Type, tt.expected.Type)
			}
		})
	}
}

func TestCheckEddystoneTLM(t *testing.T) {
	tests := []struct {
		name     string
		ad       []byte
		expected bool
	}{
		{
			name:     "Valid Eddystone TLM",
			ad:       []byte{0x12, 0x16, 0xAA, 0xFE, 0x20, 0x00, 0x01, 0x02, 0x03, 0x04},
			expected: true,
		},
		{
			name:     "Invalid - too short",
			ad:       []byte{0x03, 0x16, 0xAA},
			expected: false,
		},
		{
			name:     "Invalid - wrong type",
			ad:       []byte{0x12, 0x15, 0xAA, 0xFE, 0x20, 0x00, 0x01, 0x02, 0x03, 0x04},
			expected: false,
		},
		{
			name:     "Invalid - wrong company ID",
			ad:       []byte{0x12, 0x16, 0xAA, 0xFF, 0x20, 0x00, 0x01, 0x02, 0x03, 0x04},
			expected: false,
		},
		{
			name:     "Invalid - wrong TLM type",
			ad:       []byte{0x12, 0x16, 0xAA, 0xFE, 0x21, 0x00, 0x01, 0x02, 0x03, 0x04},
			expected: false,
		},
		{
			name:     "Valid with minimum length",
			ad:       []byte{0x04, 0x16, 0xAA, 0xFE, 0x20},
			expected: true,
		},
		{
			name:     "Empty data",
			ad:       []byte{},
			expected: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := checkEddystoneTLM(tt.ad)
			if result != tt.expected {
				t.Errorf("checkEddystoneTLM() = %v, expected %v", result, tt.expected)
			}
		})
	}
}

func TestParseEddystoneState(t *testing.T) {
	tests := []struct {
		name     string
		ad       []byte
		expected model.BeaconEvent
	}{
		{
			name: "Valid Eddystone TLM data",
			ad:   []byte{0x12, 0x16, 0xAA, 0xFE, 0x20, 0x34, 0x12, 0x78, 0x56, 0x00},
			expected: model.BeaconEvent{
				Battery: 0x1234, // 4660 in big endian (note: different from Ingics)
				Type:    "Eddystone",
			},
		},
		{
			name: "Zero battery",
			ad:   []byte{0x12, 0x16, 0xAA, 0xFE, 0x20, 0x00, 0x00, 0x78, 0x56, 0x00},
			expected: model.BeaconEvent{
				Battery: 0,
				Type:    "Eddystone",
			},
		},
		{
			name: "Max battery value",
			ad:   []byte{0x12, 0x16, 0xAA, 0xFE, 0x20, 0xFF, 0xFF, 0x78, 0x56, 0x00},
			expected: model.BeaconEvent{
				Battery: 0xFFFF,
				Type:    "Eddystone",
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := parseEddystoneState(tt.ad)
			if result.Battery != tt.expected.Battery {
				t.Errorf("parseEddystoneState() Battery = %v, expected %v", result.Battery, tt.expected.Battery)
			}
			if result.Type != tt.expected.Type {
				t.Errorf("parseEddystoneState() Type = %v, expected %v", result.Type, tt.expected.Type)
			}
		})
	}
}

func TestCheckMinewB7(t *testing.T) {
	tests := []struct {
		name     string
		ad       []byte
		expected bool
	}{
		{
			name:     "Valid Minew B7",
			ad:       []byte{0x08, 0x16, 0xE1, 0xFF, 0x01, 0x02, 0x03, 0x04},
			expected: true,
		},
		{
			name:     "Invalid - too short",
			ad:       []byte{0x03, 0x16, 0xE1},
			expected: false,
		},
		{
			name:     "Invalid - wrong type",
			ad:       []byte{0x08, 0x15, 0xE1, 0xFF, 0x01, 0x02, 0x03, 0x04},
			expected: false,
		},
		{
			name:     "Invalid - wrong company ID",
			ad:       []byte{0x08, 0x16, 0xE1, 0xFE, 0x01, 0x02, 0x03, 0x04},
			expected: false,
		},
		{
			name:     "Valid with minimum length",
			ad:       []byte{0x04, 0x16, 0xE1, 0xFF},
			expected: true,
		},
		{
			name:     "Empty data",
			ad:       []byte{},
			expected: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := checkMinewB7(tt.ad)
			if result != tt.expected {
				t.Errorf("checkMinewB7() = %v, expected %v", result, tt.expected)
			}
		})
	}
}

func TestLoopADStructures(t *testing.T) {
	tests := []struct {
		name     string
		data     []byte
		ranges   [][2]int
		id       string
		expected model.BeaconEvent
	}{
		{
			name: "Ingics beacon found",
			data: []byte{0x08, 0xFF, 0x59, 0x00, 0x80, 0xBC, 0x34, 0x12, 0x05, 0x02, 0x01, 0x06},
			ranges: [][2]int{{0, 8}, {8, 11}},
			id:     "test-beacon",
			expected: model.BeaconEvent{
				ID:      "test-beacon",
				Name:    "test-beacon",
				Battery: 0x1234,
				Event:   0x05,
				Type:    "Ingics",
			},
		},
		{
			name: "Eddystone beacon found",
			data: []byte{0x02, 0x01, 0x06, 0x12, 0x16, 0xAA, 0xFE, 0x20, 0x34, 0x12, 0x78, 0x56},
			ranges: [][2]int{{0, 2}, {2, 14}},
			id:     "eddystone-test",
			expected: model.BeaconEvent{
				ID:      "eddystone-test",
				Name:    "eddystone-test",
				Battery: 0x1234,
				Type:    "Eddystone",
			},
		},
		{
			name: "Minew B7 beacon found",
			data: []byte{0x08, 0x16, 0xE1, 0xFF, 0x01, 0x02, 0x03, 0x04, 0x02, 0x01, 0x06},
			ranges: [][2]int{{0, 8}, {8, 11}},
			id:     "minew-test",
			expected: model.BeaconEvent{
				ID:      "minew-test",
				Name:    "minew-test",
				Type:    "", // Minew B7 returns empty BeaconEvent
			},
		},
		{
			name:     "No matching beacon type",
			data:     []byte{0x02, 0x01, 0x06, 0x03, 0x02, 0x01, 0x02},
			ranges:   [][2]int{{0, 2}, {2, 5}},
			id:       "unknown-test",
			expected: model.BeaconEvent{},
		},
		{
			name:     "Invalid AD structure",
			data:     []byte{0x02, 0x01, 0x06, 0xFF, 0x01, 0x06},
			ranges:   [][2]int{{0, 2}, {2, 4}},
			id:       "invalid-test",
			expected: model.BeaconEvent{},
		},
		{
			name:   "Empty data",
			data:   []byte{},
			ranges: [][2]int{},
			id:     "empty-test",
			expected: model.BeaconEvent{
				ID:   "empty-test",
				Name: "empty-test",
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := LoopADStructures(tt.data, tt.ranges, tt.id)

			if result.ID != tt.expected.ID {
				t.Errorf("LoopADStructures() ID = %v, expected %v", result.ID, tt.expected.ID)
			}
			if result.Name != tt.expected.Name {
				t.Errorf("LoopADStructures() Name = %v, expected %v", result.Name, tt.expected.Name)
			}
			if result.Type != tt.expected.Type {
				t.Errorf("LoopADStructures() Type = %v, expected %v", result.Type, tt.expected.Type)
			}
			if result.Battery != tt.expected.Battery {
				t.Errorf("LoopADStructures() Battery = %v, expected %v", result.Battery, tt.expected.Battery)
			}
		})
	}
}

func TestLoopADStructuresPriority(t *testing.T) {
	// Test that Ingics is checked first
	data := []byte{0x08, 0xFF, 0x59, 0x00, 0x80, 0xBC, 0x34, 0x12, 0x05, 0x12, 0x16, 0xAA, 0xFE, 0x20, 0x78, 0x56}
	ranges := [][2]int{{0, 8}, {8, 15}}

	result := LoopADStructures(data, ranges, "priority-test")

	// Should detect Ingics first, not Eddystone
	if result.Type != "Ingics" {
		t.Errorf("LoopADStructures() Type = %v, expected Ingics (priority test)", result.Type)
	}
}

// Benchmark tests
func BenchmarkParseADFast(b *testing.B) {
	data := []byte{0x02, 0x01, 0x06, 0x1A, 0xFF, 0x4C, 0x00, 0x02, 0x15, 0xE2, 0xC5, 0x6D, 0xB5, 0xDF, 0xFB, 0x48, 0xD2, 0xB0, 0x60, 0xD0, 0xF5, 0xA7, 0x10, 0x96, 0xE0, 0x00, 0x00, 0x00, 0x00, 0xC5}

	for i := 0; i < b.N; i++ {
		ParseADFast(data)
	}
}

func BenchmarkRemoveFlagBytes(b *testing.B) {
	data := []byte{0x02, 0x01, 0x06, 0x1A, 0xFF, 0x4C, 0x00, 0x02, 0x15, 0xE2, 0xC5}

	for i := 0; i < b.N; i++ {
		RemoveFlagBytes(data)
	}
}