"""
Equipment baseline and fingerprint analysis.
"""

from dataclasses import dataclass
from statistics import mean, pstdev


@dataclass(frozen=True)
class BaselineMetric:
    """
    Statistical reference for one equipment metric.
    """

    mean: float
    standard_deviation: float
    minimum: float
    maximum: float
    sample_count: int


@dataclass(frozen=True)
class BaselineComparison:
    """
    Comparison between current behavior and equipment baseline.
    """

    metric: str
    baseline_mean: float
    current_mean: float
    absolute_deviation: float
    relative_deviation_percent: float
    z_score: float


class EquipmentBaseline:
    """
    Build and compare an equipment-specific fingerprint.
    """

    MINIMUM_BASELINE_SAMPLES = 30
    BASELINE_RATIO = 0.75

    @staticmethod
    def _values(
        history: list[dict[str, object]],
        field: str,
    ) -> list[float]:
        """
        Extract valid numeric values from measurement history.
        """
        return [
            float(row[field])
            for row in history
            if row.get(field) is not None
        ]

    @classmethod
    def split_history(
        cls,
        history: list[dict[str, object]],
    ) -> tuple[
        list[dict[str, object]],
        list[dict[str, object]],
    ]:
        """
        Split history into baseline and current windows.
        """
        if len(history) < cls.MINIMUM_BASELINE_SAMPLES:
            raise ValueError(
                "Insufficient data to build an equipment baseline."
            )

        split_index = int(
            len(history) * cls.BASELINE_RATIO
        )

        split_index = max(
            cls.MINIMUM_BASELINE_SAMPLES,
            split_index,
        )

        split_index = min(
            split_index,
            len(history) - 1,
        )

        return (
            history[:split_index],
            history[split_index:],
        )

    @classmethod
    def build_metric(
        cls,
        history: list[dict[str, object]],
        field: str,
    ) -> BaselineMetric:
        """
        Build the statistical baseline for one metric.
        """
        values = cls._values(
            history=history,
            field=field,
        )

        if not values:
            raise ValueError(
                f"No valid values available for {field}."
            )

        deviation = (
            pstdev(values)
            if len(values) >= 2
            else 0.0
        )

        return BaselineMetric(
            mean=round(mean(values), 4),
            standard_deviation=round(deviation, 4),
            minimum=round(min(values), 4),
            maximum=round(max(values), 4),
            sample_count=len(values),
        )

    @classmethod
    def compare_metric(
        cls,
        baseline_history: list[dict[str, object]],
        current_history: list[dict[str, object]],
        field: str,
    ) -> BaselineComparison:
        """
        Compare current behavior with the equipment baseline.
        """
        baseline = cls.build_metric(
            history=baseline_history,
            field=field,
        )

        current_values = cls._values(
            history=current_history,
            field=field,
        )

        if not current_values:
            raise ValueError(
                f"No current values available for {field}."
            )

        current_mean = mean(current_values)

        absolute_deviation = (
            current_mean - baseline.mean
        )

        if baseline.mean == 0:
            relative_deviation = 0.0
        else:
            relative_deviation = (
                absolute_deviation
                / abs(baseline.mean)
                * 100.0
            )

        if baseline.standard_deviation <= 0:
            z_score = 0.0
        else:
            z_score = (
                absolute_deviation
                / baseline.standard_deviation
            )

        return BaselineComparison(
            metric=field,
            baseline_mean=round(
                baseline.mean,
                4,
            ),
            current_mean=round(
                current_mean,
                4,
            ),
            absolute_deviation=round(
                absolute_deviation,
                4,
            ),
            relative_deviation_percent=round(
                relative_deviation,
                2,
            ),
            z_score=round(
                z_score,
                2,
            ),
        )