package xmlparser

import (
	"bytes"
	"fmt"
	"strings"
	"sync"
	"testing"
)

// Benchmark data of different sizes
var (
	smallXML = `<?xml version="1.0" encoding="UTF-8"?>
<MockElement id="small">
	<name>Small Test</name>
	<value>Small value</value>
</MockElement>`

	mediumXML = `<?xml version="1.0" encoding="UTF-8"?>
<MockElement id="medium">
	<name>Medium Test Document</name>
	<value>This is a medium-sized XML document for benchmarking purposes</value>
	<details>
		<description>Medium XML document with nested elements</description>
		<category>benchmark</category>
		<priority>medium</priority>
		<tags>
			<tag>xml</tag>
			<tag>benchmark</tag>
			<tag>test</tag>
		</tags>
	</details>
	<metadata>
		<created>2025-01-09</created>
		<author>Test Suite</author>
		<version>1.0</version>
	</metadata>
</MockElement>`

	// Generate large XML dynamically in benchmarks to avoid bloating the source
	largeXMLTemplate = `<?xml version="1.0" encoding="UTF-8"?>
<MockElement id="large">
	<name>Large Test Document</name>
	<value>This is a large XML document for benchmarking performance</value>
	<data>%s</data>
</MockElement>`
)

// generateLargeXML creates a large XML document for benchmarking
func generateLargeXML() string {
	var buffer strings.Builder

	// Generate repetitive data to create a large document
	for i := 0; i < 1000; i++ {
		buffer.WriteString(fmt.Sprintf(`
		<item id="item-%d">
			<title>Item %d Title</title>
			<description>This is item number %d with detailed description for benchmarking purposes. It contains enough text to make the document significantly larger.</description>
			<category>category-%d</category>
			<tags>
				<tag>tag1</tag>
				<tag>tag2</tag>
				<tag>tag3</tag>
			</tags>
		</item>`, i, i, i, i%10))
	}

	return fmt.Sprintf(largeXMLTemplate, buffer.String())
}

// BenchmarkParseString benchmarks parsing from string
func BenchmarkParseString(b *testing.B) {
	parser := NewParser[MockParsable](nil)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err := parser.ParseString(smallXML)
		if err != nil {
			b.Errorf("Parse error: %v", err)
		}
	}
}

// BenchmarkParseBytes benchmarks parsing from byte slice
func BenchmarkParseBytes(b *testing.B) {
	parser := NewParser[MockParsable](nil)
	xmlData := []byte(smallXML)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err := parser.ParseBytes(xmlData)
		if err != nil {
			b.Errorf("Parse error: %v", err)
		}
	}
}

// BenchmarkParse benchmarks parsing from reader
func BenchmarkParse(b *testing.B) {
	parser := NewParser[MockParsable](nil)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		reader := strings.NewReader(smallXML)
		_, err := parser.Parse(reader)
		if err != nil {
			b.Errorf("Parse error: %v", err)
		}
	}
}

// BenchmarkParseSmallXML benchmarks parsing small XML documents
func BenchmarkParseSmallXML(b *testing.B) {
	parser := NewParser[MockParsable](nil)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err := parser.ParseString(smallXML)
		if err != nil {
			b.Errorf("Parse error: %v", err)
		}
	}
}

// BenchmarkParseMediumXML benchmarks parsing medium XML documents
func BenchmarkParseMediumXML(b *testing.B) {
	parser := NewParser[MockParsable](nil)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err := parser.ParseString(mediumXML)
		if err != nil {
			b.Errorf("Parse error: %v", err)
		}
	}
}

// BenchmarkParseLargeXML benchmarks parsing large XML documents
func BenchmarkParseLargeXML(b *testing.B) {
	parser := NewParser[MockParsable](nil)
	largeXML := generateLargeXML()

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err := parser.ParseString(largeXML)
		if err != nil {
			b.Errorf("Parse error: %v", err)
		}
	}
}

// BenchmarkParseWithResult benchmarks parsing with detailed results
func BenchmarkParseWithResult(b *testing.B) {
	parser := NewParser[MockParsable](nil)
	genericParser := parser.(*GenericParser[MockParsable])

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		reader := strings.NewReader(mediumXML)
		_, err := genericParser.ParseWithResult(reader)
		if err != nil {
			b.Errorf("Parse error: %v", err)
		}
	}
}

// BenchmarkValidation benchmarks XML validation
func BenchmarkValidation(b *testing.B) {
	parser := NewParser[MockParsable](nil)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		reader := strings.NewReader(mediumXML)
		err := parser.Validate(reader)
		if err != nil {
			b.Errorf("Validation error: %v", err)
		}
	}
}

// BenchmarkConcurrentParsing benchmarks concurrent parsing performance
func BenchmarkConcurrentParsing(b *testing.B) {
	parser := NewParser[MockParsable](nil)

	b.ResetTimer()
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			_, err := parser.ParseString(smallXML)
			if err != nil {
				b.Errorf("Parse error: %v", err)
			}
		}
	})
}

// BenchmarkConcurrentParsingWithWaitGroup benchmarks coordinated concurrent parsing
func BenchmarkConcurrentParsingWithWaitGroup(b *testing.B) {
	parser := NewParser[MockParsable](nil)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		var wg sync.WaitGroup
		numGoroutines := 4

		for j := 0; j < numGoroutines; j++ {
			wg.Add(1)
			go func() {
				defer wg.Done()
				_, err := parser.ParseString(smallXML)
				if err != nil {
					b.Errorf("Parse error: %v", err)
				}
			}()
		}

		wg.Wait()
	}
}

// BenchmarkMemoryAllocation benchmarks memory allocation patterns
func BenchmarkMemoryAllocation(b *testing.B) {
	parser := NewParser[MockParsable](nil)

	b.ReportAllocs()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		result, err := parser.ParseString(mediumXML)
		if err != nil {
			b.Errorf("Parse error: %v", err)
		}
		// Use result to prevent optimization
		_ = result.GetID()
	}
}

// BenchmarkMemoryAllocationsLarge benchmarks memory allocations for large documents
func BenchmarkMemoryAllocationsLarge(b *testing.B) {
	parser := NewParser[MockParsable](nil)
	largeXML := generateLargeXML()

	b.ReportAllocs()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		result, err := parser.ParseString(largeXML)
		if err != nil {
			b.Errorf("Parse error: %v", err)
		}
		// Use result to prevent optimization
		_ = result.GetID()
	}
}

// BenchmarkSanitizeXMLString benchmarks XML string sanitization
func BenchmarkSanitizeXMLString(b *testing.B) {
	input := "Test string with\x00invalid\x01characters\x02and normal text"

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		result := SanitizeXMLString(input)
		// Use result to prevent optimization
		_ = len(result)
	}
}

// BenchmarkValidateXMLName benchmarks XML name validation
func BenchmarkValidateXMLName(b *testing.B) {
	testNames := []string{
		"ValidElement",
		"invalid-element-123",
		"_privateElement",
		"123Invalid",
		"element-with-hyphen",
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		name := testNames[i%len(testNames)]
		result := ValidateXMLName(name)
		// Use result to prevent optimization
		_ = result
	}
}

// BenchmarkIsValidNamespace benchmarks namespace validation
func BenchmarkIsValidNamespace(b *testing.B) {
	testNamespaces := []string{
		"http://example.com",
		"https://ted.europa.eu/schema",
		"urn:oasis:names:specification:ubl",
		"invalid-namespace",
		"",
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		namespace := testNamespaces[i%len(testNamespaces)]
		result := IsValidNamespace(namespace)
		// Use result to prevent optimization
		_ = result
	}
}

// BenchmarkNormalizeWhitespace benchmarks whitespace normalization
func BenchmarkNormalizeWhitespace(b *testing.B) {
	input := "  Multiple   \t\n  whitespace\r\n   characters    "

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		result := NormalizeWhitespace(input)
		// Use result to prevent optimization
		_ = len(result)
	}
}

// BenchmarkTruncateString benchmarks string truncation
func BenchmarkTruncateString(b *testing.B) {
	input := "This is a very long string that needs to be truncated for display purposes"
	maxLength := 50

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		result := TruncateString(input, maxLength)
		// Use result to prevent optimization
		_ = len(result)
	}
}

// BenchmarkParserCreation benchmarks parser creation
func BenchmarkParserCreation(b *testing.B) {
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		parser := NewParser[MockParsable](nil)
		// Use parser to prevent optimization
		_ = parser
	}
}

// BenchmarkParserCreationWithOptions benchmarks parser creation with custom options
func BenchmarkParserCreationWithOptions(b *testing.B) {
	options := &ParserOptions{
		StrictValidation:      false,
		AllowEmptyElements:    true,
		PreserveWhitespace:    false,
		IgnoreUnknownElements: true,
		MaxDepth:              100,
		MaxSize:               10 * 1024 * 1024,
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		parser := NewParser[MockParsable](options)
		// Use parser to prevent optimization
		_ = parser
	}
}

// BenchmarkValidationError benchmarks validation error creation
func BenchmarkValidationError(b *testing.B) {
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		err := ValidationError{
			Field:   "testField",
			Message: "test error message",
			Value:   "test value",
		}
		// Use error to prevent optimization
		_ = err.Error()
	}
}

// BenchmarkValidationErrors benchmarks multiple validation errors
func BenchmarkValidationErrors(b *testing.B) {
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		var errors ValidationErrors
		errors.Add("field1", "error1", "value1")
		errors.Add("field2", "error2", "value2")
		errors.Add("field3", "error3", "value3")

		// Use errors to prevent optimization
		_ = errors.Error()
	}
}

// BenchmarkMockValidation benchmarks mock object validation
func BenchmarkMockValidation(b *testing.B) {
	mock := MockParsable{
		ID:    "test-123",
		Name:  "Test Mock",
		Value: "test value",
		Valid: true,
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		err := mock.Validate()
		// Use error to prevent optimization
		_ = err
	}
}

// BenchmarkDifferentXMLSizes compares performance across different document sizes
func BenchmarkDifferentXMLSizes(b *testing.B) {
	parser := NewParser[MockParsable](nil)

	testCases := []struct {
		name string
		xml  string
	}{
		{"Small", smallXML},
		{"Medium", mediumXML},
	}

	// Add large XML dynamically to avoid memory overhead during compilation
	largeXML := generateLargeXML()
	testCases = append(testCases, struct {
		name string
		xml  string
	}{"Large", largeXML})

	for _, tc := range testCases {
		b.Run(tc.name, func(b *testing.B) {
			b.ResetTimer()
			for i := 0; i < b.N; i++ {
				_, err := parser.ParseString(tc.xml)
				if err != nil {
					b.Errorf("Parse error: %v", err)
				}
			}
		})
	}
}

// BenchmarkParsingMethods compares different parsing methods
func BenchmarkParsingMethods(b *testing.B) {
	parser := NewParser[MockParsable](nil)
	xmlData := []byte(mediumXML)

	b.Run("ParseString", func(b *testing.B) {
		b.ResetTimer()
		for i := 0; i < b.N; i++ {
			_, err := parser.ParseString(mediumXML)
			if err != nil {
				b.Errorf("Parse error: %v", err)
			}
		}
	})

	b.Run("ParseBytes", func(b *testing.B) {
		b.ResetTimer()
		for i := 0; i < b.N; i++ {
			_, err := parser.ParseBytes(xmlData)
			if err != nil {
				b.Errorf("Parse error: %v", err)
			}
		}
	})

	b.Run("Parse", func(b *testing.B) {
		b.ResetTimer()
		for i := 0; i < b.N; i++ {
			reader := bytes.NewReader(xmlData)
			_, err := parser.Parse(reader)
			if err != nil {
				b.Errorf("Parse error: %v", err)
			}
		}
	})
}

// BenchmarkUtilityFunctions benchmarks all utility functions together
func BenchmarkUtilityFunctions(b *testing.B) {
	testData := struct {
		dirtyString string
		xmlName     string
		namespace   string
		whitespace  string
		longString  string
	}{
		dirtyString: "Dirty\x00string\x01with\x02control\x03characters",
		xmlName:     "ValidElementName",
		namespace:   "http://example.com/namespace",
		whitespace:  "  Multiple   \t\n  spaces  ",
		longString:  "This is a very long string that needs truncation for testing purposes",
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		// Run all utility functions
		_ = SanitizeXMLString(testData.dirtyString)
		_ = ValidateXMLName(testData.xmlName)
		_ = IsValidNamespace(testData.namespace)
		_ = NormalizeWhitespace(testData.whitespace)
		_ = TruncateString(testData.longString, 50)
	}
}

// BenchmarkParserOptionsEffects benchmarks parsing with different options
func BenchmarkParserOptionsEffects(b *testing.B) {
	testCases := []struct {
		name    string
		options *ParserOptions
	}{
		{
			name:    "Default",
			options: nil,
		},
		{
			name: "StrictDisabled",
			options: &ParserOptions{
				StrictValidation: false,
			},
		},
		{
			name: "PreserveWhitespace",
			options: &ParserOptions{
				PreserveWhitespace: true,
			},
		},
		{
			name: "IgnoreUnknown",
			options: &ParserOptions{
				IgnoreUnknownElements: true,
			},
		},
	}

	for _, tc := range testCases {
		b.Run(tc.name, func(b *testing.B) {
			parser := NewParser[MockParsable](tc.options)
			b.ResetTimer()

			for i := 0; i < b.N; i++ {
				_, err := parser.ParseString(mediumXML)
				if err != nil {
					b.Errorf("Parse error: %v", err)
				}
			}
		})
	}
}

// BenchmarkErrorHandling benchmarks error creation and handling
func BenchmarkErrorHandling(b *testing.B) {
	b.Run("ValidationError", func(b *testing.B) {
		b.ResetTimer()
		for i := 0; i < b.N; i++ {
			err := ValidationError{
				Field:   "testField",
				Message: "test message",
				Value:   "test value",
			}
			_ = err.Error()
		}
	})

	b.Run("XMLParseError", func(b *testing.B) {
		b.ResetTimer()
		for i := 0; i < b.N; i++ {
			err := XMLParseError{
				Line:    10,
				Column:  5,
				Element: "TestElement",
				Message: "test parse error",
			}
			_ = err.Error()
		}
	})

	b.Run("ValidationErrors", func(b *testing.B) {
		b.ResetTimer()
		for i := 0; i < b.N; i++ {
			var errors ValidationErrors
			errors.Add("field1", "message1", "value1")
			errors.Add("field2", "message2", "value2")
			_ = errors.Error()
		}
	})
}

// BenchmarkMemoryEfficiency measures memory efficiency across operations
func BenchmarkMemoryEfficiency(b *testing.B) {
	parser := NewParser[MockParsable](nil)

	b.Run("SmallDocuments", func(b *testing.B) {
		b.ReportAllocs()
		b.ResetTimer()

		for i := 0; i < b.N; i++ {
			result, err := parser.ParseString(smallXML)
			if err != nil {
				b.Errorf("Parse error: %v", err)
			}
			// Ensure result is used
			_ = result.GetID()
		}
	})

	b.Run("MediumDocuments", func(b *testing.B) {
		b.ReportAllocs()
		b.ResetTimer()

		for i := 0; i < b.N; i++ {
			result, err := parser.ParseString(mediumXML)
			if err != nil {
				b.Errorf("Parse error: %v", err)
			}
			// Ensure result is used
			_ = result.GetID()
		}
	})
}
