//! Report persistence tests

use super::{BenchmarkReport, load_report_file, load_report_json, save_report_file};
use std::fs;
use tempfile::TempDir;

#[test]
fn test_save_report_file() {
    // Test saving a report to file
    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let file_path = temp_dir.path().join("test_report.json");

    // Create a test report
    let mut report = BenchmarkReport::new();
    report.set_environment("test");
    report.add_tag("test_type", "persistence");

    // Save the report
    let result = save_report_file(&report, file_path.to_str().unwrap());
    assert!(result.is_ok(), "Should successfully save report");

    // Verify file exists
    assert!(file_path.exists(), "Report file should exist after saving");

    // Verify file content is valid JSON
    let content = fs::read_to_string(&file_path).expect("Should be able to read saved file");
    assert!(!content.is_empty(), "File should not be empty");

    // Verify it's valid JSON
    let parsed: serde_json::Value =
        serde_json::from_str(&content).expect("Saved content should be valid JSON");

    // Verify key fields are present
    assert!(
        parsed.get("schema_version").is_some(),
        "Should have schema_version"
    );
    assert!(parsed.get("timestamp").is_some(), "Should have timestamp");
    assert!(
        parsed.get("benchmark_id").is_some(),
        "Should have benchmark_id"
    );

    // Verify environment tag
    if let Some(tags) = parsed.get("tags") {
        if let Some(tags_obj) = tags.as_object() {
            assert!(
                tags_obj.contains_key("test_type"),
                "Should have test_type tag"
            );
        }
    }
}

#[test]
fn test_load_report_json() {
    // Test loading a report from JSON string
    let json_str = r#"{
        "schema_version": "1.0.0",
        "system_profile": null,
        "network_results": [],
        "disk_results": [],
        "memory_results": [],
        "cpu_results": [],
        "timestamp": "2026-01-14T20:00:00Z",
        "duration": {"secs": 30, "nanos": 0},
        "benchmark_id": "test-uuid-123",
        "environment": "test",
        "git_commit": "abc123",
        "tags": {"test_type": "persistence"}
    }"#;

    // Load the report
    let result = load_report_json(json_str);
    assert!(result.is_ok(), "Should successfully load report from JSON");

    let report = result.unwrap();

    // Verify fields
    assert_eq!(report.schema_version, "1.0.0");
    assert_eq!(report.environment, Some("test".to_string()));
    assert_eq!(report.git_commit, Some("abc123".to_string()));
    assert!(report.tags.contains_key("test_type"));
    assert_eq!(
        report.tags.get("test_type"),
        Some(&"persistence".to_string())
    );
    assert!(!report.benchmark_id.is_empty());
    assert!(!report.timestamp.is_empty());
}

#[test]
fn test_load_report_file() {
    // Test loading a report from file
    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let file_path = temp_dir.path().join("test_report.json");

    // Create test JSON content
    let json_str = r#"{
        "schema_version": "1.0.0",
        "system_profile": null,
        "network_results": [],
        "disk_results": [],
        "memory_results": [],
        "cpu_results": [],
        "timestamp": "2026-01-14T20:00:00Z",
        "duration": {"secs": 30, "nanos": 0},
        "benchmark_id": "test-uuid-456",
        "environment": "test",
        "git_commit": "def456",
        "tags": {"test_type": "file_load"}
    }"#;

    // Write JSON to file
    fs::write(&file_path, json_str).expect("Should be able to write test file");

    // Load the report
    let result = load_report_file(file_path.to_str().unwrap());
    if let Err(e) = &result {
        println!("Load error: {:?}", e);
    }
    assert!(result.is_ok(), "Should successfully load report from file");

    let report = result.unwrap();

    // Verify fields
    assert_eq!(report.schema_version, "1.0.0");
    assert_eq!(report.environment, Some("test".to_string()));
    assert_eq!(report.git_commit, Some("def456".to_string()));
    assert!(report.tags.contains_key("test_type"));
    assert_eq!(report.tags.get("test_type"), Some(&"file_load".to_string()));
    assert_eq!(report.benchmark_id, "test-uuid-456");
}

#[test]
fn test_round_trip_persistence() {
    // Test save and load round trip
    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let file_path = temp_dir.path().join("round_trip.json");

    // Create original report
    let mut original = BenchmarkReport::new();
    original.set_environment("production");
    original.set_git_commit("round-trip-test");
    original.add_tag("test", "round_trip");
    original.add_tag("version", "1.0");

    // Save report
    let save_result = save_report_file(&original, file_path.to_str().unwrap());
    assert!(save_result.is_ok(), "Should save successfully");

    // Load report
    let load_result = load_report_file(file_path.to_str().unwrap());
    assert!(load_result.is_ok(), "Should load successfully");

    let loaded = load_result.unwrap();

    // Verify all fields match
    assert_eq!(original.schema_version, loaded.schema_version);
    assert_eq!(original.environment, loaded.environment);
    assert_eq!(original.git_commit, loaded.git_commit);
    assert_eq!(original.tags, loaded.tags);
    assert_eq!(original.duration, loaded.duration);

    // UUID and timestamp will be different for new reports, but should be preserved in round trip
    assert_eq!(original.benchmark_id, loaded.benchmark_id);
    assert_eq!(original.timestamp, loaded.timestamp);
}

#[test]
fn test_load_invalid_json() {
    // Test loading invalid JSON
    let invalid_json = r#"{
        "schema_version": "1.0.0",
        "system_profile": null,
        "network_results": [],
        "disk_results": [],
        "memory_results": [],
        "cpu_results": [],
        "timestamp": "2026-01-14T20:00:00Z",
        "duration": {"secs": 30, "nanos": 0},
        "benchmark_id": "test-uuid-789",
        "environment": "test",
        "git_commit": "ghi789",
        "tags": {"test_type": "invalid_json"
        // Missing closing brace for tags object
    "#;

    let result = load_report_json(invalid_json);
    assert!(result.is_err(), "Should fail to load invalid JSON");

    match result.unwrap_err() {
        super::super::error::ProfilingError::ParseError(_) => {
            // Expected error type
        }
        _ => panic!("Should return ParseError for invalid JSON"),
    }
}

#[test]
fn test_load_nonexistent_file() {
    // Test loading non-existent file
    let result = load_report_file("/nonexistent/path/report.json");
    assert!(result.is_err(), "Should fail to load non-existent file");

    match result.unwrap_err() {
        super::super::error::ProfilingError::IoError(_) => {
            // Expected error type
        }
        _ => panic!("Should return IoError for non-existent file"),
    }
}

#[test]
fn test_save_to_invalid_path() {
    // Test saving to invalid path
    let report = BenchmarkReport::new();

    // Try to save to a directory that doesn't exist
    let result = save_report_file(&report, "/nonexistent/directory/report.json");
    assert!(result.is_err(), "Should fail to save to invalid path");

    match result.unwrap_err() {
        super::super::error::ProfilingError::IoError(_) => {
            // Expected error type
        }
        _ => panic!("Should return IoError for invalid path"),
    }
}

#[test]
fn test_persistence_with_system_profile() {
    // Test persistence with system profile data
    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let file_path = temp_dir.path().join("profile_test.json");

    // Create report with system profile
    let report = BenchmarkReport::new();

    // Note: We can't easily create a real SystemProfile in tests without system access
    // But we can test that the structure handles null correctly

    // Save report
    let save_result = save_report_file(&report, file_path.to_str().unwrap());
    assert!(
        save_result.is_ok(),
        "Should save report with null system profile"
    );

    // Load report
    let load_result = load_report_file(file_path.to_str().unwrap());
    assert!(
        load_result.is_ok(),
        "Should load report with null system profile"
    );

    let loaded = load_result.unwrap();
    assert!(
        loaded.system_profile.is_none(),
        "System profile should be null"
    );
}

#[test]
fn test_persistence_with_results() {
    // Test persistence with benchmark results
    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let file_path = temp_dir.path().join("results_test.json");

    // Create report with results (using empty results for simplicity)
    let mut report = BenchmarkReport::new();
    report.set_environment("test");

    // The report should have empty result vectors by default
    assert!(report.network_results.is_empty());
    assert!(report.disk_results.is_empty());
    assert!(report.memory_results.is_empty());
    assert!(report.cpu_results.is_empty());

    // Save and load
    let save_result = save_report_file(&report, file_path.to_str().unwrap());
    assert!(save_result.is_ok(), "Should save report with empty results");

    let load_result = load_report_file(file_path.to_str().unwrap());
    assert!(load_result.is_ok(), "Should load report with empty results");

    let loaded = load_result.unwrap();
    assert_eq!(loaded.network_results.len(), 0);
    assert_eq!(loaded.disk_results.len(), 0);
    assert_eq!(loaded.memory_results.len(), 0);
    assert_eq!(loaded.cpu_results.len(), 0);
}
