"""3ms Clip (Chest Acceleration) criterion.

The 3ms clip is the maximum acceleration value sustained for a cumulative
duration of 3 milliseconds. It represents the highest acceleration level
where the total time the signal exceeds that level equals at least 3 ms.
"""

from __future__ import annotations

from typing import Any

import numpy as np

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


def clip_3ms(
    channel_or_group: Channel | ChannelGroup,
    clip_duration_ms: float = 3.0,
    channels: dict[str, Channel] | None = None,
    dummy: DummyInfo | None = None,
) -> CriterionResult:
    """Compute the 3ms clip value.

    The 3ms clip is found by determining the highest acceleration level A
    such that the cumulative time the signal exceeds A is >= 3 ms.

    Args:
        channel_or_group: Resultant acceleration Channel (in g's) or
            ChannelGroup with X/Y/Z components.
        clip_duration_ms: Clip duration in milliseconds (default 3.0).
        channels: Alternative channel dict.
        dummy: Dummy info (unused).

    Returns:
        CriterionResult with the 3ms clip value.
    """
    # Get resultant
    if isinstance(channel_or_group, ChannelGroup):
        resultant = channel_or_group.resultant()
    elif isinstance(channel_or_group, Channel):
        resultant = channel_or_group
    elif channels is not None:
        from impakt.transform.resultant import resultant_from_channels

        resultant = resultant_from_channels(*channels.values())
    else:
        raise TypeError("Provide a Channel, ChannelGroup, or channels dict")

    accel = np.abs(resultant.data)

    # Convert to g if needed
    if resultant.unit in ("m/s²", "m/s^2", "m/s2"):
        accel = accel / 9.80665

    dt = resultant.dt
    clip_duration_s = clip_duration_ms / 1000.0
    clip_samples = clip_duration_s / dt if dt > 0 else 0

    # Sort acceleration values in descending order
    sorted_accel = np.sort(accel)[::-1]

    # Find the value where cumulative exceedance time >= clip_duration
    # For each threshold level (working downward), count how many samples exceed it
    # The 3ms clip = value at index where we first accumulate enough samples
    if clip_samples <= 1:
        clip_value = float(sorted_accel[0])
    else:
        # The nth largest value, where n = ceil(clip_samples)
        n = int(np.ceil(clip_samples))
        if n >= len(sorted_accel):
            clip_value = float(sorted_accel[-1])
        else:
            clip_value = float(sorted_accel[n - 1])

    # Find time of peak
    peak_idx = int(np.argmax(accel))
    peak_time = float(resultant.time[peak_idx])

    return CriterionResult(
        criterion="3ms Clip",
        value=clip_value,
        unit="g",
        time_of_peak=peak_time,
        body_region="Chest",
        details={
            "clip_duration_ms": clip_duration_ms,
            "peak_accel_g": float(np.max(accel)),
            "input_channel": resultant.name,
        },
    )
