use std::path::Path;

use crate::app::reports::state::{RuntimeOverlayImportExportOutput, RuntimeSaveSliceExportOutput};
use rrt_fixtures::{FixtureValidationReport, normalize_runtime_state};
use rrt_runtime::documents::{
    OVERLAY_IMPORT_DOCUMENT_FORMAT_VERSION, RuntimeOverlayImportDocument,
    RuntimeOverlayImportDocumentSource, RuntimeSaveSliceDocument, RuntimeSaveSliceDocumentSource,
    SAVE_SLICE_DOCUMENT_FORMAT_VERSION, load_runtime_state_input,
    save_runtime_overlay_import_document, save_runtime_save_slice_document,
};
use rrt_runtime::inspect::smp::save_load::SmpLoadedSaveSlice;
use rrt_runtime::persistence::{
    load_runtime_snapshot_document, validate_runtime_snapshot_document,
};
use serde_json::Value;

pub(crate) fn load_normalized_runtime_state(
    path: &Path,
) -> Result<Value, Box<dyn std::error::Error>> {
    if let Ok(snapshot) = load_runtime_snapshot_document(path) {
        validate_runtime_snapshot_document(&snapshot)
            .map_err(|err| format!("invalid runtime snapshot: {err}"))?;
        return normalize_runtime_state(&snapshot.state);
    }

    let input = load_runtime_state_input(path)?;
    normalize_runtime_state(&input.state)
}

pub(crate) fn export_runtime_save_slice_document(
    smp_path: &Path,
    output_path: &Path,
    save_slice: SmpLoadedSaveSlice,
) -> Result<RuntimeSaveSliceExportOutput, Box<dyn std::error::Error>> {
    let document = RuntimeSaveSliceDocument {
        format_version: SAVE_SLICE_DOCUMENT_FORMAT_VERSION,
        save_slice_id: smp_path
            .file_stem()
            .and_then(|stem| stem.to_str())
            .unwrap_or("save-slice")
            .to_string(),
        source: RuntimeSaveSliceDocumentSource {
            description: Some(format!(
                "Exported loaded save slice from {}",
                smp_path.display()
            )),
            original_save_filename: smp_path
                .file_name()
                .and_then(|name| name.to_str())
                .map(ToString::to_string),
            original_save_sha256: None,
            notes: vec![],
        },
        save_slice,
    };
    save_runtime_save_slice_document(output_path, &document)?;
    Ok(RuntimeSaveSliceExportOutput {
        path: smp_path.display().to_string(),
        output_path: output_path.display().to_string(),
        save_slice_id: document.save_slice_id,
    })
}

pub(crate) fn export_runtime_overlay_import_document(
    snapshot_path: &Path,
    save_slice_path: &Path,
    output_path: &Path,
) -> Result<RuntimeOverlayImportExportOutput, Box<dyn std::error::Error>> {
    let import_id = output_path
        .file_stem()
        .and_then(|stem| stem.to_str())
        .unwrap_or("overlay-import")
        .to_string();
    let document = RuntimeOverlayImportDocument {
        format_version: OVERLAY_IMPORT_DOCUMENT_FORMAT_VERSION,
        import_id: import_id.clone(),
        source: RuntimeOverlayImportDocumentSource {
            description: Some(format!(
                "Overlay import referencing {} and {}",
                snapshot_path.display(),
                save_slice_path.display()
            )),
            notes: vec![],
        },
        base_snapshot_path: snapshot_path.display().to_string(),
        save_slice_path: save_slice_path.display().to_string(),
    };
    save_runtime_overlay_import_document(output_path, &document)?;
    Ok(RuntimeOverlayImportExportOutput {
        output_path: output_path.display().to_string(),
        import_id,
        base_snapshot_path: document.base_snapshot_path,
        save_slice_path: document.save_slice_path,
    })
}

pub(crate) fn print_runtime_validation_report(
    report: &FixtureValidationReport,
) -> Result<(), Box<dyn std::error::Error>> {
    println!("{}", serde_json::to_string_pretty(report)?);
    Ok(())
}
