"""Cross-module symbol normalisation and instrument matching for gold_trader."""
from __future__ import annotations

from typing import Any, Optional

_CANONICAL_ALIASES = {
    "GOLD": "XAUUSD",
    "XAUUSDUSD": "XAUUSD",
}

_COMMON_BROKER_SUFFIXES = {
    "M",
    "I",
    "R",
    "PRO",
    "RAW",
    "ECN",
    "STD",
    "MICRO",
    "MINI",
    "CASH",
}


def _apply_symbol_aliases(symbol: str) -> str:
    normalized = str(symbol or "").strip().upper()
    return _CANONICAL_ALIASES.get(normalized, normalized)


def _looks_like_broker_suffix(suffix: str) -> bool:
    cleaned = str(suffix or "").strip().upper().strip("._-+#")
    if not cleaned:
        return False
    if cleaned in _COMMON_BROKER_SUFFIXES:
        return True
    # Common MT5 brokers use one trailing marker like XAUUSDm / XAUUSDi.
    return len(cleaned) == 1 and cleaned.isalnum()


def normalize_symbol(symbol: Any, *, default: str = "XAUUSD") -> str:
    """Return the canonical uppercase form of a symbol string."""
    if not symbol:
        return default
    s = _apply_symbol_aliases(str(symbol).strip().upper())
    return s if s else default


def symbol_matches_preferred(candidate_symbol: str, preferred_symbol: str) -> bool:
    """True when *candidate_symbol* equals or starts with *preferred_symbol*
    and any trailing characters form a known broker suffix (e.g. XAUUSDm)."""
    c = normalize_symbol(candidate_symbol, default="")
    p = normalize_symbol(preferred_symbol, default="XAUUSD") or "XAUUSD"
    if not c:
        return False
    if c == p:
        return True
    if c.startswith(p):
        suffix = c[len(p):]
        if suffix and all(not ch.isalnum() for ch in suffix):
            return True
        if _looks_like_broker_suffix(suffix):
            return True
    return False



# ==========================================
# Symbol metadata for LLM prompt adaptation
# ==========================================

def classify_symbol(symbol: str) -> dict:
    """Return asset category, pip_size, and prompt context for any trading symbol.
    
    Uses PATTERN MATCHING on symbol name, NOT a fixed lookup table.
    Any new symbol is auto-classified without code changes.
    """
    s = normalize_symbol(symbol, default="")
    
    # Crypto: contains BTC, ETH, XRP, SOL, or ends with USD/USDT and has >6 chars
    crypto_hints = {"BTC", "ETH", "XRP", "SOL", "ADA", "DOGE", "DOT", "LTC", "BCH", "LINK", "UNI", "AVAX", "MATIC", "CRYPTO"}
    if any(h in s.upper() for h in crypto_hints):
        return {
            "category": "crypto",
            "category_cn": "加密货币",
            "pip_size": 1.0,
            "typical_spread": 200,
            "characteristics": "extremely volatile, driven by risk sentiment, regulatory news, and on-chain metrics",
            "macro_drivers": "global risk appetite, regulatory developments, institutional adoption, BTC ETF flows, Fed policy on risk assets",
            "dxy_relevant": False,
        }
    
    # Precious metals: XAU, XAG, GOLD, SILVER, XPT, XPD
    metal_hints = {"XAU", "XAG", "GOLD", "SILVER", "XPT", "XPD", "PLATINUM", "PALLADIUM"}
    if any(h in s.upper() for h in metal_hints):
        return {
            "category": "metal",
            "category_cn": "贵金属",
            "pip_size": 0.01,
            "typical_spread": 50,
            "characteristics": "safe-haven asset, inverse correlation with USD, driven by real yields and geopolitical risk",
            "macro_drivers": "US Dollar Index (DXY), real interest rates, inflation expectations, central bank gold reserves, geopolitical tensions",
            "dxy_relevant": True,
        }
    
    # Forex: standard 6-char pairs ending in currency codes
    forex_currencies = {"USD", "EUR", "JPY", "GBP", "AUD", "CAD", "CHF", "NZD", "CNH", "MXN", "ZAR", "TRY", "SEK", "NOK", "SGD", "HKD"}
    if len(s) == 6 and s[:3] in forex_currencies and s[3:] in forex_currencies:
        return {
            "category": "forex",
            "category_cn": "外汇",
            "pip_size": 0.0001 if "JPY" not in s[3:] else 0.01,
            "typical_spread": 2,
            "characteristics": "mean-reverting tendencies, driven by interest rate differentials and economic data",
            "macro_drivers": "central bank policy divergence, economic data surprises (NFP, CPI, GDP), bond yield spreads, trade flows",
            "dxy_relevant": True if "USD" in s else False,
        }
    
    # Indices: US30, SPX500, NAS100, GER40, UK100, JPN225, etc.
    index_hints = {"US30", "SPX", "NAS", "GER", "UK", "JPN", "DJI", "NDX", "DAX", "FTSE", "NIKKEI", "HSI", "A50"}
    if any(h in s.upper() for h in index_hints):
        return {
            "category": "index",
            "category_cn": "指数",
            "pip_size": 1.0,
            "typical_spread": 15,
            "characteristics": "trending instrument, driven by macro sentiment and earnings expectations",
            "macro_drivers": "economic growth outlook, corporate earnings, central bank policy, sector rotation, VIX",
            "dxy_relevant": False,
        }
    
    # Default: treat as generic CFD/commodity
    return {
        "category": "commodity",
        "category_cn": "商品",
        "pip_size": 0.01,
        "typical_spread": 30,
        "characteristics": "commodity instrument, driven by supply-demand dynamics and macro cycles",
        "macro_drivers": "supply-demand balance, inventory data, macro sentiment, seasonality",
        "dxy_relevant": True if len(s) <= 6 else False,
    }

def pick_best_instrument(
    instruments: dict[str, Any],
    preferred_symbol: str,
) -> tuple[Optional[str], Optional[dict[str, Any]]]:
    """Select the instrument dict whose key best matches *preferred_symbol*.

    Returns ``(matched_symbol, payload)`` or ``(None, None)``.
    """
    best = None
    best_key: Optional[str] = None

    for key, val in instruments.items():
        if not isinstance(val, dict):
            continue
        if symbol_matches_preferred(key, preferred_symbol):
            # Exact match wins immediately
            if str(key).strip().upper() == preferred_symbol:
                return key, val
            if best is None:
                best = val
                best_key = key

    if best is not None:
        return best_key, best

    # Fallback: first instrument whose key contains the preferred symbol
    for key, val in instruments.items():
        if not isinstance(val, dict):
            continue
        if preferred_symbol in str(key).strip().upper():
            return key, val

    return None, None
