use crate::derived::{
    runtime_company_annual_finance_state, runtime_company_average_live_bond_coupon,
    runtime_company_control_transfer_stat_value_f64, runtime_company_derived_stat_value,
    runtime_round_f64_to_i64, runtime_world_issue_opinion_term_sum_raw,
    runtime_world_prime_rate_baseline,
};
use crate::event::metrics::{
    RUNTIME_COMPANY_STAT_FAMILY_CONTROL_TRANSFER, RUNTIME_WORLD_ISSUE_CREDIT_MARKET,
    RUNTIME_WORLD_ISSUE_PRIME_RATE,
};
use crate::state::RuntimeState;

pub fn runtime_company_credit_rating(state: &RuntimeState, company_id: u32) -> Option<i64> {
    let company = state
        .companies
        .iter()
        .find(|company| company.company_id == company_id)?;
    if let Some(credit_rating_score) = company.credit_rating_score {
        return Some(credit_rating_score);
    }

    let annual_finance_state = runtime_company_annual_finance_state(state, company_id)?;
    if annual_finance_state.outstanding_shares == 0 {
        return Some(-512);
    }

    let mut weighted_recent_profit_total = 0.0f64;
    let mut weighted_recent_profit_weight = 0.0f64;
    for (index, (net_profit, fuel_cost)) in annual_finance_state
        .trailing_full_year_net_profits
        .iter()
        .zip(annual_finance_state.trailing_full_year_fuel_costs.iter())
        .take(4)
        .enumerate()
    {
        let weight = (4 - index) as f64;
        weighted_recent_profit_total += (*net_profit - *fuel_cost) as f64 * weight;
        weighted_recent_profit_weight += weight;
    }
    let weighted_recent_profit = if weighted_recent_profit_weight > 0.0 {
        weighted_recent_profit_total / weighted_recent_profit_weight
    } else {
        0.0
    };

    let current_slot_12 = runtime_company_control_transfer_stat_value_f64(state, company_id, 0x12)?;
    let current_slot_30 = runtime_company_derived_stat_value(
        state,
        company_id,
        RUNTIME_COMPANY_STAT_FAMILY_CONTROL_TRANSFER,
        0x30,
    )?;
    let current_slot_31 = runtime_company_derived_stat_value(
        state,
        company_id,
        RUNTIME_COMPANY_STAT_FAMILY_CONTROL_TRANSFER,
        0x31,
    )?;
    let average_live_bond_coupon = runtime_company_average_live_bond_coupon(state, company_id)?;

    let mut finance_pressure = average_live_bond_coupon * current_slot_12;
    if company.current_cash > 0 {
        let prime_baseline = runtime_world_prime_rate_baseline(state)?;
        let raw_issue_39 = runtime_world_issue_opinion_term_sum_raw(
            state,
            RUNTIME_WORLD_ISSUE_PRIME_RATE,
            company.linked_chairman_profile_id,
            Some(company_id),
            None,
        )? as f64;
        finance_pressure +=
            company.current_cash as f64 * (prime_baseline + raw_issue_39 * 0.01 + 0.03);
    }

    let profitability_ratio = if finance_pressure < 0.0 {
        weighted_recent_profit / (-finance_pressure)
    } else {
        10.0
    };
    let mut profitability_score = runtime_credit_rating_profitability_ladder(profitability_ratio);
    if let Some(years_since_founding) = annual_finance_state.years_since_founding {
        if years_since_founding < 5 {
            let missing_years = (5 - years_since_founding) as f64;
            profitability_score += (10.0 - profitability_score) * missing_years * 0.1;
        }
    }
    if current_slot_31 > 1_000_000.0 {
        profitability_score += current_slot_31 / 100_000.0 - 10.0;
    }

    let burden_ratio = if current_slot_30 > 0.0 {
        (weighted_recent_profit - current_slot_12) / current_slot_30
    } else {
        1.0
    };
    let burden_score = runtime_credit_rating_burden_ladder(burden_ratio);

    let mut rating =
        (profitability_score * burden_score / 10.0 + profitability_score + burden_score) / 3.0;
    rating *= runtime_world_credit_market_scale(state)?;
    if let Some(years_since_last_bankruptcy) = annual_finance_state.years_since_last_bankruptcy {
        if years_since_last_bankruptcy < 15 {
            rating *= years_since_last_bankruptcy as f64 * 0.0666;
        }
    }

    let raw_issue_38 = runtime_world_issue_opinion_term_sum_raw(
        state,
        RUNTIME_WORLD_ISSUE_CREDIT_MARKET,
        company.linked_chairman_profile_id,
        Some(company_id),
        None,
    )? as f64;
    runtime_round_f64_to_i64((rating + raw_issue_38 + 0.5).clamp(0.0, 10.0))
}

pub(super) fn runtime_credit_rating_profitability_ladder(ratio: f64) -> f64 {
    if !ratio.is_finite() || ratio <= 0.0 {
        0.0
    } else if ratio < 1.0 {
        1.0 + ratio * 4.0
    } else if ratio < 2.0 {
        3.0 + ratio * 2.0
    } else if ratio < 5.0 {
        5.0 + ratio
    } else {
        10.0
    }
}

pub(super) fn runtime_credit_rating_burden_ladder(ratio: f64) -> f64 {
    if !ratio.is_finite() || ratio > 1.0 {
        0.0
    } else if ratio > 0.75 {
        16.0 - ratio * 16.0
    } else if ratio > 0.5 {
        13.0 - ratio * 12.0
    } else if ratio > 0.25 {
        11.0 - ratio * 8.0
    } else if ratio > 0.1 {
        10.0 - ratio * 4.0
    } else {
        10.0
    }
}

pub fn runtime_world_credit_market_scale(state: &RuntimeState) -> Option<f64> {
    const ISSUE_38_SCALE_TABLE: [f64; 8] = [0.8, 0.9, 1.0, 1.1, 1.2, 0.9, 0.95, 1.0];
    let index = state.world_restore.issue_38_value? as usize;
    ISSUE_38_SCALE_TABLE.get(index).copied()
}
