from typing import Any

from models.signal import SignalSide, TradingViewAlert


def _normalize_side(value: str | None) -> SignalSide:
    if not value:
        raise ValueError("Missing action/side in alert payload")

    normalized = value.strip().lower()
    mapping = {
        "buy": SignalSide.BUY,
        "long": SignalSide.BUY,
        "sell": SignalSide.SELL,
        "short": SignalSide.SELL,
        "close": SignalSide.CLOSE,
        "exit": SignalSide.CLOSE,
        "flat": SignalSide.FLAT,
    }
    try:
        return mapping[normalized]
    except KeyError as exc:
        raise ValueError(f"Unknown action: {value}") from exc


def _pick(data: dict[str, Any], *keys: str) -> Any:
    for key in keys:
        if key in data and data[key] not in (None, ""):
            return data[key]
    return None


def parse_tradingview_payload(payload: dict[str, Any]) -> TradingViewAlert:
    """
    Accepte le JSON custom d'une alerte TradingView.

    Champs supportés (alias inclus) :
    ticker / symbol, action / side, price / close, qty / quantity,
    strategy / strat, timeframe / interval, comment / message,
    sl / stop_loss, tp / take_profit
    """
    ticker = _pick(payload, "ticker", "symbol", "{{ticker}}")
    if not ticker:
        raise ValueError("Missing ticker/symbol")

    action_raw = _pick(payload, "action", "side", "order")
    side = _normalize_side(str(action_raw) if action_raw is not None else None)

    price = _pick(payload, "price", "close", "{{close}}")
    quantity = _pick(payload, "quantity", "qty", "size")
    strategy = _pick(payload, "strategy", "strat", "strategy_name")
    timeframe = _pick(payload, "timeframe", "interval", "{{interval}}")
    comment = _pick(payload, "comment", "message", "alert_message")
    stop_loss = _pick(payload, "stop_loss", "sl", "stop")
    take_profit = _pick(payload, "take_profit", "tp", "target")

    def _to_float(value: Any) -> float | None:
        if value is None or value == "":
            return None
        return float(value)

    return TradingViewAlert(
        ticker=str(ticker),
        action=side,
        price=_to_float(price),
        quantity=_to_float(quantity),
        strategy=str(strategy) if strategy is not None else None,
        timeframe=str(timeframe) if timeframe is not None else None,
        comment=str(comment) if comment is not None else None,
        stop_loss=_to_float(stop_loss),
        take_profit=_to_float(take_profit),
        raw=payload,
    )
