"""Resultant computation transform.

Computes the vector magnitude (resultant) from X/Y/Z component channels:
    R = sqrt(X^2 + Y^2 + Z^2)
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import Any

import numpy as np

from impakt.channel.code import ChannelCode
from impakt.channel.model import Channel, ChannelGroup


@dataclass(frozen=True)
class ResultantTransform:
    """Compute resultant from a ChannelGroup's X/Y/Z components."""

    @property
    def name(self) -> str:
        return "resultant"

    @property
    def params(self) -> dict[str, Any]:
        return {}

    def apply(self, channel: Channel) -> Channel:
        """Not used directly — use apply_group instead."""
        raise NotImplementedError(
            "ResultantTransform operates on a ChannelGroup, not a single Channel. "
            "Use apply_group() or ChannelGroup.resultant()."
        )

    def apply_group(self, group: ChannelGroup) -> Channel:
        """Compute the resultant from a channel group."""
        return group.resultant()


def resultant(group: ChannelGroup) -> Channel:
    """Convenience function to compute a resultant from a channel group."""
    return group.resultant()


def resultant_from_channels(*channels: Channel) -> Channel:
    """Compute resultant from individual channels.

    The channels don't need to be part of a formal ChannelGroup.
    They just need compatible time vectors.
    """
    if not channels:
        raise ValueError("At least one channel is required")

    # Sum of squares
    result = np.zeros_like(channels[0].data)
    for ch in channels:
        if len(ch.data) != len(result):
            raise ValueError(
                f"Channel length mismatch: {channels[0].name} has {len(result)} samples, "
                f"{ch.name} has {len(ch.data)} samples"
            )
        result += ch.data**2
    result = np.sqrt(result)

    ref = channels[0]
    names = ", ".join(ch.name for ch in channels)

    # Build resultant code
    resultant_name = ref.name[:12] + "R" + ref.name[13:] if len(ref.name) >= 14 else ref.name + "_R"
    resultant_code = ChannelCode(
        raw=resultant_name,
        test_object=ref.code.test_object,
        main_location=ref.code.main_location,
        fine_location=ref.code.fine_location,
        measurement=ref.code.measurement,
        direction="R",
        sense=ref.code.sense,
        filter_class=ref.code.filter_class,
        _valid=ref.code.is_valid,
    )

    return Channel(
        name=resultant_name,
        code=resultant_code,
        data=result,
        time=ref.time,
        unit=ref.unit,
        sample_rate=ref.sample_rate,
        cfc_class=ref.cfc_class,
        metadata=ref.metadata,
        source_test_id=ref.source_test_id,
        transform_history=ref.transform_history + (f"resultant({names})",),
    )
