import numpy as np
from .utils import apply_sliding_window

def higher_order_moments(signal=None, window_size=None, overlap=None, windows=None, order=5):
    """
    Calculate the higher-order standardized moment of the signal within each window.
    For a perfectly symmetric distribution, odd moments should be near zero.

    :param signal: The input signal as a 1D array or list (if windows not provided).
    :type signal: array-like
    :param window_size: The number of samples in each window (if windows not provided).
    :type window_size: int
    :param overlap: The number of samples to overlap between consecutive windows (if windows not provided).
    :type overlap: int
    :param windows: Optional precomputed list of windows.
    :type windows: list
    :param order: The order of the moment to compute (e.g., 5 for the 5th moment).
    :type order: int
    :return: 1D array of higher-order moment values for each window.
    :rtype: numpy.ndarray

    Reference: https://en.wikipedia.org/wiki/Moment_(mathematics)
    """
    return apply_sliding_window(
        lambda x: np.mean(((x - np.mean(x)) / np.std(x)) ** order),
        windows=windows, signal=signal, window_size=window_size, overlap=overlap
    )


def percentiles(signal=None, window_size=None, overlap=None, windows=None, percentile_value=50):
    """
    Calculate a specific percentile of the signal within each window.

    :param signal: The input signal as a 1D array or list (if windows not provided).
    :type signal: array-like
    :param window_size: The number of samples in each window (if windows not provided).
    :type window_size: int
    :param overlap: The number of samples to overlap between consecutive windows (if windows not provided).
    :type overlap: int
    :param windows: Optional precomputed list of windows.
    :type windows: list
    :param percentile_value: The percentile to compute (0-100).
    :type percentile_value: float
    :return: 1D array of the specified percentile values for each window.
    :rtype: numpy.ndarray

    Reference: https://en.wikipedia.org/wiki/Percentile
    """
    return apply_sliding_window(
        lambda x: np.percentile(x, percentile_value),
        windows=windows, signal=signal, window_size=window_size, overlap=overlap
    )


def l_moments(signal=None, window_size=None, overlap=None, windows=None):
    """
    Calculate L-moments for each window in the signal. This is a simplified
    example returning mean, variance, mean absolute deviation from the median,
    and mean absolute difference of consecutive points.

    :param signal: The input signal as a 1D array or list (if windows not provided).
    :type signal: array-like
    :param window_size: The number of samples in each window (if windows not provided).
    :type window_size: int
    :param overlap: The number of samples to overlap between consecutive windows (if windows not provided).
    :type overlap: int
    :param windows: Optional precomputed list of windows.
    :type windows: list
    :return: 2D array where each row contains [mean, variance, MAD, mean_abs_diff] for that window.
    :rtype: numpy.ndarray

    Reference: https://en.wikipedia.org/wiki/L-moment
    """
    def l_moments_func(x):
        return (
            np.mean(x),
            np.var(x),
            np.mean(np.abs(x - np.median(x))),
            np.mean(np.abs(np.diff(x)))
        )
    return apply_sliding_window(
        l_moments_func,
        windows=windows, signal=signal, window_size=window_size, overlap=overlap
    )


def quantile_range(signal=None, window_size=None, overlap=None, windows=None, lower_quantile=0.25, upper_quantile=0.75):
    """
    Calculate the difference between two quantiles (e.g., 25th and 75th)
    within each window.

    :param signal: The input signal as a 1D array or list (if windows not provided).
    :type signal: array-like
    :param window_size: The number of samples in each window (if windows not provided).
    :type window_size: int
    :param overlap: The number of samples to overlap between consecutive windows (if windows not provided).
    :type overlap: int
    :param windows: Optional precomputed list of windows.
    :type windows: list
    :param lower_quantile: The lower quantile (0-1 range).
    :type lower_quantile: float
    :param upper_quantile: The upper quantile (0-1 range).
    :type upper_quantile: float
    :return: 1D array of quantile range values for each window.
    :rtype: numpy.ndarray

    Reference: https://en.wikipedia.org/wiki/Quantile
    """
    return apply_sliding_window(
        lambda x: np.percentile(x, upper_quantile * 100) - np.percentile(x, lower_quantile * 100),
        windows=windows, signal=signal, window_size=window_size, overlap=overlap
    )


def autoregressive_coefficients(signal=None, window_size=None, overlap=None, windows=None, order=4):
    """
    Estimate autoregressive (AR) model coefficients for each window in the signal.

    :param signal: The input signal as a 1D array or list (if windows not provided).
    :type signal: array-like
    :param window_size: The number of samples in each window (if windows not provided).
    :type window_size: int
    :param overlap: The number of samples to overlap between consecutive windows (if windows not provided).
    :type overlap: int
    :param windows: Optional precomputed list of windows.
    :type windows: list
    :param order: The order of the AR model.
    :type order: int
    :return: 2D array of AR coefficients for each window, with shape (num_windows, order).
    :rtype: numpy.ndarray

    Reference: https://en.wikipedia.org/wiki/Autoregressive_model
    """
    from numpy.linalg import lstsq

    def ar_func(x):
        N = len(x)
        if N <= order:
            return np.zeros(order)
        # Build the regression matrix X and target vector y.
        X = np.vstack([x[i:N - order + i] for i in range(order)]).T
        y = x[order:]
        ar_coeffs = lstsq(X, y, rcond=None)[0]
        return ar_coeffs

    return apply_sliding_window(
        ar_func,
        windows=windows, signal=signal, window_size=window_size, overlap=overlap
    )
