"""
Net Present Value and discounting.

Convention (matches the Forrester TEI methodology used in the Amazon Connect
study):

* The *Initial* investment (column "Initial" in TEI tables) is **not**
  discounted — it occurs at time zero.
* Year-N cash flows are discounted at the end of the year:
  ``PV = CF_n / (1 + r) ** n``  for ``n = 1, 2, …``.

That matches the PDF note: *"The initial investment column contains costs
incurred at 'time 0' or at the beginning of Year 1 that are not discounted.
All other cash flows are discounted using the discount rate at the end of
the year."*
"""

from __future__ import annotations

from collections.abc import Iterable


def discount_factor(year: int, discount_rate: float) -> float:
    """Return ``1 / (1 + r) ** year``. Year 0 → 1.0 (no discount)."""
    if year < 0:
        raise ValueError("year must be >= 0")
    return 1.0 / ((1.0 + discount_rate) ** year)


def present_value(amount: float, year: int, discount_rate: float) -> float:
    """Discount ``amount`` from end-of-year ``year`` to present."""
    return amount * discount_factor(year, discount_rate)


def present_value_series(
    cashflows: Iterable[float],
    discount_rate: float,
    start_year: int = 1,
) -> float:
    """
    Sum the present value of a stream of year-end cashflows.

    Args:
        cashflows: iterable of year-N amounts (Y1, Y2, …).
        discount_rate: e.g. 0.10 for 10%.
        start_year: year of the first element. Default 1 (skip year-0).
    """
    total = 0.0
    for offset, cf in enumerate(cashflows):
        total += present_value(cf, start_year + offset, discount_rate)
    return total


def npv(
    cashflows: Iterable[float],
    discount_rate: float,
    initial: float = 0.0,
) -> float:
    """
    Net Present Value.

    Args:
        cashflows: year-end cashflows for Year 1, Year 2, …
        discount_rate: e.g. 0.10
        initial: undiscounted year-0 cashflow (negative for a cost,
            positive for a one-off benefit).

    Returns:
        ``initial + Σ CF_n / (1 + r)^n``  for ``n = 1..len(cashflows)``.

    Example::

        >>> # Amazon Connect total benefits PV ≈ $101.7M
        >>> benefits = [27_279_019, 40_333_658, 57_983_794]
        >>> round(npv(benefits, 0.10) / 1_000_000, 1)
        101.7
    """
    return initial + present_value_series(cashflows, discount_rate, start_year=1)
