use std::path::Path;

use crate::app::helpers::state_io::load_normalized_runtime_state;
use crate::app::reports::state::{RuntimeStateDiffReport, RuntimeStateSummaryReport};
use rrt_fixtures::diff_json_values;
use rrt_runtime::{
    documents::load_runtime_state_input,
    persistence::{
        RuntimeSnapshotDocument, RuntimeSnapshotSource, SNAPSHOT_FORMAT_VERSION,
        load_runtime_snapshot_document, save_runtime_snapshot_document,
        validate_runtime_snapshot_document,
    },
    summary::RuntimeSummary,
};

pub(crate) fn summarize_state(snapshot_path: &Path) -> Result<(), Box<dyn std::error::Error>> {
    if let Ok(snapshot) = load_runtime_snapshot_document(snapshot_path) {
        validate_runtime_snapshot_document(&snapshot)
            .map_err(|err| format!("invalid runtime snapshot: {err}"))?;
        let report = RuntimeStateSummaryReport {
            snapshot_id: snapshot.snapshot_id.clone(),
            summary: snapshot.summary(),
        };
        println!("{}", serde_json::to_string_pretty(&report)?);
        return Ok(());
    }

    let input = load_runtime_state_input(snapshot_path)?;
    let report = RuntimeStateSummaryReport {
        snapshot_id: input.input_id,
        summary: RuntimeSummary::from_state(&input.state),
    };
    println!("{}", serde_json::to_string_pretty(&report)?);
    Ok(())
}

pub(crate) fn diff_state(
    left_path: &Path,
    right_path: &Path,
) -> Result<(), Box<dyn std::error::Error>> {
    let left = load_normalized_runtime_state(left_path)?;
    let right = load_normalized_runtime_state(right_path)?;
    let differences = diff_json_values(&left, &right);
    let report = RuntimeStateDiffReport {
        matches: differences.is_empty(),
        difference_count: differences.len(),
        differences,
    };
    println!("{}", serde_json::to_string_pretty(&report)?);
    Ok(())
}

pub(crate) fn snapshot_state(
    input_path: &Path,
    output_path: &Path,
) -> Result<(), Box<dyn std::error::Error>> {
    let input = load_runtime_state_input(input_path)?;
    let snapshot = RuntimeSnapshotDocument {
        format_version: SNAPSHOT_FORMAT_VERSION,
        snapshot_id: format!("{}-snapshot", input.input_id),
        source: RuntimeSnapshotSource {
            source_fixture_id: None,
            description: Some(match input.description {
                Some(description) => format!(
                    "Runtime snapshot from {} ({description})",
                    input_path.display()
                ),
                None => format!("Runtime snapshot from {}", input_path.display()),
            }),
        },
        state: input.state,
    };
    save_runtime_snapshot_document(output_path, &snapshot)?;
    let summary = snapshot.summary();
    let report = RuntimeStateSummaryReport {
        snapshot_id: snapshot.snapshot_id,
        summary,
    };
    println!("{}", serde_json::to_string_pretty(&report)?);
    Ok(())
}
