"""
price_digits — adaptive price rounding based on broker digit precision.

Some brokers quote XAUUSD with 2 decimal places (e.g., 2900.12),
others with 3 (e.g., 2900.123). MT5 provides symbol_info().digits
to indicate the correct precision.

This module:
1. Reads symbol_digits from MT5 on first access (cached)
2. Falls back to auto-detection from price range (2900 → 2 digits, 2900.1 → 1)
3. Provides round_price() and format_price() for consistent rounding
"""

from __future__ import annotations

import threading
from typing import Optional

# Per-symbol cache — keyed by symbol name, thread-safe
_cache: dict[str, tuple[int, float]] = {}
_lock = threading.Lock()

# Defaults per category (from classify_symbol fallback)
_CATEGORY_DEFAULTS: dict[str, tuple[int, float]] = {
    "metal":     (2, 0.01),
    "crypto":    (2, 0.01),   # BTC: 2 decimals (e.g., 64123.45)
    "forex":     (5, 0.00001),
    "index":     (1, 0.1),
    "commodity": (2, 0.01),
}


def _classify_fallback(symbol: str) -> tuple[int, float]:
    """Fallback: use classify_symbol to get per-category defaults."""
    try:
        from src.symbol_resolver import classify_symbol
        meta = classify_symbol(symbol)
        cat = meta.get("category", "metal")
        return _CATEGORY_DEFAULTS.get(cat, (2, 0.01))
    except Exception:
        return (2, 0.01)


def _detect(symbol: str) -> tuple[int, float]:
    """Detect digits/point from MT5 for a specific symbol."""
    digits = 2
    point = 0.01
    try:
        import MetaTrader5 as mt5
        if mt5.initialize():
            info = mt5.symbol_info(symbol)
            if info is not None:
                digits = int(getattr(info, "digits", 2) or 2)
                point = float(getattr(info, "point", 0.01) or 0.01)
            mt5.shutdown()
    except Exception:
        # MT5 unavailable — use classify_symbol fallback
        digits, point = _classify_fallback(symbol)
    if digits < 1:
        digits = 2
    if point <= 0:
        point = 10.0 ** (-digits)
    return digits, point


def detect_symbol_digits(*, preferred_symbol: str = "XAUUSD") -> tuple[int, float]:
    """Detect the decimal digit count from MT5 for the given symbol.

    Cached per-symbol — different instruments may have different precisions.
    """
    sym = (preferred_symbol or "XAUUSD").strip().upper()
    if sym in _cache:
        return _cache[sym]
    with _lock:
        if sym in _cache:
            return _cache[sym]
        result = _detect(sym)
        _cache[sym] = result
        return result


def get_symbol_digits(*, preferred_symbol: str = "XAUUSD") -> int:
    """Get the correct number of decimal digits for price rounding."""
    sym = (preferred_symbol or "XAUUSD").strip().upper()
    if sym in _cache:
        return _cache[sym][0]
    digits, _ = detect_symbol_digits(preferred_symbol=sym)
    return digits


def get_point(*, preferred_symbol: str = "XAUUSD") -> float:
    """Get the point size (e.g., 0.01 for 2-digit, 0.001 for 3-digit)."""
    sym = (preferred_symbol or "XAUUSD").strip().upper()
    if sym in _cache:
        return _cache[sym][1]
    _, point = detect_symbol_digits(preferred_symbol=sym)
    return point


def round_price(price: float, *, preferred_symbol: str = "XAUUSD") -> float:
    """Round a price to the correct number of decimal places for this symbol.

    >>> round_price(2900.1234)                     # XAUUSD → 2900.12
    >>> round_price(64123.456, preferred_symbol='BTCUSD')  # BTC → 64123.46
    """
    digits = get_symbol_digits(preferred_symbol=preferred_symbol)
    return round(price, digits)


def format_price(price: float, *, preferred_symbol: str = "XAUUSD") -> str:
    """Format a price to the correct decimal places for display/API."""
    digits = get_symbol_digits(preferred_symbol=preferred_symbol)
    return f"{price:.{digits}f}"
