"""AlertConverter — converts TradingView webhook alerts to PULSE messages."""

import json
from typing import Any, Dict, Optional


# Map TradingView action strings to PULSE directions
ACTION_MAP = {
    "buy": "long",
    "long": "long",
    "enter_long": "long",
    "sell": "short",
    "short": "short",
    "enter_short": "short",
    "close": "neutral",
    "exit": "neutral",
    "flat": "neutral",
}

# Map TradingView interval strings to standard labels
INTERVAL_MAP = {
    "1": "1m", "3": "3m", "5": "5m", "15": "15m", "30": "30m",
    "45": "45m", "60": "1h", "120": "2h", "180": "3h", "240": "4h",
    "1D": "1d", "1W": "1w", "1M": "1M",
}


class AlertConverter:
    """Convert TradingView webhook JSON to PULSE message parameters.

    TradingView sends alerts as JSON via HTTP POST. The exact format
    depends on the alert message template the user sets up in Pine Script.

    Supported alert formats:

    1. Standard format (recommended):
       {"action": "buy", "symbol": "BTCUSDT", "price": "83000",
        "interval": "1h", "confidence": "0.85"}

    2. Minimal format:
       {"action": "buy", "symbol": "BTCUSDT"}

    3. Pine Script strategy format:
       {"action": "{{strategy.order.action}}", "symbol": "{{ticker}}",
        "price": "{{close}}", "interval": "{{interval}}"}

    4. Plain text (legacy):
       "BUY BTCUSDT"

    Example:
        >>> converter = AlertConverter()
        >>> params = converter.parse('{"action": "buy", "symbol": "BTCUSDT", "price": "83000"}')
        >>> params["direction"]  # "long"
        >>> params["pair"]       # "BTC/USDT"
    """

    def parse(self, raw: str) -> Dict[str, Any]:
        """Parse a TradingView alert message into PULSE parameters.

        Args:
            raw: Raw alert body (JSON string or plain text)

        Returns:
            Dict with keys: pair, direction, price, interval, confidence,
            symbol, source

        Raises:
            ValueError: If the alert cannot be parsed
        """
        raw = raw.strip()

        # Try JSON first
        if raw.startswith("{"):
            try:
                data = json.loads(raw)
                return self._parse_json(data)
            except json.JSONDecodeError as e:
                raise ValueError(f"Invalid JSON in alert: {e}")

        # Fallback: plain text "BUY BTCUSDT" or "SELL ETHUSDT 3500"
        return self._parse_text(raw)

    def _parse_json(self, data: Dict[str, Any]) -> Dict[str, Any]:
        """Parse structured JSON alert."""
        # Action → direction
        action_raw = str(data.get("action", data.get("side", ""))).lower().strip()
        direction = ACTION_MAP.get(action_raw)
        if not direction:
            raise ValueError(
                f"Unknown action '{action_raw}'. Use: buy, sell, long, short, close"
            )

        # Symbol → pair
        symbol = str(data.get("symbol", data.get("ticker", ""))).upper().strip()
        if not symbol:
            raise ValueError("Alert missing 'symbol' field")
        pair = self._symbol_to_pair(symbol)

        # Price
        price_raw = data.get("price", data.get("close", None))
        price = float(price_raw) if price_raw is not None else None

        # Interval
        interval_raw = str(data.get("interval", data.get("timeframe", ""))).strip()
        interval = INTERVAL_MAP.get(interval_raw, interval_raw) if interval_raw else None

        # Confidence (0-1)
        confidence_raw = data.get("confidence", data.get("strength", None))
        if confidence_raw is not None:
            confidence = float(confidence_raw)
            if confidence > 1.0:
                confidence = confidence / 100.0  # handle percentage format
        else:
            confidence = 1.0  # TradingView alerts are deterministic, full confidence

        # Extra passthrough fields
        extra = {
            k: v for k, v in data.items()
            if k not in ("action", "side", "symbol", "ticker", "price", "close",
                         "interval", "timeframe", "confidence", "strength")
        }

        result = {
            "pair": pair,
            "symbol": symbol,
            "direction": direction,
            "confidence": confidence,
            "source": "tradingview",
        }
        if price is not None:
            result["price"] = price
        if interval:
            result["interval"] = interval
        if extra:
            result["extra"] = extra

        return result

    def _parse_text(self, text: str) -> Dict[str, Any]:
        """Parse plain text alert like 'BUY BTCUSDT' or 'SELL ETHUSDT 3500'."""
        parts = text.upper().split()
        if len(parts) < 2:
            raise ValueError(
                f"Cannot parse alert text '{text}'. "
                "Expected format: 'BUY BTCUSDT' or JSON"
            )
        action_raw = parts[0].lower()
        direction = ACTION_MAP.get(action_raw)
        if not direction:
            raise ValueError(f"Unknown action '{parts[0]}'")

        symbol = parts[1]
        pair = self._symbol_to_pair(symbol)
        price = float(parts[2]) if len(parts) > 2 else None

        result = {
            "pair": pair,
            "symbol": symbol,
            "direction": direction,
            "confidence": 1.0,
            "source": "tradingview",
        }
        if price is not None:
            result["price"] = price
        return result

    def _symbol_to_pair(self, symbol: str) -> str:
        """Convert exchange symbol to PULSE pair format.

        BTCUSDT → BTC/USDT
        ETHBTC  → ETH/BTC
        BTC/USDT → BTC/USDT (already formatted)
        """
        if "/" in symbol:
            return symbol

        quotes = ["USDT", "USDC", "BUSD", "BTC", "ETH", "BNB", "EUR", "USD", "GBP"]
        for quote in quotes:
            if symbol.endswith(quote) and len(symbol) > len(quote):
                base = symbol[: -len(quote)]
                return f"{base}/{quote}"

        # Fallback: split at 3/4 chars
        if len(symbol) >= 6:
            return f"{symbol[:3]}/{symbol[3:]}"
        return symbol
