"""
================================================================================
Quantum Terminal — Data Server Configuration
================================================================================
Central config for the FastAPI data server (Phase 3C).

All paths are relative to PROJECT_ROOT for portability (Rule 3).
MT5 terminal path is read from local_config.ini (gitignored, Rule 3).
Universe must match the orchestrator's ASSET_UNIVERSE.

Usage:
    from server_config import ServerConfig, PROJECT_ROOT
    config = ServerConfig()
================================================================================
"""

import configparser
from pathlib import Path
from dataclasses import dataclass, field
from typing import List, Optional, Dict

# ── Portable project root (Rule 3) ──
PROJECT_ROOT = Path(__file__).resolve().parent


# ── Load local_config.ini for machine-specific values (Rule 3) ──
def _load_mt5_path() -> Optional[str]:
    """
    Read MT5 terminal path from local_config.ini.
    Falls back to None (MT5 auto-detect) if file or key is missing.
    
    Expected format in local_config.ini:
        [mt5]
        terminal_path = C:/Program Files/MetaTrader 5/terminal64.exe
    """
    ini_path = PROJECT_ROOT / "local_config.ini"
    if not ini_path.exists():
        return None
    cp = configparser.ConfigParser()
    cp.read(str(ini_path))
    return cp.get("mt5", "terminal_path", fallback=None)


MT5_TERMINAL_PATH = _load_mt5_path()


# ── Asset universe — must stay in sync with orchestrator.py ──
ASSET_UNIVERSE = [
    # Forex
    "EURUSD", "GBPUSD", "USDJPY", "AUDUSD", "USDCHF", "USDCAD",
    # Indices
    "US500", "USTEC", "GER40", "UK100",
    # Commodities
    "XAUUSD", "XAGUSD", "XTIUSD",
]


# ── Symbol alias map ──
# Canonical name → list of possible broker names to try (in order).
# The adapter tries each until one resolves in the connected MT5 terminal.
# Only needed for symbols whose canonical name doesn't match the broker.
# Forex pairs (EURUSD etc.) are universal and don't need aliases.
SYMBOL_ALIASES: Dict[str, List[str]] = {
    "US500":  ["US500", "US500.cash", "SPX500", "SP500", "US500Cash", "USA500"],
    "USTEC":  ["USTEC", "USTEC.cash", "NAS100", "NSDQ100", "USTECCash", "USTECH"],
    "GER40":  ["GER40", "GER40.cash", "DAX40", "DE40", "GER40Cash", "GDAXI"],
    "UK100":  ["UK100", "UK100.cash", "FTSE100", "UK100Cash"],
    "XTIUSD": ["XTIUSD", "USOIL", "WTI", "XTIUSD.cash", "USOIL.cash", "CL"],
    "XBRUSD": ["XBRUSD", "UKOIL", "BRENT", "XBRUSD.cash", "UKOIL.cash"],
    "XAUUSD": ["XAUUSD", "GOLD", "XAUUSD.cash"],
    "XAGUSD": ["XAGUSD", "SILVER", "XAGUSD.cash"],
}


# ── MT5 timeframe mapping (string → MT5 constant) ──
# These are the actual MetaTrader5 Python API constants.
# M1-M30 happen to equal their minute value, but H1+ use encoded values.
MT5_TIMEFRAMES = {
    "M1":  1,      # mt5.TIMEFRAME_M1
    "M5":  5,      # mt5.TIMEFRAME_M5
    "M15": 15,     # mt5.TIMEFRAME_M15
    "M30": 30,     # mt5.TIMEFRAME_M30
    "H1":  16385,  # mt5.TIMEFRAME_H1
    "H4":  16388,  # mt5.TIMEFRAME_H4
    "D1":  16408,  # mt5.TIMEFRAME_D1
    "W1":  32769,  # mt5.TIMEFRAME_W1
}


# ── Decimal precision per asset class ──
ASSET_DECIMALS = {
    # Forex — 5 decimal places (pipettes)
    "EURUSD": 5, "GBPUSD": 5, "AUDUSD": 5, "USDCHF": 5, "USDCAD": 5,
    "USDJPY": 3,
    # Indices — 2 decimal places
    "US500": 2, "USTEC": 2, "GER40": 2, "UK100": 2,
    # Commodities
    "XAUUSD": 2, "XAGUSD": 3, "XTIUSD": 2,
}


# ── Asset class classification ──
ASSET_CLASSES = {
    "EURUSD": "FX", "GBPUSD": "FX", "USDJPY": "FX",
    "AUDUSD": "FX", "USDCHF": "FX", "USDCAD": "FX",
    "US500": "INDEX", "USTEC": "INDEX", "GER40": "INDEX", "UK100": "INDEX",
    "XAUUSD": "COMMODITY", "XAGUSD": "COMMODITY",
    "XTIUSD": "COMMODITY",
    # Futures (Rithmic)
    "ES": "FUTURES", "NQ": "FUTURES", "YM": "FUTURES",
    "GC": "FUTURES", "SI": "FUTURES", "CL": "FUTURES", "BZ": "FUTURES",
}


@dataclass
class ServerConfig:
    """
    Master configuration for the data server.
    All file paths are relative to PROJECT_ROOT.
    MT5 terminal path comes from local_config.ini (Rule 3).
    """

    # ── Network ──
    host: str = "127.0.0.1"
    port: int = 8501

    # ── MT5 ──
    mt5_terminal_path: Optional[str] = field(default_factory=lambda: MT5_TERMINAL_PATH)

    # ── Universe ──
    universe: List[str] = field(default_factory=lambda: list(ASSET_UNIVERSE))

    # ── MT5 polling intervals (seconds) ──
    tick_interval: float = 1.0        # How often to poll latest ticks
    bar_check_interval: float = 5.0   # How often to check for new closed bars

    # ── State file paths (relative to PROJECT_ROOT) ──
    terminal_payload: str = "terminal_payload.json"
    daily_signals: str = "daily_signals.json"
    signal_lifecycle: str = "signal_lifecycle.json"
    weekly_state: str = "weekly_state.json"
    bands_data_dir: str = "bands_data"

    # ── Display defaults ──
    default_timeframe: str = "M15"
    default_lookback_bars: int = 200

    # ── Frontend CORS ──
    cors_origins: List[str] = field(default_factory=lambda: [
        "http://localhost:3000",
        "http://127.0.0.1:3000",
    ])

    # ── Reconnection ──
    mt5_reconnect_delay: float = 5.0      # Seconds between reconnect attempts
    mt5_max_reconnect_attempts: int = 10   # Then log error, keep trying

    # ── File watcher ──
    file_watch_debounce: float = 1.0  # Seconds to debounce rapid file changes

    def resolve_path(self, relative: str) -> Path:
        """Resolve a config path relative to PROJECT_ROOT."""
        return PROJECT_ROOT / relative

    @property
    def watched_files(self) -> List[Path]:
        """All state files that the file watcher should monitor."""
        return [
            self.resolve_path(self.terminal_payload),
            self.resolve_path(self.daily_signals),
            self.resolve_path(self.signal_lifecycle),
            self.resolve_path(self.weekly_state),
        ]