use crate::derived::runtime_company_bond_interest_rate_quote;
use crate::engine::service::annual_finance::constants::COMPANY_STOCK_AND_BOND_CAPITAL_POST_SCALE;
use crate::engine::service::{
    service_post_company_stat_delta, service_repay_matured_company_live_bonds_and_compact,
};
use crate::event::metrics::RUNTIME_COMPANY_STAT_SLOT_CURRENT_CASH;
use crate::state::{RuntimeCompanyBondSlot, RuntimeState};

pub(super) fn apply_bond_issue(state: &mut RuntimeState, company_id: u32) -> bool {
    let Some(bond_state) =
        crate::derived::runtime_company_annual_bond_policy_state(state, company_id)
    else {
        return false;
    };
    if !bond_state.eligible_for_bond_issue_branch {
        return false;
    }

    let mut mutated = false;
    let retired_principal_total =
        service_repay_matured_company_live_bonds_and_compact(state, company_id).unwrap_or(0);
    mutated |= retired_principal_total > 0;
    state.service_state.annual_bond_last_retired_principal_total = state
        .service_state
        .annual_bond_last_retired_principal_total
        .saturating_add(retired_principal_total);

    let issue_bond_count = bond_state.proposed_issue_bond_count.unwrap_or(0);
    let Some(principal) = bond_state.issue_principal_step else {
        return mutated;
    };
    let Some(years_to_maturity) = bond_state.proposed_issue_years_to_maturity else {
        return mutated;
    };
    let Some(maturity_year) = state
        .world_restore
        .packed_year_word_raw_u16
        .map(u32::from)
        .and_then(|year| year.checked_add(years_to_maturity))
    else {
        return mutated;
    };

    for _ in 0..issue_bond_count {
        let Some(quote_rate) = runtime_company_bond_interest_rate_quote(
            state,
            company_id,
            principal,
            years_to_maturity,
        ) else {
            break;
        };

        mutated |= service_post_company_stat_delta(
            state,
            company_id,
            0x0c,
            (principal as f64) * COMPANY_STOCK_AND_BOND_CAPITAL_POST_SCALE,
            true,
        );
        mutated |=
            service_post_company_stat_delta(state, company_id, 0x12, -(principal as f64), false);
        mutated |= service_post_company_stat_delta(
            state,
            company_id,
            RUNTIME_COMPANY_STAT_SLOT_CURRENT_CASH,
            principal as f64,
            false,
        );

        let Some(market_state) = state
            .service_state
            .company_market_state
            .get_mut(&company_id)
        else {
            break;
        };
        let slot_index = market_state.bond_count as u32;
        if market_state.bond_count == u8::MAX {
            break;
        }
        market_state.live_bond_slots.push(RuntimeCompanyBondSlot {
            slot_index,
            principal,
            maturity_year,
            coupon_rate_raw_u32: (quote_rate as f32).to_bits(),
        });
        market_state.bond_count = market_state.bond_count.saturating_add(1);
        market_state.largest_live_bond_principal = Some(
            market_state
                .largest_live_bond_principal
                .unwrap_or(0)
                .max(principal),
        );
        market_state.highest_coupon_live_bond_principal = market_state
            .live_bond_slots
            .iter()
            .filter_map(|slot| {
                let coupon = f32::from_bits(slot.coupon_rate_raw_u32) as f64;
                coupon.is_finite().then_some((coupon, slot.principal))
            })
            .max_by(|left, right| {
                left.0
                    .partial_cmp(&right.0)
                    .unwrap_or(std::cmp::Ordering::Equal)
            })
            .map(|(_, principal)| principal);
        state.service_state.annual_bond_last_issued_principal_total = state
            .service_state
            .annual_bond_last_issued_principal_total
            .saturating_add(u64::from(principal));
    }
    if let Some(company) = state
        .companies
        .iter_mut()
        .find(|company| company.company_id == company_id)
    {
        company.debt = state
            .service_state
            .company_market_state
            .get(&company_id)
            .map(|market_state| {
                market_state
                    .live_bond_slots
                    .iter()
                    .map(|slot| u64::from(slot.principal))
                    .sum::<u64>()
            })
            .unwrap_or(company.debt);
    }
    mutated
}
