//! Property tests for Unicode edge cases that optimizations must handle correctly.
//!
//! These tests ensure optimizations don't break on:
//! - Multi-byte UTF-8 characters
//! - Grapheme clusters (emoji sequences, combining characters)
//! - Control characters
//! - Zero-width characters
//! - Surrogate pairs (invalid in UTF-8, but should handle gracefully)

use anno::{Entity, EntityType, Model, StackedNER};
use proptest::prelude::*;

proptest! {
    #![proptest_config(ProptestConfig::with_cases(100))]

    /// Property: Optimizations handle multi-byte UTF-8 characters correctly
    #[test]
    fn multi_byte_utf8_optimization_correctness(text in "[\\u{0080}-\\u{FFFF}]{0,100}") {
        let ner = StackedNER::default();
        let entities = ner.extract_entities(&text, None).unwrap();
        let text_char_count = text.chars().count();
        
        for entity in entities {
            // Use optimized methods
            let extracted = entity.extract_text_with_len(&text, text_char_count);
            let issues = entity.validate_with_len(&text, text_char_count);
            
            // Should not have span bounds issues
            for issue in &issues {
                match issue {
                    anno::ValidationIssue::SpanOutOfBounds { .. } |
                    anno::ValidationIssue::InvalidSpan { .. } => {
                        prop_assert!(
                            false,
                            "Multi-byte UTF-8 should not cause span issues: start={}, end={}, text_len={}",
                            entity.start, entity.end, text_char_count
                        );
                    }
                    _ => {}
                }
            }
            
            // Extracted text should be valid UTF-8
            prop_assert!(
                std::str::from_utf8(extracted.as_bytes()).is_ok(),
                "Extracted text should be valid UTF-8"
            );
        }
    }

    /// Property: Optimizations handle emoji and grapheme clusters correctly
    #[test]
    fn emoji_grapheme_clusters_optimization(text in "[\\u{1F300}-\\u{1F9FF}]{0,50}") {
        let ner = StackedNER::default();
        let entities = ner.extract_entities(&text, None).unwrap();
        let text_char_count = text.chars().count();
        
        for entity in entities {
            // Use optimized extraction
            let extracted = entity.extract_text_with_len(&text, text_char_count);
            
            // Should handle emoji correctly (may be multiple code points per grapheme)
            prop_assert!(
                entity.start < entity.end,
                "Entity should have valid span with emoji"
            );
            
            // Extracted text should be valid
            prop_assert!(
                !extracted.is_empty() || entity.start >= text_char_count || entity.end > text_char_count,
                "Extracted text should be non-empty for valid spans"
            );
        }
    }

    /// Property: Optimizations handle control characters correctly
    #[test]
    fn control_characters_optimization(text in "[\\u{0000}-\\u{001F}\\u{007F}-\\u{009F}]{0,100}") {
        let ner = StackedNER::default();
        let entities = ner.extract_entities(&text, None).unwrap();
        let text_char_count = text.chars().count();
        
        for entity in entities {
            // Use optimized methods
            let extracted = entity.extract_text_with_len(&text, text_char_count);
            let issues = entity.validate_with_len(&text, text_char_count);
            
            // Should not panic or produce invalid spans
            prop_assert!(
                entity.start < entity.end,
                "Entity should have valid span with control characters"
            );
            
            // Extracted text should be valid (may contain control chars, that's ok)
            let _ = extracted; // Just ensure it doesn't panic
        }
    }

    /// Property: extract_text_with_len matches extract_text for Unicode text
    #[test]
    fn unicode_extract_text_equivalence(
        text in "[\\u{0000}-\\u{FFFF}]{0,200}",
        start in 0usize..500,
        end in 0usize..500
    ) {
        let text_char_count = text.chars().count();
        let start = start.min(text_char_count);
        let end = end.min(text_char_count).max(start);
        
        let entity = Entity::new("test", EntityType::Person, start, end, 0.5);
        
        let result_optimized = entity.extract_text_with_len(&text, text_char_count);
        let result_original = entity.extract_text(&text);
        
        prop_assert_eq!(
            result_optimized, result_original,
            "Unicode text extraction should match: start={}, end={}, text_len={}",
            start, end, text_char_count
        );
    }

    /// Property: validate_with_len matches validate for Unicode text
    #[test]
    fn unicode_validate_equivalence(
        text in "[\\u{0000}-\\u{FFFF}]{0,200}",
        start in 0usize..500,
        end in 0usize..500
    ) {
        let text_char_count = text.chars().count();
        let start = start.min(text_char_count);
        let end = end.min(text_char_count).max(start);
        
        // Create entity with matching text
        let entity_text: String = text.chars().skip(start).take(end - start).collect();
        let entity = Entity::new(&entity_text, EntityType::Person, start, end, 0.5);
        
        let result_optimized = entity.validate_with_len(&text, text_char_count);
        let result_original = entity.validate(&text);
        
        prop_assert_eq!(
            result_optimized.len(), result_original.len(),
            "Unicode validation should produce same number of issues"
        );
    }
}

