use anyhow::Result;
use docx_mcp::docx_handler::{DocxHandler, DocxStyle, TableData};
use tempfile::TempDir;
use std::path::PathBuf;
use pretty_assertions::assert_eq;
use rstest::*;
use chrono::Utc;

fn setup_test_handler() -> (DocxHandler, TempDir) {
    let temp_dir = TempDir::new().unwrap();
    let handler = DocxHandler::new_with_temp_dir(temp_dir.path()).unwrap();
    (handler, temp_dir)
}

#[fixture]
fn handler_and_doc() -> (DocxHandler, String, TempDir) {
    let (mut handler, temp_dir) = setup_test_handler();
    let doc_id = handler.create_document().unwrap();
    (handler, doc_id, temp_dir)
}

#[test]
fn test_create_document() {
    let (mut handler, _temp_dir) = setup_test_handler();
    
    let doc_id = handler.create_document().unwrap();
    assert!(!doc_id.is_empty());
    
    // Document should be in the handler's registry
    assert!(handler.documents.contains_key(&doc_id));
    
    let metadata = handler.get_metadata(&doc_id).unwrap();
    assert_eq!(metadata.id, doc_id);
    assert!(metadata.path.exists());
}

#[test]
fn test_add_paragraph() {
    let (mut handler, doc_id, _temp_dir) = handler_and_doc();
    
    let result = handler.add_paragraph(&doc_id, "Test paragraph", None);
    assert!(result.is_ok());
    
    // Verify content was added by extracting text
    let text = handler.extract_text(&doc_id).unwrap();
    assert!(text.contains("Test paragraph"));
}

#[test]
fn test_add_paragraph_with_style() {
    let (mut handler, doc_id, _temp_dir) = handler_and_doc();
    
    let style = DocxStyle {
        font_family: Some("Arial".to_string()),
        font_size: Some(14),
        bold: Some(true),
        italic: Some(false),
        underline: Some(false),
        color: Some("#FF0000".to_string()),
        alignment: Some("center".to_string()),
        line_spacing: Some(1.5),
    };
    
    let result = handler.add_paragraph(&doc_id, "Styled paragraph", Some(style));
    assert!(result.is_ok());
    
    let text = handler.extract_text(&doc_id).unwrap();
    assert!(text.contains("Styled paragraph"));
}

#[test]
fn test_add_heading() {
    let (mut handler, doc_id, _temp_dir) = handler_and_doc();
    
    for level in 1..=6 {
        let heading_text = format!("Heading Level {}", level);
        let result = handler.add_heading(&doc_id, &heading_text, level);
        assert!(result.is_ok(), "Failed to add heading level {}", level);
        
        let text = handler.extract_text(&doc_id).unwrap();
        assert!(text.contains(&heading_text));
    }
}

#[test]
fn test_add_table() {
    let (mut handler, doc_id, _temp_dir) = handler_and_doc();
    
    let table_data = TableData {
        rows: vec![
            vec!["Name".to_string(), "Age".to_string(), "City".to_string()],
            vec!["John".to_string(), "30".to_string(), "NYC".to_string()],
            vec!["Jane".to_string(), "25".to_string(), "LA".to_string()],
        ],
        headers: Some(vec!["Name".to_string(), "Age".to_string(), "City".to_string()]),
        border_style: Some("single".to_string()),
    };
    
    let result = handler.add_table(&doc_id, table_data);
    assert!(result.is_ok());
    
    let text = handler.extract_text(&doc_id).unwrap();
    assert!(text.contains("John"));
    assert!(text.contains("Jane"));
    assert!(text.contains("NYC"));
}

#[test]
fn test_add_list() {
    let (mut handler, doc_id, _temp_dir) = handler_and_doc();
    
    let items = vec![
        "First item".to_string(),
        "Second item".to_string(),
        "Third item".to_string(),
    ];
    
    // Test unordered list
    let result = handler.add_list(&doc_id, items.clone(), false);
    assert!(result.is_ok());
    
    // Test ordered list
    let result = handler.add_list(&doc_id, items.clone(), true);
    assert!(result.is_ok());
    
    let text = handler.extract_text(&doc_id).unwrap();
    assert!(text.contains("First item"));
    assert!(text.contains("Second item"));
    assert!(text.contains("Third item"));
}

#[test]
fn test_set_header_footer() {
    let (mut handler, doc_id, _temp_dir) = handler_and_doc();
    
    let header_result = handler.set_header(&doc_id, "Document Header");
    assert!(header_result.is_ok());
    
    let footer_result = handler.set_footer(&doc_id, "Document Footer");
    assert!(footer_result.is_ok());
}

#[test]
fn test_add_page_break() {
    let (mut handler, doc_id, _temp_dir) = handler_and_doc();
    
    handler.add_paragraph(&doc_id, "Before page break", None).unwrap();
    
    let result = handler.add_page_break(&doc_id);
    assert!(result.is_ok());
    
    handler.add_paragraph(&doc_id, "After page break", None).unwrap();
    
    let text = handler.extract_text(&doc_id).unwrap();
    assert!(text.contains("Before page break"));
    assert!(text.contains("After page break"));
}

#[test]
fn test_extract_text_empty_document() {
    let (handler, doc_id, _temp_dir) = handler_and_doc();
    
    let text = handler.extract_text(&doc_id).unwrap();
    // Empty document might have some default content or be truly empty
    assert!(text.is_empty() || text.trim().is_empty());
}

#[test]
fn test_save_and_close_document() {
    let (mut handler, doc_id, temp_dir) = handler_and_doc();
    
    handler.add_paragraph(&doc_id, "Test content", None).unwrap();
    
    let save_path = temp_dir.path().join("test_output.docx");
    let save_result = handler.save_document(&doc_id, &save_path);
    assert!(save_result.is_ok());
    assert!(save_path.exists());
    
    let close_result = handler.close_document(&doc_id);
    assert!(close_result.is_ok());
    assert!(!handler.documents.contains_key(&doc_id));
}

#[test]
fn test_open_existing_document() {
    let (mut handler, doc_id, temp_dir) = handler_and_doc();
    
    // Create and save a document
    handler.add_paragraph(&doc_id, "Original content", None).unwrap();
    let save_path = temp_dir.path().join("existing.docx");
    handler.save_document(&doc_id, &save_path).unwrap();
    handler.close_document(&doc_id).unwrap();
    
    // Open the saved document
    let opened_doc_id = handler.open_document(&save_path).unwrap();
    assert_ne!(opened_doc_id, doc_id); // Should be a new ID
    
    let text = handler.extract_text(&opened_doc_id).unwrap();
    assert!(text.contains("Original content"));
}

#[test]
fn test_list_documents() {
    let (mut handler, _temp_dir) = setup_test_handler();
    
    // Initially should be empty
    let docs = handler.list_documents();
    let initial_count = docs.len();
    
    // Create some documents
    let _doc1 = handler.create_document().unwrap();
    let _doc2 = handler.create_document().unwrap();
    let _doc3 = handler.create_document().unwrap();
    
    let docs = handler.list_documents();
    assert_eq!(docs.len(), initial_count + 3);
}

#[test]
fn test_document_not_found_error() {
    let (handler, _temp_dir) = setup_test_handler();
    
    let fake_id = "nonexistent-document-id";
    
    let result = handler.extract_text(fake_id);
    assert!(result.is_err());
    assert!(result.unwrap_err().to_string().contains("Document not found"));
}

#[test]
fn test_get_metadata() {
    let (handler, doc_id, _temp_dir) = handler_and_doc();
    
    let metadata = handler.get_metadata(&doc_id).unwrap();
    
    assert_eq!(metadata.id, doc_id);
    assert!(metadata.path.exists());
    assert!(metadata.created_at <= Utc::now());
    assert!(metadata.modified_at <= Utc::now());
    assert_eq!(metadata.page_count, Some(1));
    assert_eq!(metadata.word_count, Some(0));
}

#[test]
fn test_concurrent_document_operations() {
    use std::sync::Arc;
    use std::sync::Mutex;
    use std::thread;
    
    let (handler, _temp_dir) = setup_test_handler();
    let handler = Arc::new(Mutex::new(handler));
    
    let handles: Vec<_> = (0..5).map(|i| {
        let handler = Arc::clone(&handler);
        thread::spawn(move || {
            let doc_id = {
                let mut h = handler.lock().unwrap();
                h.create_document().unwrap()
            };
            
            {
                let mut h = handler.lock().unwrap();
                h.add_paragraph(&doc_id, &format!("Thread {} content", i), None).unwrap();
            }
            
            {
                let h = handler.lock().unwrap();
                let text = h.extract_text(&doc_id).unwrap();
                assert!(text.contains(&format!("Thread {} content", i)));
            }
            
            doc_id
        })
    }).collect();
    
    let doc_ids: Vec<_> = handles.into_iter().map(|h| h.join().unwrap()).collect();
    
    // All documents should be different
    let mut unique_ids = doc_ids.clone();
    unique_ids.sort();
    unique_ids.dedup();
    assert_eq!(unique_ids.len(), doc_ids.len());
}

#[test]
fn test_large_document_creation() {
    let (mut handler, doc_id, _temp_dir) = handler_and_doc();
    
    // Add many paragraphs to test performance
    for i in 0..100 {
        let content = format!("Paragraph number {} with some content to make it realistic", i);
        handler.add_paragraph(&doc_id, &content, None).unwrap();
    }
    
    let text = handler.extract_text(&doc_id).unwrap();
    assert!(text.contains("Paragraph number 0"));
    assert!(text.contains("Paragraph number 99"));
    
    // Verify word count
    let words: Vec<&str> = text.split_whitespace().collect();
    assert!(words.len() > 1000); // Should have many words
}

#[test]
fn test_special_characters_in_content() {
    let (mut handler, doc_id, _temp_dir) = handler_and_doc();
    
    let special_content = "Special chars: éñüñdéd, 中文, русский, العربية, 🚀📝✨";
    handler.add_paragraph(&doc_id, special_content, None).unwrap();
    
    let text = handler.extract_text(&doc_id).unwrap();
    assert!(text.contains("éñüñdéd"));
    assert!(text.contains("🚀📝✨"));
}