package xmlparser

import (
	"fmt"
	"strings"
)

// ValidationError represents a validation error with details
type ValidationError struct {
	Field   string `json:"field"`
	Message string `json:"message"`
	Value   string `json:"value,omitempty"`
}

func (ve ValidationError) Error() string {
	if ve.Value != "" {
		return fmt.Sprintf("validation error in field '%s': %s (value: %s)", ve.Field, ve.Message, ve.Value)
	}
	return fmt.Sprintf("validation error in field '%s': %s", ve.Field, ve.Message)
}

// ValidationErrors represents multiple validation errors
type ValidationErrors []ValidationError

func (ve ValidationErrors) Error() string {
	if len(ve) == 0 {
		return "no validation errors"
	}

	var messages []string
	for _, err := range ve {
		messages = append(messages, err.Error())
	}
	return fmt.Sprintf("validation failed with %d error(s): %s", len(ve), strings.Join(messages, "; "))
}

// IsEmpty returns true if there are no validation errors
func (ve ValidationErrors) IsEmpty() bool {
	return len(ve) == 0
}

// Add adds a validation error
func (ve *ValidationErrors) Add(field, message, value string) {
	*ve = append(*ve, ValidationError{
		Field:   field,
		Message: message,
		Value:   value,
	})
}

// XMLParseError represents an XML parsing error with context
type XMLParseError struct {
	Line     int    `json:"line,omitempty"`
	Column   int    `json:"column,omitempty"`
	Element  string `json:"element,omitempty"`
	Message  string `json:"message"`
	Original error  `json:"-"`
}

func (xpe XMLParseError) Error() string {
	var parts []string

	if xpe.Element != "" {
		parts = append(parts, fmt.Sprintf("element '%s'", xpe.Element))
	}

	if xpe.Line > 0 {
		if xpe.Column > 0 {
			parts = append(parts, fmt.Sprintf("line %d, column %d", xpe.Line, xpe.Column))
		} else {
			parts = append(parts, fmt.Sprintf("line %d", xpe.Line))
		}
	}

	context := ""
	if len(parts) > 0 {
		context = " (" + strings.Join(parts, ", ") + ")"
	}

	return fmt.Sprintf("XML parse error%s: %s", context, xpe.Message)
}

// Unwrap returns the original error
func (xpe XMLParseError) Unwrap() error {
	return xpe.Original
}

// SanitizeXMLString removes invalid XML characters and cleans the string
func SanitizeXMLString(s string) string {
	// Remove control characters except tab, newline, and carriage return
	var result strings.Builder
	for _, r := range s {
		if (r >= 0x20 && r <= 0xD7FF) ||
			(r >= 0xE000 && r <= 0xFFFD) ||
			(r >= 0x10000 && r <= 0x10FFFF) ||
			r == 0x09 || r == 0x0A || r == 0x0D {
			result.WriteRune(r)
		}
	}
	return result.String()
}

// ValidateXMLName checks if a string is a valid XML name
func ValidateXMLName(name string) bool {
	if name == "" {
		return false
	}

	// XML names must start with a letter or underscore
	first := []rune(name)[0]
	if !((first >= 'A' && first <= 'Z') ||
		(first >= 'a' && first <= 'z') ||
		first == '_') {
		return false
	}

	// Subsequent characters can be letters, digits, hyphens, periods, or underscores
	for _, r := range []rune(name)[1:] {
		if !((r >= 'A' && r <= 'Z') ||
			(r >= 'a' && r <= 'z') ||
			(r >= '0' && r <= '9') ||
			r == '-' || r == '.' || r == '_') {
			return false
		}
	}

	return true
}

// IsValidNamespace checks if a string is a valid XML namespace URI
func IsValidNamespace(namespace string) bool {
	if namespace == "" {
		return true // Empty namespace is valid
	}

	// Basic URI validation - should start with http:// or https:// or urn:
	return strings.HasPrefix(namespace, "http://") ||
		strings.HasPrefix(namespace, "https://") ||
		strings.HasPrefix(namespace, "urn:")
}

// NormalizeWhitespace normalizes whitespace in XML text content
func NormalizeWhitespace(s string) string {
	// Replace all whitespace sequences with single spaces
	fields := strings.Fields(s)
	return strings.Join(fields, " ")
}

// TruncateString truncates a string to maxLength and adds "..." if needed
func TruncateString(s string, maxLength int) string {
	if len(s) <= maxLength {
		return s
	}
	if maxLength <= 3 {
		return s[:maxLength]
	}
	return s[:maxLength-3] + "..."
}
