"""
ferro_ta.cross_asset — Cross-asset and relative strength analytics.

Provides helpers for relative value and pair-trading workflows:
- relative_strength(asset_returns, benchmark_returns)
- spread(a, b, hedge=1.0)
- ratio(a, b)
- zscore(x, window)
- rolling_beta(a, b, window)

Compute-intensive work delegates to Rust (via ferro_ta._ferro_ta).

Functions
---------
relative_strength(asset_returns, benchmark_returns)
    Cumulative-return ratio (asset / benchmark), starting at 1.

spread(a, b, hedge=1.0)
    Spread series: a - hedge * b.

ratio(a, b)
    Ratio series: a / b.

zscore(x, window)
    Rolling Z-score of series *x* over a sliding window.

rolling_beta(a, b, window)
    Rolling beta (hedge ratio) of series *a* vs *b*.

Rust backend
------------
    ferro_ta._ferro_ta.relative_strength
    ferro_ta._ferro_ta.spread
    ferro_ta._ferro_ta.zscore_series
    ferro_ta._ferro_ta.rolling_beta
"""

from __future__ import annotations

import numpy as np
from numpy.typing import ArrayLike, NDArray

from ferro_ta._ferro_ta import ratio as _rust_ratio
from ferro_ta._ferro_ta import relative_strength as _rust_rel_strength
from ferro_ta._ferro_ta import rolling_beta as _rust_rolling_beta
from ferro_ta._ferro_ta import spread as _rust_spread
from ferro_ta._ferro_ta import zscore_series as _rust_zscore
from ferro_ta._utils import _to_f64

__all__ = [
    "relative_strength",
    "spread",
    "ratio",
    "zscore",
    "rolling_beta",
]


# ---------------------------------------------------------------------------
# relative_strength
# ---------------------------------------------------------------------------


def relative_strength(
    asset_returns: ArrayLike,
    benchmark_returns: ArrayLike,
) -> NDArray[np.float64]:
    """Compute relative strength of an asset versus a benchmark.

    Returns the ratio of cumulative returns::

        RS[i] = (1 + r_asset[0]) * … * (1 + r_asset[i]) /
                ((1 + r_bench[0]) * … * (1 + r_bench[i]))

    starting from RS[0] ≈ 1.

    Parameters
    ----------
    asset_returns, benchmark_returns : array-like
        Fractional returns per bar (e.g. 0.01 for +1%).  Equal length.

    Returns
    -------
    numpy.ndarray of same length — relative strength series.

    Examples
    --------
    >>> import numpy as np
    >>> from ferro_ta.analysis.cross_asset import relative_strength
    >>> r_a = np.array([0.01, 0.02, -0.01, 0.005])
    >>> r_b = np.array([0.005, 0.01, -0.005, 0.002])
    >>> rs = relative_strength(r_a, r_b)
    >>> rs[0] > 1  # asset outperformed at bar 0
    True
    """
    a = _to_f64(asset_returns)
    b = _to_f64(benchmark_returns)
    return _rust_rel_strength(a, b)


# ---------------------------------------------------------------------------
# spread
# ---------------------------------------------------------------------------


def spread(
    a: ArrayLike,
    b: ArrayLike,
    hedge: float = 1.0,
) -> NDArray[np.float64]:
    """Compute the spread between two series.

    ``spread[i] = a[i] - hedge * b[i]``

    Parameters
    ----------
    a, b : array-like  (equal length)
    hedge : float  — hedge ratio (default 1.0)

    Returns
    -------
    numpy.ndarray

    Examples
    --------
    >>> import numpy as np
    >>> from ferro_ta.analysis.cross_asset import spread
    >>> a = np.array([10.0, 11.0, 12.0])
    >>> b = np.array([9.0, 10.0, 11.0])
    >>> list(spread(a, b))
    [1.0, 1.0, 1.0]
    """
    return _rust_spread(_to_f64(a), _to_f64(b), float(hedge))


# ---------------------------------------------------------------------------
# ratio
# ---------------------------------------------------------------------------


def ratio(
    a: ArrayLike,
    b: ArrayLike,
) -> NDArray[np.float64]:
    """Compute the ratio of two series: a / b.

    Zeros in *b* produce ``NaN`` in the result.

    Parameters
    ----------
    a, b : array-like  (equal length)

    Returns
    -------
    numpy.ndarray

    Examples
    --------
    >>> import numpy as np
    >>> from ferro_ta.analysis.cross_asset import ratio
    >>> a = np.array([10.0, 12.0, 15.0])
    >>> b = np.array([5.0, 4.0, 5.0])
    >>> list(ratio(a, b))
    [2.0, 3.0, 3.0]
    """
    return _rust_ratio(_to_f64(a), _to_f64(b))


# ---------------------------------------------------------------------------
# zscore
# ---------------------------------------------------------------------------


def zscore(
    x: ArrayLike,
    window: int,
) -> NDArray[np.float64]:
    """Compute the rolling Z-score of series *x*.

    ``z[i] = (x[i] - mean(x[i-window+1..i])) / std(x[i-window+1..i])``

    Parameters
    ----------
    x : array-like
    window : int  — must be >= 2

    Returns
    -------
    numpy.ndarray — NaN for first ``window-1`` positions.

    Examples
    --------
    >>> import numpy as np
    >>> from ferro_ta.analysis.cross_asset import zscore
    >>> x = np.array([1.0, 2.0, 3.0, 2.0, 1.0])
    >>> z = zscore(x, window=3)
    >>> np.isnan(z[0]) and np.isnan(z[1])
    True
    """
    return _rust_zscore(_to_f64(x), int(window))


# ---------------------------------------------------------------------------
# rolling_beta
# ---------------------------------------------------------------------------


def rolling_beta(
    a: ArrayLike,
    b: ArrayLike,
    window: int,
) -> NDArray[np.float64]:
    """Compute rolling beta (hedge ratio) of series *a* vs *b*.

    Parameters
    ----------
    a, b : array-like  (equal length)
    window : int  — rolling window size (must be >= 2)

    Returns
    -------
    numpy.ndarray — NaN for first ``window-1`` positions.

    Examples
    --------
    >>> import numpy as np
    >>> from ferro_ta.analysis.cross_asset import rolling_beta
    >>> rng = np.random.default_rng(42)
    >>> b = rng.normal(0, 1, 50)
    >>> a = 0.8 * b + rng.normal(0, 0.1, 50)
    >>> rb = rolling_beta(a, b, window=20)
    >>> np.isnan(rb[18])
    True
    >>> abs(rb[-1] - 0.8) < 0.3
    True
    """
    return _rust_rolling_beta(_to_f64(a), _to_f64(b), int(window))
