use std::fs;
use std::path::Path;

use crate::app::helpers::inspect::build_profile_block_export_document;
use crate::app::reports::inspect::{
    RuntimeBuildingTypeInspectionOutput, RuntimeCampaignExeInspectionOutput,
    RuntimeCargoEconomyInspectionOutput, RuntimeCargoSelectorInspectionOutput,
    RuntimeCargoSkinInspectionOutput, RuntimeCargoTypeInspectionOutput, RuntimePk4ExtractionOutput,
    RuntimePk4InspectionOutput, RuntimeProfileBlockExportReport, RuntimeWinInspectionOutput,
};
use rrt_runtime::inspect::{
    building::inspect_building_types_dir_with_bindings,
    campaign::inspect_campaign_exe_file,
    cargo::{
        inspect_cargo_economy_sources_with_bindings, inspect_cargo_skin_pk4,
        inspect_cargo_types_dir,
    },
    pk4::{extract_pk4_entry_file, inspect_pk4_file},
    smp::bundle::inspect_smp_file,
    win::inspect_win_file,
};

pub(crate) fn inspect_pk4(pk4_path: &Path) -> Result<(), Box<dyn std::error::Error>> {
    let report = RuntimePk4InspectionOutput {
        path: pk4_path.display().to_string(),
        inspection: inspect_pk4_file(pk4_path)?,
    };
    println!("{}", serde_json::to_string_pretty(&report)?);
    Ok(())
}

pub(crate) fn inspect_cargo_types(
    cargo_types_dir: &Path,
) -> Result<(), Box<dyn std::error::Error>> {
    let report = RuntimeCargoTypeInspectionOutput {
        path: cargo_types_dir.display().to_string(),
        inspection: inspect_cargo_types_dir(cargo_types_dir)?,
    };
    println!("{}", serde_json::to_string_pretty(&report)?);
    Ok(())
}

pub(crate) fn inspect_building_type_sources(
    building_types_dir: &Path,
    bindings_path: Option<&Path>,
) -> Result<(), Box<dyn std::error::Error>> {
    let report = RuntimeBuildingTypeInspectionOutput {
        path: building_types_dir.display().to_string(),
        inspection: inspect_building_types_dir_with_bindings(building_types_dir, bindings_path)?,
    };
    println!("{}", serde_json::to_string_pretty(&report)?);
    Ok(())
}

pub(crate) fn inspect_cargo_skins(
    cargo_skin_pk4_path: &Path,
) -> Result<(), Box<dyn std::error::Error>> {
    let report = RuntimeCargoSkinInspectionOutput {
        path: cargo_skin_pk4_path.display().to_string(),
        inspection: inspect_cargo_skin_pk4(cargo_skin_pk4_path)?,
    };
    println!("{}", serde_json::to_string_pretty(&report)?);
    Ok(())
}

pub(crate) fn inspect_cargo_economy_sources(
    cargo_types_dir: &Path,
    cargo_skin_pk4_path: &Path,
) -> Result<(), Box<dyn std::error::Error>> {
    let cargo_bindings_path =
        Path::new("artifacts/exports/rt3-1.06/event-effects-cargo-bindings.json");
    let report = RuntimeCargoEconomyInspectionOutput {
        cargo_types_dir: cargo_types_dir.display().to_string(),
        cargo_skin_pk4_path: cargo_skin_pk4_path.display().to_string(),
        inspection: inspect_cargo_economy_sources_with_bindings(
            cargo_types_dir,
            cargo_skin_pk4_path,
            Some(cargo_bindings_path),
        )?,
    };
    println!("{}", serde_json::to_string_pretty(&report)?);
    Ok(())
}

pub(crate) fn inspect_cargo_production_selector(
    cargo_types_dir: &Path,
    cargo_skin_pk4_path: &Path,
) -> Result<(), Box<dyn std::error::Error>> {
    let cargo_bindings_path =
        Path::new("artifacts/exports/rt3-1.06/event-effects-cargo-bindings.json");
    let inspection = inspect_cargo_economy_sources_with_bindings(
        cargo_types_dir,
        cargo_skin_pk4_path,
        Some(cargo_bindings_path),
    )?;
    let selector = inspection
        .production_selector
        .ok_or("named cargo production selector is not available in the checked-in bindings")?;
    let report = RuntimeCargoSelectorInspectionOutput {
        cargo_types_dir: cargo_types_dir.display().to_string(),
        cargo_skin_pk4_path: cargo_skin_pk4_path.display().to_string(),
        selector,
    };
    println!("{}", serde_json::to_string_pretty(&report)?);
    Ok(())
}

pub(crate) fn inspect_cargo_price_selector(
    cargo_types_dir: &Path,
    cargo_skin_pk4_path: &Path,
) -> Result<(), Box<dyn std::error::Error>> {
    let cargo_bindings_path =
        Path::new("artifacts/exports/rt3-1.06/event-effects-cargo-bindings.json");
    let inspection = inspect_cargo_economy_sources_with_bindings(
        cargo_types_dir,
        cargo_skin_pk4_path,
        Some(cargo_bindings_path),
    )?;
    let report = RuntimeCargoSelectorInspectionOutput {
        cargo_types_dir: cargo_types_dir.display().to_string(),
        cargo_skin_pk4_path: cargo_skin_pk4_path.display().to_string(),
        selector: inspection.price_selector,
    };
    println!("{}", serde_json::to_string_pretty(&report)?);
    Ok(())
}

pub(crate) fn inspect_win(win_path: &Path) -> Result<(), Box<dyn std::error::Error>> {
    let report = RuntimeWinInspectionOutput {
        path: win_path.display().to_string(),
        inspection: inspect_win_file(win_path)?,
    };
    println!("{}", serde_json::to_string_pretty(&report)?);
    Ok(())
}

pub(crate) fn extract_pk4_entry(
    pk4_path: &Path,
    entry_name: &str,
    output_path: &Path,
) -> Result<(), Box<dyn std::error::Error>> {
    let report = RuntimePk4ExtractionOutput {
        path: pk4_path.display().to_string(),
        output_path: output_path.display().to_string(),
        extraction: extract_pk4_entry_file(pk4_path, entry_name, output_path)?,
    };
    println!("{}", serde_json::to_string_pretty(&report)?);
    Ok(())
}

pub(crate) fn inspect_campaign_exe(exe_path: &Path) -> Result<(), Box<dyn std::error::Error>> {
    let report = RuntimeCampaignExeInspectionOutput {
        path: exe_path.display().to_string(),
        inspection: inspect_campaign_exe_file(exe_path)?,
    };
    println!("{}", serde_json::to_string_pretty(&report)?);
    Ok(())
}

pub(crate) fn export_profile_block(
    smp_path: &Path,
    output_path: &Path,
) -> Result<(), Box<dyn std::error::Error>> {
    let inspection = inspect_smp_file(smp_path)?;
    let document = build_profile_block_export_document(smp_path, &inspection)?;
    let bytes = serde_json::to_vec_pretty(&document)?;
    fs::write(output_path, bytes)?;
    let report = RuntimeProfileBlockExportReport {
        output_path: output_path.display().to_string(),
        profile_kind: document.profile_kind,
        profile_family: document.profile_family,
    };
    println!("{}", serde_json::to_string_pretty(&report)?);
    Ok(())
}
