use std::path::Path;

use crate::app::helpers::state_io::print_runtime_validation_report;
use crate::app::reports::state::{RuntimeFixtureSummaryReport, RuntimeStateSummaryReport};
use rrt_fixtures::{
    normalize_runtime_state,
    summary::compare_expected_state_fragment,
    validation::{load_fixture_document, validate_fixture_document},
};
use rrt_runtime::{
    engine::execute_step_command,
    persistence::{
        RuntimeSnapshotDocument, RuntimeSnapshotSource, SNAPSHOT_FORMAT_VERSION,
        save_runtime_snapshot_document,
    },
    summary::RuntimeSummary,
};

pub(crate) fn validate_fixture(fixture_path: &Path) -> Result<(), Box<dyn std::error::Error>> {
    let fixture = load_fixture_document(fixture_path)?;
    let report = validate_fixture_document(&fixture);
    print_runtime_validation_report(&report)?;
    if !report.valid {
        return Err(format!("fixture validation failed for {}", fixture_path.display()).into());
    }
    Ok(())
}

pub(crate) fn summarize_fixture(fixture_path: &Path) -> Result<(), Box<dyn std::error::Error>> {
    let fixture = load_fixture_document(fixture_path)?;
    let validation_report = validate_fixture_document(&fixture);
    if !validation_report.valid {
        print_runtime_validation_report(&validation_report)?;
        return Err(format!("fixture validation failed for {}", fixture_path.display()).into());
    }

    let mut state = fixture.state.clone();
    for command in &fixture.commands {
        execute_step_command(&mut state, command)?;
    }

    let final_summary = RuntimeSummary::from_state(&state);
    let expected_summary_mismatches = fixture.expected_summary.compare(&final_summary);
    let expected_state_fragment_mismatches = match &fixture.expected_state_fragment {
        Some(expected_fragment) => {
            let normalized_state = normalize_runtime_state(&state)?;
            compare_expected_state_fragment(expected_fragment, &normalized_state)
        }
        None => Vec::new(),
    };
    let report = RuntimeFixtureSummaryReport {
        fixture_id: fixture.fixture_id,
        command_count: fixture.commands.len(),
        expected_summary_matches: expected_summary_mismatches.is_empty(),
        expected_summary_mismatches: expected_summary_mismatches.clone(),
        expected_state_fragment_matches: expected_state_fragment_mismatches.is_empty(),
        expected_state_fragment_mismatches: expected_state_fragment_mismatches.clone(),
        final_summary,
    };
    println!("{}", serde_json::to_string_pretty(&report)?);

    if !expected_summary_mismatches.is_empty() || !expected_state_fragment_mismatches.is_empty() {
        let mut mismatch_messages = expected_summary_mismatches;
        mismatch_messages.extend(expected_state_fragment_mismatches);
        return Err(format!(
            "fixture summary mismatched expected output: {}",
            mismatch_messages.join("; ")
        )
        .into());
    }

    Ok(())
}

pub(crate) fn export_fixture_state(
    fixture_path: &Path,
    output_path: &Path,
) -> Result<(), Box<dyn std::error::Error>> {
    let fixture = load_fixture_document(fixture_path)?;
    let validation_report = validate_fixture_document(&fixture);
    if !validation_report.valid {
        print_runtime_validation_report(&validation_report)?;
        return Err(format!("fixture validation failed for {}", fixture_path.display()).into());
    }

    let mut state = fixture.state.clone();
    for command in &fixture.commands {
        execute_step_command(&mut state, command)?;
    }

    let snapshot = RuntimeSnapshotDocument {
        format_version: SNAPSHOT_FORMAT_VERSION,
        snapshot_id: format!("{}-final-state", fixture.fixture_id),
        source: RuntimeSnapshotSource {
            source_fixture_id: Some(fixture.fixture_id.clone()),
            description: Some(format!(
                "Exported final runtime state for fixture {}",
                fixture.fixture_id
            )),
        },
        state,
    };
    save_runtime_snapshot_document(output_path, &snapshot)?;
    let summary = snapshot.summary();

    println!(
        "{}",
        serde_json::to_string_pretty(&RuntimeStateSummaryReport {
            snapshot_id: snapshot.snapshot_id,
            summary,
        })?
    );

    Ok(())
}
