use docx_mcp::security::{SecurityConfig, SecurityMiddleware, SecurityError};
use serde_json::json;
use std::collections::HashSet;
use pretty_assertions::assert_eq;
use rstest::*;

#[test]
fn test_default_security_config() {
    let config = SecurityConfig::default();
    
    assert!(!config.readonly_mode);
    assert!(config.command_whitelist.is_none());
    assert!(config.command_blacklist.is_none());
    assert_eq!(config.max_document_size, 100 * 1024 * 1024);
    assert_eq!(config.max_open_documents, 50);
    assert!(config.allow_external_tools);
    assert!(config.allow_network);
    assert!(!config.sandbox_mode);
}

#[test]
fn test_readonly_mode_allows_only_safe_commands() {
    let config = SecurityConfig {
        readonly_mode: true,
        ..Default::default()
    };
    
    // Should allow readonly commands
    assert!(config.is_command_allowed("open_document"));
    assert!(config.is_command_allowed("extract_text"));
    assert!(config.is_command_allowed("get_metadata"));
    assert!(config.is_command_allowed("search_text"));
    assert!(config.is_command_allowed("export_to_markdown"));
    
    // Should block write commands
    assert!(!config.is_command_allowed("create_document"));
    assert!(!config.is_command_allowed("add_paragraph"));
    assert!(!config.is_command_allowed("save_document"));
    assert!(!config.is_command_allowed("find_and_replace"));
    assert!(!config.is_command_allowed("convert_to_pdf"));
}

#[test]
fn test_command_whitelist() {
    let mut whitelist = HashSet::new();
    whitelist.insert("open_document".to_string());
    whitelist.insert("extract_text".to_string());
    
    let config = SecurityConfig {
        command_whitelist: Some(whitelist),
        ..Default::default()
    };
    
    // Should allow whitelisted commands
    assert!(config.is_command_allowed("open_document"));
    assert!(config.is_command_allowed("extract_text"));
    
    // Should block non-whitelisted commands
    assert!(!config.is_command_allowed("create_document"));
    assert!(!config.is_command_allowed("add_paragraph"));
    assert!(!config.is_command_allowed("get_metadata"));
}

#[test]
fn test_command_blacklist() {
    let mut blacklist = HashSet::new();
    blacklist.insert("save_document".to_string());
    blacklist.insert("convert_to_pdf".to_string());
    
    let config = SecurityConfig {
        command_blacklist: Some(blacklist),
        ..Default::default()
    };
    
    // Should allow non-blacklisted commands
    assert!(config.is_command_allowed("open_document"));
    assert!(config.is_command_allowed("extract_text"));
    assert!(config.is_command_allowed("add_paragraph"));
    
    // Should block blacklisted commands
    assert!(!config.is_command_allowed("save_document"));
    assert!(!config.is_command_allowed("convert_to_pdf"));
}

#[test]
fn test_whitelist_overrides_blacklist() {
    let mut whitelist = HashSet::new();
    whitelist.insert("save_document".to_string());
    
    let mut blacklist = HashSet::new();
    blacklist.insert("save_document".to_string());
    
    let config = SecurityConfig {
        command_whitelist: Some(whitelist),
        command_blacklist: Some(blacklist),
        ..Default::default()
    };
    
    // Whitelist should take precedence
    assert!(config.is_command_allowed("save_document"));
}

#[test]
fn test_external_tools_restriction() {
    let config = SecurityConfig {
        allow_external_tools: false,
        ..Default::default()
    };
    
    // Should block conversion commands that might use external tools
    assert!(!config.is_command_allowed("convert_to_pdf"));
    assert!(!config.is_command_allowed("convert_to_images"));
    
    // Should allow other commands
    assert!(config.is_command_allowed("open_document"));
    assert!(config.is_command_allowed("add_paragraph"));
}

#[test]
fn test_security_middleware_command_check() {
    let config = SecurityConfig {
        readonly_mode: true,
        ..Default::default()
    };
    let middleware = SecurityMiddleware::new(config);
    
    let safe_args = json!({"document_id": "test"});
    
    // Should pass readonly commands
    let result = middleware.check_command("extract_text", &safe_args);
    assert!(result.is_ok());
    
    // Should fail write commands
    let result = middleware.check_command("add_paragraph", &safe_args);
    assert!(matches!(result, Err(SecurityError::CommandNotAllowed(_))));
}

#[test]
fn test_sandbox_mode_path_restrictions() {
    let config = SecurityConfig {
        sandbox_mode: true,
        ..Default::default()
    };
    let middleware = SecurityMiddleware::new(config);
    
    // Should allow temp directory paths
    let temp_args = json!({"path": "/tmp/docx-mcp/test.docx"});
    let result = middleware.check_command("open_document", &temp_args);
    assert!(result.is_ok());
    
    // Should block paths outside temp directory
    let home_args = json!({"path": "/home/user/documents/test.docx"});
    let result = middleware.check_command("open_document", &home_args);
    assert!(matches!(result, Err(SecurityError::PathNotAllowed(_))));
}

#[test]
fn test_file_size_limits() {
    use tempfile::NamedTempFile;
    use std::io::Write;
    
    let config = SecurityConfig {
        max_document_size: 100, // 100 bytes limit
        ..Default::default()
    };
    let middleware = SecurityMiddleware::new(config);
    
    // Create a test file larger than limit
    let mut temp_file = NamedTempFile::new().unwrap();
    let large_content = vec![0u8; 200]; // 200 bytes
    temp_file.write_all(&large_content).unwrap();
    temp_file.flush().unwrap();
    
    let args = json!({"path": temp_file.path().to_str().unwrap()});
    let result = middleware.check_command("open_document", &args);
    
    assert!(matches!(result, Err(SecurityError::FileTooLarge { .. })));
}

#[test]
fn test_readonly_commands_list() {
    let readonly_commands = SecurityConfig::get_readonly_commands();
    
    // Should include expected readonly commands
    assert!(readonly_commands.contains("open_document"));
    assert!(readonly_commands.contains("extract_text"));
    assert!(readonly_commands.contains("get_metadata"));
    assert!(readonly_commands.contains("search_text"));
    assert!(readonly_commands.contains("analyze_formatting"));
    
    // Should not include write commands
    assert!(!readonly_commands.contains("create_document"));
    assert!(!readonly_commands.contains("add_paragraph"));
    assert!(!readonly_commands.contains("save_document"));
}

#[test]
fn test_write_commands_list() {
    let write_commands = SecurityConfig::get_write_commands();
    
    // Should include expected write commands
    assert!(write_commands.contains("create_document"));
    assert!(write_commands.contains("add_paragraph"));
    assert!(write_commands.contains("save_document"));
    assert!(write_commands.contains("find_and_replace"));
    
    // Should not include readonly commands
    assert!(!write_commands.contains("open_document"));
    assert!(!write_commands.contains("extract_text"));
    assert!(!write_commands.contains("get_metadata"));
}

#[test]
fn test_security_summary() {
    let config = SecurityConfig {
        readonly_mode: true,
        sandbox_mode: true,
        allow_external_tools: false,
        ..Default::default()
    };
    
    let summary = config.get_summary();
    assert!(summary.contains("READONLY MODE"));
    assert!(summary.contains("SANDBOX MODE"));
    assert!(summary.contains("No external tools"));
}

#[test]
fn test_combined_security_modes() {
    let mut whitelist = HashSet::new();
    whitelist.insert("open_document".to_string());
    whitelist.insert("extract_text".to_string());
    
    let config = SecurityConfig {
        readonly_mode: true,
        sandbox_mode: true,
        command_whitelist: Some(whitelist),
        allow_external_tools: false,
        allow_network: false,
        max_document_size: 1024,
        ..Default::default()
    };
    
    // Should only allow whitelisted readonly commands
    assert!(config.is_command_allowed("open_document"));
    assert!(config.is_command_allowed("extract_text"));
    
    // Should block everything else
    assert!(!config.is_command_allowed("get_metadata")); // Not in whitelist
    assert!(!config.is_command_allowed("add_paragraph")); // Not readonly
    assert!(!config.is_command_allowed("convert_to_pdf")); // External tools disabled
}

#[test]
fn test_recursive_path_argument_checking() {
    let config = SecurityConfig {
        sandbox_mode: true,
        ..Default::default()
    };
    let middleware = SecurityMiddleware::new(config);
    
    // Complex nested arguments with paths
    let nested_args = json!({
        "document_id": "test",
        "options": {
            "output_path": "/home/user/bad/path.docx",
            "settings": {
                "temp_file": "/tmp/safe/path.tmp"
            }
        },
        "files": [
            "/home/user/another/bad/path.docx",
            "/tmp/docx-mcp/safe/path.docx"
        ]
    });
    
    let result = middleware.check_command("some_command", &nested_args);
    assert!(matches!(result, Err(SecurityError::PathNotAllowed(_))));
}

#[test]
fn test_security_error_messages() {
    let error = SecurityError::CommandNotAllowed("dangerous_command".to_string());
    assert!(error.to_string().contains("dangerous_command"));
    
    let error = SecurityError::PathNotAllowed("/bad/path".to_string());
    assert!(error.to_string().contains("/bad/path"));
    
    let error = SecurityError::FileTooLarge { size: 2000, max_size: 1000 };
    assert!(error.to_string().contains("2000"));
    assert!(error.to_string().contains("1000"));
}

#[fixture]
fn readonly_config() -> SecurityConfig {
    SecurityConfig {
        readonly_mode: true,
        ..Default::default()
    }
}

#[fixture] 
fn sandbox_config() -> SecurityConfig {
    SecurityConfig {
        sandbox_mode: true,
        allow_external_tools: false,
        allow_network: false,
        ..Default::default()
    }
}

#[fixture]
fn restrictive_config() -> SecurityConfig {
    let mut whitelist = HashSet::new();
    whitelist.insert("open_document".to_string());
    whitelist.insert("extract_text".to_string());
    
    SecurityConfig {
        readonly_mode: true,
        sandbox_mode: true,
        command_whitelist: Some(whitelist),
        max_document_size: 1024 * 1024, // 1MB
        max_open_documents: 5,
        allow_external_tools: false,
        allow_network: false,
    }
}

#[rstest]
#[case("open_document", true)]
#[case("extract_text", true)]  
#[case("get_metadata", true)]
#[case("create_document", false)]
#[case("add_paragraph", false)]
#[case("save_document", false)]
fn test_readonly_mode_commands(readonly_config: SecurityConfig, #[case] command: &str, #[case] expected: bool) {
    assert_eq!(readonly_config.is_command_allowed(command), expected);
}

#[rstest]
#[case("open_document", true)]
#[case("extract_text", true)]
#[case("add_paragraph", false)] // Not in whitelist
#[case("get_metadata", false)]  // Not in whitelist
fn test_restrictive_mode_commands(restrictive_config: SecurityConfig, #[case] command: &str, #[case] expected: bool) {
    assert_eq!(restrictive_config.is_command_allowed(command), expected);
}