"""Jim Simons / Renaissance-style quantitative scoring.

Simons ignored fundamentals entirely and traded statistical patterns in
price and volume. This scorer is deliberately fundamentals-free: it blends
a mean-reversion signal (z-score of price vs its moving average, plus RSI),
a cross-sectional momentum signal (12-month minus 1-month return), and a
volatility-adjusted return (a Sharpe-like ratio).
"""
from __future__ import annotations

import numpy as np
import pandas as pd

from ..data import StockData
from ..scoring import (Signal, StrategyResult, combine, higher_better,
                       lower_better, verdict_from)


def _zscore_vs_ma(close: pd.Series, window: int = 20):
    if len(close) < window + 1:
        return None
    ma = close.rolling(window).mean()
    sd_ = close.rolling(window).std()
    last_ma, last_sd = ma.iloc[-1], sd_.iloc[-1]
    if not last_sd or np.isnan(last_sd) or last_sd == 0:
        return None
    return float((close.iloc[-1] - last_ma) / last_sd)


def _rsi(close: pd.Series, window: int = 14):
    if len(close) < window + 1:
        return None
    delta = close.diff()
    gain = delta.clip(lower=0).rolling(window).mean()
    loss = (-delta.clip(upper=0)).rolling(window).mean()
    last_gain, last_loss = gain.iloc[-1], loss.iloc[-1]
    if last_loss is None or np.isnan(last_loss) or last_loss == 0:
        return 100.0
    rs = last_gain / last_loss
    return float(100 - 100 / (1 + rs))


def _sharpe(close: pd.Series, window: int = 126):
    if len(close) < window + 1:
        return None
    rets = close.pct_change().dropna().iloc[-window:]
    if rets.std() == 0:
        return None
    return float(np.sqrt(252) * rets.mean() / rets.std())


def evaluate(sd: StockData) -> StrategyResult:
    if sd.history.empty:
        signals = [Signal("no_price_data", None, 0.0, 1.0)]
        return StrategyResult("Simons", 0.0, signals, "No data")

    close = sd.history["Close"].dropna()

    z = _zscore_vs_ma(close, 20)
    rsi = _rsi(close, 14)
    mom = None
    r12 = sd.returns(252)
    r1 = sd.returns(21)
    if r12 is not None and r1 is not None:
        mom = r12 - r1                 # classic 12-1 momentum
    sharpe = _sharpe(close, 126)

    # Mean reversion: oversold (negative z, low RSI) scores high.
    mr_z = lower_better(z, -2.5, 1.0) if z is not None else 0.5
    mr_rsi = lower_better(rsi, 30, 70) if rsi is not None else 0.5

    signals = [
        Signal("MeanRev_Z", z, mr_z, 0.28, "z-score vs 20d MA (oversold favoured)"),
        Signal("RSI", rsi, mr_rsi, 0.18, "oversold favoured"),
        Signal("Momentum_12_1", mom, higher_better(mom, -0.2, 0.4), 0.30),
        Signal("Sharpe_6M", sharpe, higher_better(sharpe, -0.5, 2.0), 0.24,
               "volatility-adjusted trend"),
    ]
    score = combine(signals)
    return StrategyResult("Simons", score, signals, verdict_from(score))
