"""Femur load criterion.

Peak compressive axial force measured at the femur load cells.
Evaluated separately for left and right femur.
"""

from __future__ import annotations

from typing import Any

import numpy as np

from impakt.channel.model import Channel, DummyInfo
from impakt.criteria.base import CriterionResult


def femur_load(
    channel: Channel | None = None,
    channels: dict[str, Channel] | None = None,
    side: str = "both",
    dummy: DummyInfo | None = None,
) -> CriterionResult | list[CriterionResult]:
    """Compute peak femur axial load.

    Args:
        channel: Single femur force channel (N or kN).
        channels: Dict of channels (will auto-detect left/right from names).
        side: 'left', 'right', or 'both'.
        dummy: Dummy info (unused currently).

    Returns:
        CriterionResult for a single side, or list of two results if 'both'.
    """
    if side == "both" and channels is not None:
        results = []
        left_ch = None
        right_ch = None

        for name, ch in channels.items():
            name_upper = name.upper()
            if "LE" in name_upper or "LEFT" in name_upper or "L" == name_upper:
                left_ch = ch
            elif "RI" in name_upper or "RIGHT" in name_upper or "R" == name_upper:
                right_ch = ch

        if left_ch is not None:
            results.append(_compute_single(left_ch, "Left"))
        if right_ch is not None:
            results.append(_compute_single(right_ch, "Right"))

        if not results:
            # Just compute for whatever channels we have
            for i, ch in enumerate(channels.values()):
                results.append(_compute_single(ch, f"#{i + 1}"))

        return results if len(results) > 1 else results[0] if results else _empty_result()

    if channel is None and channels is not None:
        channel = next(iter(channels.values()))

    if channel is None:
        raise ValueError("A femur load channel is required")

    side_label = side.capitalize() if side != "both" else ""
    return _compute_single(channel, side_label)


def _compute_single(channel: Channel, side_label: str) -> CriterionResult:
    """Compute femur load for a single channel."""
    force = channel.data.copy()

    # Convert to N if in kN
    if channel.unit in ("kN",):
        force = force * 1000.0

    # Femur load criterion uses compressive force (typically negative in SAE convention)
    # Report as absolute peak compressive
    peak_compressive = float(np.max(np.abs(force)))

    # But also track the signed peak for detail
    peak_idx = int(np.argmax(np.abs(force)))
    peak_time = float(channel.time[peak_idx])

    label = f"Femur Load {side_label}".strip()

    return CriterionResult(
        criterion=label,
        value=peak_compressive / 1000.0,  # Report in kN
        unit="kN",
        time_of_peak=peak_time,
        body_region=f"Femur {side_label}".strip(),
        details={
            "peak_force_N": peak_compressive,
            "signed_peak_N": float(force[peak_idx]),
            "input_channel": channel.name,
        },
    )


def _empty_result() -> CriterionResult:
    return CriterionResult(
        criterion="Femur Load",
        value=0.0,
        unit="kN",
        body_region="Femur",
        details={"error": "No femur channel provided"},
    )
