//! Financial functions: compound interest and mortgage payment.
//!
//! ## compound_interest(principal, rate, periods)
//!
//! Returns `principal * (1 + rate)^periods`.
//! - `principal` — initial investment / loan amount
//! - `rate`      — interest rate per period (e.g. 0.05 for 5%)
//! - `periods`   — number of compounding periods
//!
//! ## mortgage_payment(principal, annual_rate, years)
//!
//! Returns the monthly payment for a fixed-rate mortgage using the standard
//! amortization formula:
//!
//!   M = P * [r(1+r)^n] / [(1+r)^n - 1]
//!
//! where `r` = `annual_rate / 12` and `n` = `years * 12`.
//!
//! If the rate is 0, returns `principal / (years * 12)`.

use super::{FunctionError, FunctionRegistry};

/// compound_interest(principal, rate, periods)
fn compound_interest_fn(args: &[f64]) -> Result<f64, FunctionError> {
    let principal = args[0];
    let rate = args[1];
    let periods = args[2];

    if rate < -1.0 {
        return Err(FunctionError::new(
            "Interest rate must be >= -1 (i.e. at most a 100% loss per period)",
        ));
    }

    Ok(principal * (1.0 + rate).powf(periods))
}

/// mortgage_payment(principal, annual_rate, years)
fn mortgage_payment_fn(args: &[f64]) -> Result<f64, FunctionError> {
    let principal = args[0];
    let annual_rate = args[1];
    let years = args[2];

    if principal < 0.0 {
        return Err(FunctionError::new("Principal must be non-negative"));
    }
    if annual_rate < 0.0 {
        return Err(FunctionError::new("Annual rate must be non-negative"));
    }
    if years <= 0.0 {
        return Err(FunctionError::new("Loan term must be positive"));
    }

    let n = years * 12.0; // total monthly payments

    if annual_rate == 0.0 {
        // No interest — just divide evenly.
        return Ok(principal / n);
    }

    let r = annual_rate / 12.0; // monthly rate
    let factor = (1.0 + r).powf(n);
    let payment = principal * (r * factor) / (factor - 1.0);
    Ok(payment)
}

/// Register financial functions.
pub fn register(reg: &mut FunctionRegistry) {
    reg.register_fixed("compound_interest", 3, compound_interest_fn);
    reg.register_fixed("mortgage_payment", 3, mortgage_payment_fn);
}

#[cfg(test)]
mod tests {
    use super::*;

    fn reg() -> FunctionRegistry {
        FunctionRegistry::new()
    }

    // --- compound_interest ---

    #[test]
    fn compound_interest_basic() {
        // $1000 at 5% for 10 years => 1000 * 1.05^10 = 1628.89...
        let v = reg()
            .call("compound_interest", &[1000.0, 0.05, 10.0])
            .unwrap();
        assert!((v - 1628.894627).abs() < 0.01);
    }

    #[test]
    fn compound_interest_zero_rate() {
        let v = reg()
            .call("compound_interest", &[1000.0, 0.0, 10.0])
            .unwrap();
        assert!((v - 1000.0).abs() < 1e-10);
    }

    #[test]
    fn compound_interest_zero_periods() {
        let v = reg()
            .call("compound_interest", &[1000.0, 0.05, 0.0])
            .unwrap();
        assert!((v - 1000.0).abs() < 1e-10);
    }

    #[test]
    fn compound_interest_one_period() {
        let v = reg()
            .call("compound_interest", &[1000.0, 0.1, 1.0])
            .unwrap();
        assert!((v - 1100.0).abs() < 1e-10);
    }

    #[test]
    fn compound_interest_negative_rate_too_low() {
        let err = reg()
            .call("compound_interest", &[1000.0, -1.5, 1.0])
            .unwrap_err();
        assert!(err.message.contains("rate"));
    }

    // --- mortgage_payment ---

    #[test]
    fn mortgage_payment_standard() {
        // $200,000 at 6% annual for 30 years => ~$1199.10/month
        let v = reg()
            .call("mortgage_payment", &[200_000.0, 0.06, 30.0])
            .unwrap();
        assert!((v - 1199.10).abs() < 0.02);
    }

    #[test]
    fn mortgage_payment_zero_rate() {
        // $120,000 at 0% for 10 years => $1000/month
        let v = reg()
            .call("mortgage_payment", &[120_000.0, 0.0, 10.0])
            .unwrap();
        assert!((v - 1000.0).abs() < 1e-10);
    }

    #[test]
    fn mortgage_payment_short_term() {
        // $12,000 at 12% annual for 1 year => ~$1066.19/month
        let v = reg()
            .call("mortgage_payment", &[12_000.0, 0.12, 1.0])
            .unwrap();
        assert!((v - 1066.19).abs() < 0.02);
    }

    #[test]
    fn mortgage_payment_negative_principal_error() {
        let err = reg()
            .call("mortgage_payment", &[-1000.0, 0.05, 10.0])
            .unwrap_err();
        assert!(err.message.contains("Principal"));
    }

    #[test]
    fn mortgage_payment_negative_rate_error() {
        let err = reg()
            .call("mortgage_payment", &[1000.0, -0.05, 10.0])
            .unwrap_err();
        assert!(err.message.contains("rate"));
    }

    #[test]
    fn mortgage_payment_zero_years_error() {
        let err = reg()
            .call("mortgage_payment", &[1000.0, 0.05, 0.0])
            .unwrap_err();
        assert!(err.message.contains("term"));
    }

    #[test]
    fn mortgage_payment_arity_error() {
        let err = reg()
            .call("mortgage_payment", &[1000.0, 0.05])
            .unwrap_err();
        assert!(err.message.contains("expects 3 argument"));
    }
}
