"""Feature transforms for declarative model specifications."""

from __future__ import annotations

from abc import ABC, abstractmethod

import numpy as np


class FeatureTransform(ABC):
    """Base class for model-spec feature transforms."""

    suffix: str = ""

    @abstractmethod
    def apply(self, X: np.ndarray) -> np.ndarray:
        """Apply the transform to a one-dimensional feature column.

        Parameters
        ----------
        X : np.ndarray
            Input feature vector.

        Returns
        -------
        np.ndarray
            Transformed feature vector.
        """

        raise NotImplementedError()

    def output_name(self, name: str) -> str:
        """Return the transformed feature name."""

        return f"{name}{self.suffix}"


class DerivativeTransform(FeatureTransform):
    """First-order temporal derivative transform."""

    suffix = "_derivative"

    def apply(self, X: np.ndarray) -> np.ndarray:
        """Compute a padded temporal derivative."""

        X_array = np.asarray(X, dtype=float).reshape(-1)
        return np.diff(X_array, prepend=X_array[0])


class ZScoreTransform(FeatureTransform):
    """Z-score standardisation transform."""

    suffix = "_zscore"

    def apply(self, X: np.ndarray) -> np.ndarray:
        """Standardise a feature vector."""

        X_array = np.asarray(X, dtype=float).reshape(-1)
        std = np.std(X_array)
        if std == 0.0:
            std = 1.0
        return (X_array - np.mean(X_array)) / std
