package util

import (
	"math"
	"strconv"
	"testing"
)

func TestContainsInt(t *testing.T) {
	tests := []struct {
		name     string
		slice    []int
		value    int
		expected bool
	}{
		{
			name:     "value exists in slice",
			slice:    []int{1, 2, 3, 4, 5},
			value:    3,
			expected: true,
		},
		{
			name:     "value does not exist in slice",
			slice:    []int{1, 2, 3, 4, 5},
			value:    6,
			expected: false,
		},
		{
			name:     "empty slice",
			slice:    []int{},
			value:    1,
			expected: false,
		},
		{
			name:     "single element slice - match",
			slice:    []int{42},
			value:    42,
			expected: true,
		},
		{
			name:     "single element slice - no match",
			slice:    []int{42},
			value:    43,
			expected: false,
		},
		{
			name:     "duplicate values in slice",
			slice:    []int{1, 2, 2, 3, 2},
			value:    2,
			expected: true,
		},
		{
			name:     "negative values",
			slice:    []int{-5, -3, -1, 0, 1},
			value:    -3,
			expected: true,
		},
		{
			name:     "zero value",
			slice:    []int{-1, 0, 1},
			value:    0,
			expected: true,
		},
	}

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

func TestParseHex(t *testing.T) {
	tests := []struct {
		name          string
		hash          string
		startPosition int
		octets        int
		expected      int
		expectError   bool
		errorType     string
	}{
		// Valid cases
		{
			name:          "simple hex parsing",
			hash:          "abc123def456",
			startPosition: 0,
			octets:        2,
			expected:      0xab,
			expectError:   false,
		},
		{
			name:          "parse single character",
			hash:          "a1b2c3d4e5f6",
			startPosition: 1,
			octets:        1,
			expected:      0x1,
			expectError:   false,
		},
		{
			name:          "parse from middle",
			hash:          "123456789abc",
			startPosition: 6,
			octets:        3,
			expected:      0x789,
			expectError:   false,
		},
		{
			name:          "negative position (from end)",
			hash:          "abcdef123456",
			startPosition: -2,
			octets:        2,
			expected:      0x56,
			expectError:   false,
		},
		{
			name:          "octets 0 reads to end",
			hash:          "abc123",
			startPosition: 3,
			octets:        0,
			expected:      0x123,
			expectError:   false,
		},
		// Error cases
		{
			name:          "position out of bounds",
			hash:          "abc123",
			startPosition: 10,
			octets:        1,
			expectError:   true,
			errorType:     "position out of bounds",
		},
		{
			name:          "negative position too large",
			hash:          "abc123",
			startPosition: -10,
			octets:        1,
			expectError:   true,
			errorType:     "position out of bounds",
		},
		{
			name:          "invalid hex character",
			hash:          "abcghi",
			startPosition: 3,
			octets:        3,
			expectError:   true,
			errorType:     "invalid hex format",
		},
		// Platform-specific overflow tests
		{
			name:          "value at 32-bit int boundary (safe)",
			hash:          "7fffffff",
			startPosition: 0,
			octets:        8,
			expected:      math.MaxInt32,
			expectError:   false,
		},
	}

	// Add platform-specific overflow test that should fail on 32-bit systems
	if strconv.IntSize == 32 {
		tests = append(tests, struct {
			name          string
			hash          string
			startPosition int
			octets        int
			expected      int
			expectError   bool
			errorType     string
		}{
			name:          "overflow on 32-bit systems",
			hash:          "80000000", // This exceeds math.MaxInt32
			startPosition: 0,
			octets:        8,
			expectError:   true,
			errorType:     "value out of range",
		})
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result, err := ParseHex(tt.hash, tt.startPosition, tt.octets)

			if tt.expectError {
				if err == nil {
					t.Errorf("ParseHex(%q, %d, %d) expected error but got none",
						tt.hash, tt.startPosition, tt.octets)
					return
				}
				if tt.errorType != "" && !containsString(err.Error(), tt.errorType) {
					t.Errorf("ParseHex(%q, %d, %d) error %q does not contain expected type %q",
						tt.hash, tt.startPosition, tt.octets, err.Error(), tt.errorType)
				}
				return
			}

			if err != nil {
				t.Errorf("ParseHex(%q, %d, %d) unexpected error: %v",
					tt.hash, tt.startPosition, tt.octets, err)
				return
			}

			if result != tt.expected {
				t.Errorf("ParseHex(%q, %d, %d) = %d, expected %d",
					tt.hash, tt.startPosition, tt.octets, result, tt.expected)
			}
		})
	}
}

func TestValidateHash(t *testing.T) {
	tests := []struct {
		name        string
		hash        string
		expectError bool
		errorType   string
	}{
		{
			name:        "valid hash",
			hash:        "abc123def456789",
			expectError: false,
		},
		{
			name:        "minimum valid length",
			hash:        "abc123def45", // exactly 11 chars
			expectError: false,
		},
		{
			name:        "hash too short",
			hash:        "abc123def4", // 10 chars
			expectError: true,
			errorType:   "insufficient length",
		},
		{
			name:        "invalid character",
			hash:        "abc123gef456789",
			expectError: true,
			errorType:   "invalid character",
		},
		{
			name:        "uppercase hex is valid",
			hash:        "ABC123DEF456789",
			expectError: false,
		},
		{
			name:        "mixed case is valid",
			hash:        "AbC123dEf456789",
			expectError: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := ValidateHash(tt.hash)

			if tt.expectError {
				if err == nil {
					t.Errorf("ValidateHash(%q) expected error but got none", tt.hash)
					return
				}
				if tt.errorType != "" && !containsString(err.Error(), tt.errorType) {
					t.Errorf("ValidateHash(%q) error %q does not contain expected type %q",
						tt.hash, err.Error(), tt.errorType)
				}
				return
			}

			if err != nil {
				t.Errorf("ValidateHash(%q) unexpected error: %v", tt.hash, err)
			}
		})
	}
}

func TestIsValidHash(t *testing.T) {
	tests := []struct {
		name     string
		hash     string
		expected bool
	}{
		{
			name:     "valid hash returns true",
			hash:     "abc123def456789",
			expected: true,
		},
		{
			name:     "invalid hash returns false",
			hash:     "abc123g", // too short and invalid char
			expected: false,
		},
		{
			name:     "empty hash returns false",
			hash:     "",
			expected: false,
		},
	}

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

// TestParseHexDeterministic verifies that ParseHex produces consistent results
func TestParseHexDeterministic(t *testing.T) {
	testCases := []struct {
		hash string
		pos  int
		oct  int
	}{
		{"abc123def456", 0, 2},
		{"fedcba987654", 3, 4},
		{"123456789abc", 6, 3},
	}

	for _, tc := range testCases {
		t.Run("deterministic_"+tc.hash, func(t *testing.T) {
			// Parse the same input multiple times
			results := make([]int, 10)
			for i := 0; i < 10; i++ {
				result, err := ParseHex(tc.hash, tc.pos, tc.oct)
				if err != nil {
					t.Fatalf("ParseHex failed on iteration %d: %v", i, err)
				}
				results[i] = result
			}

			// Verify all results are identical
			first := results[0]
			for i, result := range results[1:] {
				if result != first {
					t.Errorf("ParseHex result not deterministic: iteration %d gave %d, expected %d",
						i+1, result, first)
				}
			}
		})
	}
}

// Helper function to check if a string contains a substring
func containsString(s, substr string) bool {
	return len(s) >= len(substr) &&
		(len(substr) == 0 ||
			func() bool {
				for i := 0; i <= len(s)-len(substr); i++ {
					if s[i:i+len(substr)] == substr {
						return true
					}
				}
				return false
			}())
}
