#!/usr/bin/env python3
"""rsi_extreme_signal.py — RSI extreme mean-reversion signal generator.

Strategy: Buy when RSI is oversold (< threshold_low), sell when overbought (> threshold_high).
Designed for RANGING markets — inverse of trend-following strategies.

Uses the base Signal Generator interface (BaseSignalGenerator.analyze).
"""
from __future__ import annotations

# ── Adaptive price rounding (2-digit vs 3-digit brokers) ──────────
try:
    from src.price_digits import round_price as _round_price
except ImportError:
    def _round_price(price, **_kwargs):
        return round(float(price), 2)


import time
import uuid
from typing import Any

from signal_generators.base import BaseSignalGenerator


class RSIExtremeSignal(BaseSignalGenerator):
    """RSI extreme value mean-reversion strategy.

    Long  entry: RSI < oversold_threshold  (price oversold → buy)
    Short entry: RSI > overbought_threshold (price overbought → sell)

    SL set at 1.5× ATR from entry. TP at 2× ATR (RR ~1.33).
    """

    SCHEMA_VERSION = "2.1.0"

    # ── Default parameters ──────────────────────────────────────

    DEFAULT_PARAMS: dict[str, Any] = {
        "rsi_period": 14,
        "oversold_threshold": 25,     # RSI below this → BUY
        "overbought_threshold": 75,   # RSI above this → SELL
        "atr_period": 14,
        "sl_atr_mult": 2.0,
        "tp_atr_mult": 3.5,
        "min_confidence": 0.55,
        "min_bars": 50,
        "_counter_direction_allowed": True,  # bypass HARD_BIND in trending markets
    }

    # ── Helpers ──────────────────────────────────────────────────

    @staticmethod
    def _rsi(closes: list[float], period: int = 14) -> float | None:
        n = len(closes)
        if n < period + 1:
            return None
        gains = [max(closes[i] - closes[i - 1], 0) for i in range(1, n)]
        losses = [max(closes[i - 1] - closes[i], 0) for i in range(1, n)]
        avg_gain = sum(gains[-period:]) / period
        avg_loss = sum(losses[-period:]) / period
        if avg_loss < 1e-9:
            return 100.0
        rs = avg_gain / avg_loss
        return 100.0 - (100.0 / (1.0 + rs))

    @staticmethod
    def _atr(bars: list[dict], period: int = 14) -> float:
        n = len(bars)
        if n < period + 1:
            return 0.0
        trs = []
        for i in range(1, n):
            b, pb = bars[i], bars[i - 1]
            h, l, pc = b["high"], b["low"], pb["close"]
            trs.append(max(h - l, abs(h - pc), abs(l - pc)))
        return sum(trs[-period:]) / period

    # ── Core ─────────────────────────────────────────────────────

    def analyze(self, bars: list[dict], params: dict | None = None) -> dict[str, Any]:
        p = {**self.DEFAULT_PARAMS, **(params or {})}
        trace_id = f"rsi_extreme_{uuid.uuid4().hex[:12]}"

        if len(bars) < p["min_bars"]:
            return self._hold(trace_id, "insufficient_bars", {})

        closes = [float(b["close"]) for b in bars]
        current_price = closes[-1]

        rsi = self._rsi(closes, int(p["rsi_period"]))
        atr_val = self._atr(bars, int(p["atr_period"]))

        if rsi is None or atr_val <= 0:
            return self._hold(trace_id, "insufficient_data", {"rsi": rsi, "atr": atr_val})

        action = "HOLD"
        side = "NONE"
        reason = ""
        confidence = 0.0

        if rsi <= p["oversold_threshold"]:
            action, side = "BUY", "BUY"
            # Confidence scales with how oversold: 25→0.55, 15→0.75, 5→0.85
            oversold_depth = max(0, p["oversold_threshold"] - rsi)
            confidence = min(0.85, p["min_confidence"] + oversold_depth * 0.015)
            reason = f"RSI oversold ({rsi:.0f}<{p['oversold_threshold']})"
        elif rsi >= p["overbought_threshold"]:
            action, side = "SELL", "SELL"
            overbought_depth = max(0, rsi - p["overbought_threshold"])
            confidence = min(0.85, p["min_confidence"] + overbought_depth * 0.015)
            reason = f"RSI overbought ({rsi:.0f}>{p['overbought_threshold']})"
        else:
            return self._hold(trace_id, f"RSI neutral ({rsi:.0f})", {"rsi": round(rsi, 1)})

        # Entry / SL / TP
        entry = current_price
        sl = _round_price(entry - atr_val * p["sl_atr_mult"]) if side == "BUY" else _round_price(entry + atr_val * p["sl_atr_mult"])
        tp = _round_price(entry + atr_val * p["tp_atr_mult"]) if side == "BUY" else _round_price(entry - atr_val * p["tp_atr_mult"])

        return {
            "schema_version": self.SCHEMA_VERSION,
            "trace_id": trace_id,
            "strategy_id": self.get_strategy_id(),
            "model_version": None,
            "timestamp": time.strftime("%Y-%m-%dT%H:%M:%S+08:00"),
            "payload": {
                "action": action,
                "side": side,
                "entry": round(entry, 3),
                "sl": sl,
                "tp": tp,
                "confidence": round(confidence, 3),
                "reason": reason,
                "indicators": {
                    "rsi": round(rsi, 1),
                    "atr": round(atr_val, 3),
                    "current_price": round(current_price, 3),
                },
            },
            "_counter_direction_allowed": bool(p.get("_counter_direction_allowed", True)),
        }

    def _hold(self, trace_id: str, reason: str, indicators: dict) -> dict:
        return {
            "schema_version": self.SCHEMA_VERSION,
            "trace_id": trace_id,
            "strategy_id": self.get_strategy_id(),
            "model_version": None,
            "timestamp": time.strftime("%Y-%m-%dT%H:%M:%S+08:00"),
            "payload": {
                "action": "HOLD",
                "confidence": 0.0,
                "reason": reason,
                "indicators": indicators,
            },
        }

    def get_strategy_id(self) -> str:
        return "rsi_extreme"

    def get_params_schema(self) -> dict:
        return {
            "rsi_period":          {"type": "int",   "default": 14, "range": [7, 28]},
            "oversold_threshold":  {"type": "int",   "default": 25, "range": [15, 35]},
            "overbought_threshold":{"type": "int",   "default": 75, "range": [65, 85]},
            "atr_period":         {"type": "int",   "default": 14, "range": [7, 28]},
            "sl_atr_mult":        {"type": "float", "default": 1.5, "range": [1.0, 3.0]},
            "tp_atr_mult":        {"type": "float", "default": 2.0, "range": [1.5, 4.0]},
            "min_confidence":     {"type": "float", "default": 0.55, "range": [0.45, 0.70]},
        }
