from __future__ import annotations

from typing import Any

from strategy_sdk.interfaces import StrategyPlugin
from strategy_sdk.types import (
    MarketSnapshot,
    PortfolioStateView,
    StrategyInitContext,
    StrategyIntent,
    StrategyManifest,
    StrategyRiskOpinion,
)
from src.strategy_registry import canonical_strategy_id, get_strategy_runtime_contract


def _normalize_legacy_action(action: Any, side: Any) -> str:
    action_upper = str(action or "HOLD").upper()
    side_upper = str(side or "NONE").upper()
    if action_upper in {"BUY", "SELL"}:
        return "OPEN"
    if action_upper in {"OPEN", "CLOSE", "HOLD"}:
        return action_upper
    if side_upper in {"BUY", "SELL"} and action_upper not in {"HOLD", "NONE"}:
        return "OPEN"
    return "HOLD"


class LegacySignalStrategyAdapter(StrategyPlugin):
    """Bridges a BaseSignalGenerator to the StrategyPlugin ABC.

    Wraps a legacy signal generator and adapts its analyze() -> dict
    output to the StrategyPlugin interface (on_market_snapshot ->
    StrategyIntent).  This is the critical adapter that lets all
    existing BaseSignalGenerator subclasses plug into the new
    StrategyRuntime without rewriting them.
    """

    def __init__(self, spec, legacy_generator, instance_id: str = ""):
        self._spec = spec
        self._generator = legacy_generator
        self._instance_id = instance_id
        self._ctx: StrategyInitContext | None = None
        self._params: dict[str, Any] = {}

    # -- StrategyPlugin implementation ---------------------------------

    def manifest(self) -> StrategyManifest:
        strategy_id = canonical_strategy_id(self._spec.strategy_id)
        runtime_contract = get_strategy_runtime_contract(strategy_id)
        return StrategyManifest(
            strategy_id=strategy_id,
            name=self._spec.name,
            version=self._spec.version,
            kind=self._spec.kind,
            entrypoint=self._spec.entrypoint,
            description=self._spec.description,
            capabilities=list(self._spec.capabilities),
            supported_symbols=["XAUUSD"],
            supported_timeframes=["M1", "M5", "M15", "H1"],
            params_schema=self._generator.get_params_schema(),
            default_mode=self._spec.default_mode,
            trust_level=self._spec.trust_level,
            tags=[
                strategy_id,
                str(runtime_contract.get("strategy_style", "hybrid") or "hybrid"),
            ],
        )

    def initialize(self, ctx: StrategyInitContext) -> None:
        self._ctx = ctx
        self._params = dict(ctx.config)
        self._instance_id = self._instance_id or canonical_strategy_id(ctx.strategy_id)

    def on_market_snapshot(self, snapshot: MarketSnapshot) -> StrategyIntent:
        merged_params = dict(self._params)
        merged_params.update(snapshot.params)

        # Inject symbol-aware calibration (ATR floor, etc.) for legacy generators
        if snapshot.symbol:
            merged_params.setdefault("_symbol", snapshot.symbol)
            try:
                from src.symbol_calibrator import calibrate_symbol_params as _csp
                cal = _csp(snapshot.symbol, save=False)
                merged_params.setdefault("atr_floor", cal.get("atr_floor", 1.0))
                merged_params.setdefault("_pip_size", cal.get("pip_size", 0.01))
            except Exception:
                pass

        signal = self._generator.analyze(snapshot.bars, merged_params)
        payload = signal.get("payload", {}) if isinstance(signal, dict) else {}

        side = str(payload.get("side", "BUY") or "BUY").upper()
        action = _normalize_legacy_action(payload.get("action", "HOLD"), side)
        confidence = float(payload.get("confidence", 0.0))
        strategy_id = canonical_strategy_id(self._spec.strategy_id)
        runtime_contract = get_strategy_runtime_contract(strategy_id)

        entry_plan: dict[str, Any] = {}
        exit_plan: dict[str, Any] = {}
        if action != "HOLD":
            entry_plan = {"entry": payload.get("entry"), "side": side}
            exit_plan = {"sl": payload.get("sl"), "tp": payload.get("tp")}

        return StrategyIntent(
            strategy_id=strategy_id,
            strategy_version=self._spec.version,
            config_revision=self._ctx.config_revision if self._ctx else "manual",
            instance_id=self._instance_id or strategy_id,
            action=action,
            side=side or "BUY",
            confidence=confidence,
            reason=str(payload.get("reason", "")) if isinstance(signal, dict) else "",
            tags=[strategy_id, self._spec.kind, str(runtime_contract.get("strategy_style", "hybrid") or "hybrid")],
            entry_plan=entry_plan,
            exit_plan=exit_plan,
            diagnostics={
                "schema_version": signal.get("schema_version", "") if isinstance(signal, dict) else "",
                "trace_id": signal.get("trace_id", "") if isinstance(signal, dict) else "",
                "indicators": payload.get("indicators", {}),
                "vol_ratio": snapshot.vol_ratio,
                "runtime_contract": runtime_contract,
            },
        )

    def on_risk_check(
        self, intent: StrategyIntent, portfolio: PortfolioStateView
    ) -> StrategyRiskOpinion:
        reasons: list[str] = []
        allow = True

        if intent.action == "HOLD":
            return StrategyRiskOpinion(allow=True, reasons=["no action"])

        if intent.confidence < 0.5:
            reasons.append(f"low confidence ({intent.confidence:.2f})")

        if portfolio.position_side != "FLAT" and portfolio.position_side == intent.side:
            reasons.append(
                f"already positioned {portfolio.position_side}, "
                f"size={portfolio.position_size}"
            )

        if not reasons:
            reasons.append("legacy signal OK")

        return StrategyRiskOpinion(
            allow=allow,
            severity="INFO" if allow else "WARN",
            recommended_action="ALLOW" if allow else "REDUCE",
            score=intent.confidence,
            reasons=reasons,
        )

    def shutdown(self) -> None:
        pass

    def get_strategy_id(self) -> str:
        return canonical_strategy_id(self._spec.strategy_id)
