"""Battery state-of-charge estimation from cell voltage.

All curves assume single-cell voltages measured at rest (no load).
Multi-cell packs are not supported — the voltage_scaling_factor in
PowerOption is implementation-specific and cannot be safely used as a
cell-count multiplier.
"""

from __future__ import annotations

from .models.enums import CapacityEstimator

# ---------------------------------------------------------------------------
# Lookup tables: list of (voltage_mv, soc_percent) sorted high → low.
# Linear interpolation is used between breakpoints.
# ---------------------------------------------------------------------------

# Li-Ion / LiPo single cell.
# Source: widely-used embedded reference curve (e.g. Nordic DevZone battery
# state-of-charge article), empirically validated against standard cells.
_SOC_LI_ION: list[tuple[int, int]] = [
    (4200, 100),
    (4150, 95),
    (4110, 90),
    (4080, 85),
    (4020, 80),
    (3980, 75),
    (3950, 70),
    (3910, 65),
    (3870, 60),
    (3830, 55),
    (3790, 50),
    (3750, 45),
    (3710, 40),
    (3670, 35),
    (3630, 30),
    (3590, 25),
    (3550, 20),
    (3490, 15),
    (3430, 10),
    (3350, 5),
    (3000, 0),
]

# LiFePO4 single cell (3.2 V nominal).
# Source: EVLithium LiFePO4 voltage chart (evlithium.com).
# The extremely flat plateau from ~30–80 % is characteristic of this
# chemistry — voltage alone is a poor indicator in that range.
_SOC_LIFEPO4: list[tuple[int, int]] = [
    (3650, 100),
    (3400, 90),
    (3350, 80),
    (3320, 70),
    (3290, 60),
    (3270, 50),
    (3250, 40),
    (3220, 30),
    (3200, 20),
    (3000, 10),
    (2500, 0),
]

# ---------------------------------------------------------------------------
# Linear-approximation placeholders — to be replaced once hardware-specific
# data is available.
# ---------------------------------------------------------------------------

# Lithium primary (Li-SOCl₂, 3.6 V type, e.g. LS14500).
# WARNING: Li-SOCl₂ has an almost perfectly flat discharge curve for ~95 %
# of its capacity. Voltage-based SoC is fundamentally unreliable for this
# chemistry. This linear approximation is a placeholder only.
# TODO: Replace with manufacturer discharge curve once the specific cell
#       used in OpenDisplay hardware is confirmed.
_SOC_LITHIUM_PRIMARY: list[tuple[int, int]] = [
    (3600, 100),
    (2800, 0),
]

# Supercapacitor.
# The physically correct model is quadratic: SoC = (V²−Vmin²)/(Vmax²−Vmin²)
# because energy is proportional to V². The linear approximation below is a
# placeholder until the actual cell voltage range for OpenDisplay hardware
# is confirmed.
# TODO: Replace with quadratic formula using hardware-specific Vmin/Vmax
#       once confirmed.
_SOC_SUPERCAP: list[tuple[int, int]] = [
    (5000, 100),
    (2000, 0),
]


def _interpolate(table: list[tuple[int, int]], voltage_mv: int) -> int:
    """Linearly interpolate SOC from a voltage lookup table.

    The table must be sorted in descending voltage order.
    Returns a clamped value in the range [0, 100].
    """
    if voltage_mv >= table[0][0]:
        return table[0][1]
    if voltage_mv <= table[-1][0]:
        return table[-1][1]

    for i in range(len(table) - 1):
        v_high, soc_high = table[i]
        v_low, soc_low = table[i + 1]
        if v_low <= voltage_mv <= v_high:
            ratio = (voltage_mv - v_low) / (v_high - v_low)
            return round(soc_low + ratio * (soc_high - soc_low))

    return 0  # unreachable


def voltage_to_percent(
    voltage_mv: int,
    chemistry: CapacityEstimator | int,
) -> int | None:
    """Estimate battery state of charge from cell voltage.

    Args:
        voltage_mv: Battery voltage in millivolts, measured at rest.
        chemistry: Battery chemistry, as a CapacityEstimator enum or raw int.

    Returns:
        Estimated SOC as an integer in [0, 100], or None if the chemistry
        is unknown.
    """
    try:
        chemistry = CapacityEstimator(chemistry)
    except ValueError:
        return None

    match chemistry:
        case CapacityEstimator.LI_ION:
            return _interpolate(_SOC_LI_ION, voltage_mv)
        case CapacityEstimator.LIFEPO4:
            return _interpolate(_SOC_LIFEPO4, voltage_mv)
        case CapacityEstimator.LITHIUM_PRIMARY:
            return _interpolate(_SOC_LITHIUM_PRIMARY, voltage_mv)
        case CapacityEstimator.SUPERCAP:
            return _interpolate(_SOC_SUPERCAP, voltage_mv)
        case _:
            return None
