use std::fs;
use std::io;
use std::path::{Path, PathBuf};

use rrt_model::finance::{
    AnnualFinancePolicy, BondPosition, CompanyFinanceState, FinanceOutcome, FinanceSnapshot,
};
use serde::Serialize;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FinanceLogPaths {
    pub snapshot_path: PathBuf,
    pub outcome_path: PathBuf,
}

pub fn sample_finance_snapshot() -> FinanceSnapshot {
    FinanceSnapshot {
        policy: AnnualFinancePolicy {
            dividends_allowed: false,
            ..AnnualFinancePolicy::default()
        },
        company: CompanyFinanceState {
            current_cash: 100_000,
            support_adjusted_share_price: 27.5,
            book_value_per_share: 20.0,
            outstanding_share_count: 60_000,
            recent_net_profits: [40_000, 30_000, 20_000],
            recent_revenue_totals: [250_000, 240_000, 230_000],
            bonds: vec![
                BondPosition {
                    principal: 150_000,
                    coupon_rate: 0.12,
                    years_remaining: 12,
                },
                BondPosition {
                    principal: 10_000,
                    coupon_rate: 0.10,
                    years_remaining: 10,
                },
            ],
            ..CompanyFinanceState::default()
        },
    }
}

pub fn write_finance_snapshot_bundle(
    base_dir: &Path,
    stem: &str,
    snapshot: &FinanceSnapshot,
) -> io::Result<FinanceLogPaths> {
    fs::create_dir_all(base_dir)?;

    let snapshot_path = base_dir.join(format!("rrt_finance_{stem}_snapshot.json"));
    let outcome_path = base_dir.join(format!("rrt_finance_{stem}_outcome.json"));
    let outcome: FinanceOutcome = snapshot.evaluate();

    let snapshot_json = serde_json::to_vec_pretty(snapshot)
        .map_err(|err| io::Error::other(format!("serialize snapshot: {err}")))?;
    let outcome_json = serde_json::to_vec_pretty(&outcome)
        .map_err(|err| io::Error::other(format!("serialize outcome: {err}")))?;

    fs::write(&snapshot_path, snapshot_json)?;
    fs::write(&outcome_path, outcome_json)?;

    Ok(FinanceLogPaths {
        snapshot_path,
        outcome_path,
    })
}

pub fn write_finance_snapshot_only(
    base_dir: &Path,
    stem: &str,
    snapshot: &FinanceSnapshot,
) -> io::Result<PathBuf> {
    fs::create_dir_all(base_dir)?;

    let snapshot_path = base_dir.join(format!("rrt_finance_{stem}_snapshot.json"));
    let snapshot_json = serde_json::to_vec_pretty(snapshot)
        .map_err(|err| io::Error::other(format!("serialize snapshot: {err}")))?;
    fs::write(&snapshot_path, snapshot_json)?;

    Ok(snapshot_path)
}

#[derive(Debug, Clone, Serialize)]
pub struct IndexedCollectionProbeRow {
    pub entry_id: usize,
    pub live: bool,
    pub resolved_ptr: usize,
    pub active_flag: Option<u8>,
}

#[derive(Debug, Clone, Serialize)]
pub struct IndexedCollectionProbe {
    pub collection_addr: usize,
    pub flat_payload: bool,
    pub stride: u32,
    pub id_bound: i32,
    pub payload_ptr: usize,
    pub tombstone_ptr: usize,
    pub first_rows: Vec<IndexedCollectionProbeRow>,
}

pub fn write_indexed_collection_probe(
    base_dir: &Path,
    stem: &str,
    probe: &IndexedCollectionProbe,
) -> io::Result<PathBuf> {
    fs::create_dir_all(base_dir)?;

    let path = base_dir.join(format!("rrt_finance_{stem}_collection_probe.json"));
    let json = serde_json::to_vec_pretty(probe)
        .map_err(|err| io::Error::other(format!("serialize collection probe: {err}")))?;
    fs::write(&path, json)?;

    Ok(path)
}

#[derive(Debug, Clone, Serialize)]
pub struct CargoCollectionProbeRow {
    pub entry_id: usize,
    pub live: bool,
    pub resolved_ptr: usize,
    pub stem: Option<String>,
    pub route_style_byte: Option<u8>,
    pub subtype_byte: Option<u8>,
    pub class_marker: Option<u32>,
}

#[derive(Debug, Clone, Serialize)]
pub struct CargoCollectionProbe {
    pub collection_addr: usize,
    pub flat_payload: bool,
    pub stride: u32,
    pub id_bound: i32,
    pub payload_ptr: usize,
    pub tombstone_ptr: usize,
    pub live_entry_count: usize,
    pub rows: Vec<CargoCollectionProbeRow>,
}

pub fn write_cargo_collection_probe(
    base_dir: &Path,
    stem: &str,
    probe: &CargoCollectionProbe,
) -> io::Result<PathBuf> {
    fs::create_dir_all(base_dir)?;

    let path = base_dir.join(format!("rrt_cargo_{stem}_collection_probe.json"));
    let json = serde_json::to_vec_pretty(probe)
        .map_err(|err| io::Error::other(format!("serialize cargo collection probe: {err}")))?;
    fs::write(&path, json)?;

    Ok(path)
}
