use chrono::NaiveDate;
use fidc_core::{CashReceivable, PortfolioState, Position};

fn d(year: i32, month: u32, day: u32) -> NaiveDate {
    NaiveDate::from_ymd_opt(year, month, day).expect("valid date")
}

#[test]
fn cash_dividend_adjusts_cost_basis_and_returns_cash_delta() {
    let mut position = Position::new("000001.SZ");
    position.buy(d(2025, 1, 2), 1_000, 10.0);

    let cash_delta = position.apply_cash_dividend(0.5);

    assert!((cash_delta - 500.0).abs() < 1e-9);
    assert_eq!(position.quantity, 1_000);
    assert!((position.average_cost - 9.5).abs() < 1e-9);
    assert!((position.last_price - 9.5).abs() < 1e-9);
}

#[test]
fn stock_split_scales_lots_quantity_and_average_cost() {
    let mut position = Position::new("000001.SZ");
    position.buy(d(2025, 1, 2), 1_000, 10.0);

    let delta_quantity = position.apply_split_ratio(1.2);

    assert_eq!(delta_quantity, 200);
    assert_eq!(position.quantity, 1_200);
    assert!((position.average_cost - (10.0 / 1.2)).abs() < 1e-9);
    assert!((position.last_price - (10.0 / 1.2)).abs() < 1e-9);
    assert_eq!(position.sellable_qty(d(2025, 1, 3)), 1_200);
}

#[test]
fn portfolio_settles_cash_receivable_on_payable_date() {
    let mut portfolio = PortfolioState::new(1_000_000.0);
    portfolio.add_cash_receivable(CashReceivable {
        symbol: "000001.SZ".to_string(),
        ex_date: d(2025, 1, 2),
        payable_date: d(2025, 1, 5),
        amount: 500.0,
        reason: "cash_dividend 0.5".to_string(),
    });

    let settled_early = portfolio.settle_cash_receivables(d(2025, 1, 4));
    assert!(settled_early.is_empty());
    assert!((portfolio.cash() - 1_000_000.0).abs() < 1e-9);

    let settled = portfolio.settle_cash_receivables(d(2025, 1, 5));
    assert_eq!(settled.len(), 1);
    assert!((portfolio.cash() - 1_000_500.0).abs() < 1e-9);
    assert!(portfolio.cash_receivables().is_empty());
}
