#![allow(clippy::unwrap_used)]

use crate::{cell::CellValue, test::util::new_empty_model};

#[test]
fn fn_arguments() {
    let mut model = new_empty_model();
    model._set("A1", "=PMT()");
    model._set("A2", "=PMT(1,1)");
    model._set("A3", "=PMT(1,1,1,1,1,1)");

    model._set("B1", "=FV()");
    model._set("B2", "=FV(1,1)");
    model._set("B3", "=FV(1,1,1,1,1,1)");

    model._set("C1", "=PV()");
    model._set("C2", "=PV(1,1)");
    model._set("C3", "=PV(1,1,1,1,1,1)");

    model._set("D1", "=NPER()");
    model._set("D2", "=NPER(1,1)");
    model._set("D3", "=NPER(1,1,1,1,1,1)");

    model._set("E1", "=RATE()");
    model._set("E2", "=RATE(1,1)");
    model._set("E3", "=RATE(1,1,1,1,1,1)");

    model.evaluate();

    assert_eq!(model._get_text("A1"), *"#ERROR!");
    assert_eq!(model._get_text("A2"), *"#ERROR!");
    assert_eq!(model._get_text("A3"), *"#ERROR!");

    assert_eq!(model._get_text("B1"), *"#ERROR!");
    assert_eq!(model._get_text("B2"), *"#ERROR!");
    assert_eq!(model._get_text("B3"), *"#ERROR!");

    assert_eq!(model._get_text("C1"), *"#ERROR!");
    assert_eq!(model._get_text("C2"), *"#ERROR!");
    assert_eq!(model._get_text("C3"), *"#ERROR!");

    assert_eq!(model._get_text("D1"), *"#ERROR!");
    assert_eq!(model._get_text("D2"), *"#ERROR!");
    assert_eq!(model._get_text("D3"), *"#ERROR!");

    assert_eq!(model._get_text("E1"), *"#ERROR!");
    assert_eq!(model._get_text("E2"), *"#ERROR!");
    assert_eq!(model._get_text("E3"), *"#ERROR!");
}

#[test]
fn fn_impmt_ppmt_arguments() {
    let mut model = new_empty_model();
    model._set("A1", "=IPMT()");
    model._set("A2", "=IPMT(1,1,1)");
    model._set("A3", "=IPMT(1,1,1,1,1,1,1)");

    model._set("B1", "=PPMT()");
    model._set("B2", "=PPMT(1,1,1)");
    model._set("B3", "=PPMT(1,1,1,1,1,1,1)");

    model.evaluate();

    assert_eq!(model._get_text("A1"), *"#ERROR!");
    assert_eq!(model._get_text("A2"), *"#ERROR!");
    assert_eq!(model._get_text("A3"), *"#ERROR!");

    assert_eq!(model._get_text("B1"), *"#ERROR!");
    assert_eq!(model._get_text("B2"), *"#ERROR!");
    assert_eq!(model._get_text("B3"), *"#ERROR!");
}

#[test]
fn fn_irr_npv_arguments() {
    let mut model = new_empty_model();
    model._set("A1", "=NPV()");
    model._set("A2", "=NPV(1,1)");

    model._set("C1", "-2"); // v0
    model._set("C2", "5"); // v1
    model._set("B1", "=IRR()");
    model._set("B3", "=IRR(1, 2, 3, 4)");
    // r such that v0 + v1/(1+r) = 0
    // r = -v1/v0 - 1
    model._set("B4", "=IRR(C1:C2)");

    model.evaluate();

    assert_eq!(model._get_text("A1"), *"#ERROR!");
    assert_eq!(model._get_text("A2"), *"$0.50");

    assert_eq!(model._get_text("B1"), *"#ERROR!");
    assert_eq!(model._get_text("B3"), *"#ERROR!");
    // r = 5/2-1 = 1.5
    assert_eq!(model._get_text("B4"), *"150%");
}

#[test]
fn fn_mirr() {
    let mut model = new_empty_model();
    model._set("A2", "-120000");
    model._set("A3", "39000");
    model._set("A4", "30000");
    model._set("A5", "21000");
    model._set("A6", "37000");
    model._set("A7", "46000");
    model._set("A8", "0.1");
    model._set("A9", "0.12");

    model._set("B1", "=MIRR(A2:A7, A8, A9)");
    model._set("B2", "=MIRR(A2:A5, A8, A9)");

    model.evaluate();
    assert_eq!(
        model.get_cell_value_by_ref("Sheet1!B1"),
        Ok(CellValue::Number(0.1260941303659051))
    );
    assert_eq!(model._get_text("B1"), "13%");
    assert_eq!(model._get_text("B2"), "-5%");
}

#[test]
fn fn_mirr_div_0() {
    // This test produces #DIV/0! in Excel (but it is incorrect)
    let mut model = new_empty_model();
    model._set("A2", "-30");
    model._set("A3", "-20");
    model._set("A4", "-10");
    model._set("A5", "5");
    model._set("A6", "5");
    model._set("A7", "5");
    model._set("A8", "-1");
    model._set("A9", "2");

    model._set("B1", "=MIRR(A2:A7, A8, A9)");

    model.evaluate();
    assert_eq!(
        model.get_cell_value_by_ref("Sheet1!B1"),
        Ok(CellValue::Number(-1.0))
    );
    assert_eq!(model._get_text("B1"), "-100%");
}

#[test]
fn fn_ispmt() {
    let mut model = new_empty_model();
    model._set("A1", "1"); // rate
    model._set("A2", "2"); // per
    model._set("A3", "5"); // nper
    model._set("A4", "4"); // pv

    model._set("B1", "=ISPMT(A1, A2, A3, A4)");
    model._set("B2", "=ISPMT(A1, A2, A3, A4, 1)");
    model._set("B3", "=ISPMT(A1, A2, A3)");

    model.evaluate();

    assert_eq!(model._get_text("B1"), "-2.4");
    assert_eq!(model._get_text("B2"), *"#ERROR!");
    assert_eq!(model._get_text("B3"), *"#ERROR!");
}

#[test]
fn fn_rri() {
    let mut model = new_empty_model();
    model._set("A1", "1"); // nper
    model._set("A2", "2"); // pv
    model._set("A3", "3"); // fv

    model._set("B1", "=RRI(A1, A2, A3)");
    model._set("B2", "=RRI(A1, A2)");
    model._set("B3", "=RRI(A1, A2, A3, 1)");

    model.evaluate();

    assert_eq!(model._get_text("B1"), "0.5");
    assert_eq!(model._get_text("B2"), *"#ERROR!");
    assert_eq!(model._get_text("B3"), *"#ERROR!");
}

#[test]
fn fn_sln() {
    let mut model = new_empty_model();
    model._set("A1", "1"); // cost
    model._set("A2", "2"); // salvage
    model._set("A3", "3"); // life

    model._set("B1", "=SLN(A1, A2, A3)");
    model._set("B2", "=SLN(A1, A2)");
    model._set("B3", "=SLN(A1, A2, A3, 1)");

    model.evaluate();

    assert_eq!(
        model.get_cell_value_by_ref("Sheet1!B1"),
        Ok(CellValue::Number(-1.0 / 3.0))
    );
    assert_eq!(model._get_text("B1"), "-$0.33");
    assert_eq!(model._get_text("B2"), *"#ERROR!");
    assert_eq!(model._get_text("B3"), *"#ERROR!");
}

#[test]
fn fn_syd() {
    let mut model = new_empty_model();
    model._set("A1", "100"); // cost
    model._set("A2", "5"); // salvage
    model._set("A3", "20"); // life
    model._set("A4", "10"); // periods

    model._set("B1", "=SYD(A1, A2, A3, A4)");
    model._set("B2", "=SYD(A1, A2, A3)");
    model._set("B3", "=SYD(A1, A2, A3, A4, 1)");

    model.evaluate();

    assert_eq!(
        model.get_cell_value_by_ref("Sheet1!B1"),
        Ok(CellValue::Number(4.976190476190476))
    );
    assert_eq!(model._get_text("B1"), "$4.98");
    assert_eq!(model._get_text("B2"), *"#ERROR!");
    assert_eq!(model._get_text("B3"), *"#ERROR!");
}

#[test]
fn fn_effect() {
    let mut model = new_empty_model();
    model._set("A1", "2"); // rate
    model._set("A2", "1"); // periods

    model._set("B1", "=EFFECT(A1, A2)");
    model._set("B2", "=EFFECT(A1)");
    model._set("B3", "=EFFECT(A1, A2, A3)");

    model.evaluate();

    assert_eq!(model._get_text("B1"), "2");
    assert_eq!(model._get_text("B2"), *"#ERROR!");
    assert_eq!(model._get_text("B3"), *"#ERROR!");
}

#[test]
fn fn_nominal() {
    let mut model = new_empty_model();
    model._set("A1", "2"); // rate
    model._set("A2", "1"); // periods

    model._set("B1", "=NOMINAL(A1, A2)");
    model._set("B2", "=NOMINAL(A1)");
    model._set("B3", "=NOMINAL(A1, A2, A3)");

    model.evaluate();

    assert_eq!(model._get_text("B1"), "2");
    assert_eq!(model._get_text("B2"), *"#ERROR!");
    assert_eq!(model._get_text("B3"), *"#ERROR!");
}

#[test]
fn fn_db() {
    let mut model = new_empty_model();
    model._set("A2", "$1,000,000"); // cost
    model._set("A3", "$100,000"); // salvage
    model._set("A4", "6"); // life

    model._set("B1", "=DB(A2,A3,A4,1,7)");
    model._set("B2", "=DB(A2,A3,A4,2,7)");
    model._set("B3", "=DB(A2,A3,A4,3,7)");
    model._set("B4", "=DB(A2,A3,A4,4,7)");
    model._set("B5", "=DB(A2,A3,A4,5,7)");
    model._set("B6", "=DB(A2,A3,A4,6,7)");
    model._set("B7", "=DB(A2,A3,A4,7,7)");

    model._set("C1", "=DB(A2,A3,A4,7,7,1)");
    model._set("C2", "=DB(A2,A3,A4)");

    model.evaluate();

    assert_eq!(model._get_text("B1"), "$186,083.33");
    assert_eq!(model._get_text("B2"), "$259,639.42");
    assert_eq!(model._get_text("B3"), "$176,814.44");
    assert_eq!(model._get_text("B4"), "$120,410.64");
    assert_eq!(model._get_text("B5"), "$81,999.64");
    assert_eq!(model._get_text("B6"), "$55,841.76");
    assert_eq!(model._get_text("B7"), "$15,845.10");

    assert_eq!(model._get_text("C1"), *"#ERROR!");
    assert_eq!(model._get_text("C2"), *"#ERROR!");
}

#[test]
fn fn_ddb() {
    let mut model = new_empty_model();
    model._set("A2", "$2,400"); // cost
    model._set("A3", "$300"); // salvage
    model._set("A4", "10"); // life

    model._set("B1", "=DDB(A2,A3,A4*365,1)");
    model._set("B2", "=DDB(A2,A3,A4*12,1,2)");
    model._set("B3", "=DDB(A2,A3,A4,1,2)");
    model._set("B4", "=DDB(A2,A3,A4,2,1.5)");
    model._set("B5", "=DDB(A2,A3,A4,10)");

    model._set("C1", "=DB(A2,A3,A4,7,7,1)");
    model._set("C2", "=DB(A2,A3,A4)");

    model.evaluate();

    assert_eq!(model._get_text("B1"), "$1.32");
    assert_eq!(model._get_text("B2"), "$40.00");
    assert_eq!(model._get_text("B3"), "$480.00");
    assert_eq!(model._get_text("B4"), "$306.00");
    assert_eq!(model._get_text("B5"), "$22.12");

    assert_eq!(model._get_text("C1"), *"#ERROR!");
    assert_eq!(model._get_text("C2"), *"#ERROR!");
}

#[test]
fn fn_tbilleq() {
    let mut model = new_empty_model();
    model._set("A2", "=DATE(2008, 3, 31)"); // settlement date
    model._set("A3", "=DATE(2008, 6, 1)"); // maturity date
    model._set("A4", "9.14%");

    model._set("B1", "=TBILLEQ(A2,A3,A4)");

    model._set("C1", "=TBILLEQ(A2,A3)");
    model._set("C2", "=TBILLEQ(A2,A3,A4,1)");

    model.evaluate();

    assert_eq!(model._get_text("B1"), "9.42%");

    assert_eq!(model._get_text("C1"), *"#ERROR!");
    assert_eq!(model._get_text("C2"), *"#ERROR!");
}

#[test]
fn fn_tbillprice() {
    let mut model = new_empty_model();
    model._set("A2", "=DATE(2008, 3, 31)"); // settlement date
    model._set("A3", "=DATE(2008, 6, 1)"); // maturity date
    model._set("A4", "9.0%");

    model._set("B1", "=TBILLPRICE(A2,A3,A4)");

    model._set("C1", "=TBILLPRICE(A2,A3)");
    model._set("C2", "=TBILLPRICE(A2,A3,A4,1)");

    model.evaluate();

    assert_eq!(model._get_text("B1"), "$98.45");

    assert_eq!(model._get_text("C1"), *"#ERROR!");
    assert_eq!(model._get_text("C2"), *"#ERROR!");
}

#[test]
fn fn_tbillyield() {
    let mut model = new_empty_model();
    model._set("A2", "=DATE(2008, 3, 31)"); // settlement date
    model._set("A3", "=DATE(2008, 6, 1)"); // maturity date
    model._set("A4", "$98.45");

    model._set("B1", "=TBILLYIELD(A2,A3,A4)");

    model._set("C1", "=TBILLYIELD(A2,A3)");
    model._set("C2", "=TBILLYIELD(A2,A3,A4,1)");

    model.evaluate();

    assert_eq!(model._get_text("B1"), "9.14%");

    assert_eq!(model._get_text("C1"), *"#ERROR!");
    assert_eq!(model._get_text("C2"), *"#ERROR!");
}

#[test]
fn fn_dollarde() {
    let mut model = new_empty_model();
    model._set("A1", "=DOLLARDE(1.02, 16)");
    model._set("A2", "=DOLLARDE(1.1, 32)");

    model._set("C1", "=DOLLARDE(1.1)");
    model._set("C2", "=DOLLARDE(1.1, 32, 1)");

    model.evaluate();

    assert_eq!(model._get_text("A1"), "1.125");
    assert_eq!(model._get_text("A2"), "1.3125");

    assert_eq!(model._get_text("C1"), *"#ERROR!");
    assert_eq!(model._get_text("C2"), *"#ERROR!");
}

#[test]
fn fn_dollarfr() {
    let mut model = new_empty_model();
    model._set("A1", "=DOLLARFR(1.125,16)");
    model._set("A2", "=DOLLARFR(1.125,32)");

    model._set("C1", "=DOLLARFR(1.1)");
    model._set("C2", "=DOLLARFR(1.1, 32, 1)");

    model.evaluate();

    assert_eq!(model._get_text("A1"), "1.02");
    assert_eq!(model._get_text("A2"), "1.04");

    assert_eq!(model._get_text("C1"), *"#ERROR!");
    assert_eq!(model._get_text("C2"), *"#ERROR!");
}

#[test]
fn fn_cumipmt() {
    let mut model = new_empty_model();
    model._set("A2", "9%"); // annual interest rate
    model._set("A3", "30"); // years of the load
    model._set("A4", "$125,000"); // present value

    model._set("B1", "=CUMIPMT(A2/12,A3*12,A4,13,24,0)");
    model._set("B2", "=CUMIPMT(A2/12,A3*12,A4,1,1,0)");

    model._set("C1", "=CUMIPMT(A2/12,A3*12,A4,1,1,0,1)");
    model._set("C2", "=CUMIPMT(A2/12,A3*12,A4,1,1)");

    model.evaluate();

    assert_eq!(model._get_text("B1"), "-$11,135.23");
    assert_eq!(model._get_text("B2"), "-$937.50");

    assert_eq!(model._get_text("C1"), *"#ERROR!");
    assert_eq!(model._get_text("C2"), *"#ERROR!");
}

#[test]
fn fn_cumprinc() {
    let mut model = new_empty_model();
    model._set("A2", "9%"); // annual interest rate
    model._set("A3", "30"); // years of the load
    model._set("A4", "$125,000"); // present value

    model._set("B1", "=CUMPRINC(A2/12,A3*12,A4,13,24,0)");
    model._set("B2", "=CUMPRINC(A2/12,A3*12,A4,1,1,0)");

    model._set("C1", "=CUMPRINC(A2/12,A3*12,A4,1,1,0,1)");
    model._set("C2", "=CUMPRINC(A2/12,A3*12,A4,1,1)");

    model.evaluate();

    assert_eq!(model._get_text("B1"), "-$934.11");
    assert_eq!(model._get_text("B2"), "-$68.28");

    assert_eq!(model._get_text("C1"), *"#ERROR!");
    assert_eq!(model._get_text("C2"), *"#ERROR!");
}

#[test]
fn fn_db_misc() {
    let mut model = new_empty_model();

    model._set("B1", "=DB(0,10,1,2,2)");

    model.evaluate();

    assert_eq!(model._get_text("B1"), "$0.00");
}
