#!/usr/bin/env python3
"""
MT5 Reload Loop - 生产级持续自动交易核心
原理：监控 to_ea/ 请求目录 → 有新请求 → 自动重启 MT5 → EA OnInit 处理 → 循环
替代 EA OnTimer，绕过 Wine 后台 OnTimer 不触发的限制
"""

import os
import subprocess
import sys
import time
import shutil
from datetime import datetime, timezone
from pathlib import Path

PROJECT_ROOT = Path(__file__).resolve().parents[1]
MT5_FILES = Path.home() / "Library/Application Support/net.metaquotes.wine.metatrader5/drive_c/Program Files/MetaTrader 5/MQL5/Files"
MT5_APP = "/Applications/MetaTrader 5.app"

TIMER_SECONDS = 30  # 每 N 秒检查一次
LOG_DIR = PROJECT_ROOT / "logs"
LOG_DIR.mkdir(parents=True, exist_ok=True)
LOG_FILE = LOG_DIR / f"reload_loop.{datetime.now(timezone.utc).strftime('%Y%m%d')}.log"


def log(msg: str) -> None:
    ts = datetime.now(timezone.utc).strftime("%H:%M:%S")
    line = f"[{ts}] {msg}"
    print(line)
    with LOG_FILE.open("a", encoding="utf-8") as f:
        f.write(line + "\n")


def get_mt5_pid() -> int:
    try:
        return int(subprocess.check_output(
            ["osascript", "-e", 'tell application "System Events" to unix id of process "MetaTrader 5"'],
            text=True
        ).strip())
    except Exception:
        return 0


def mt5_is_running() -> bool:
    return get_mt5_pid() > 0


def mt5_restart() -> bool:
    """Kill MT5 → wait 3s → restart. Returns True if successful."""
    pid = get_mt5_pid()
    if pid > 0:
        try:
            subprocess.run(["kill", str(pid)], timeout=5)
            log(f"MT5 killed (PID {pid})")
        except Exception as e:
            log(f"kill failed: {e}")
            return False
        time.sleep(3)

    # Start MT5
    try:
        subprocess.Popen(["open", "-a", "MetaTrader 5"],
                       stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        log("MT5 started")
        time.sleep(8)  # wait for MT5 + EA to initialize
        return True
    except Exception as e:
        log(f"MT5 start failed: {e}")
        return False


def scan_and_reload() -> bool:
    """
    Scan to_ea/ for new request files.
    If found, restart MT5 to trigger EA OnInit processing.
    Returns True if reload was triggered.
    """
    to_ea = MT5_FILES / "gold_autotrade/bridge/to_ea"
    if not to_ea.exists():
        return False

    files = sorted(to_ea.glob("*.request.json"), key=lambda p: p.stat().st_mtime)
    if not files:
        return False

    # Find most recent file
    newest = files[-1]
    age = time.time() - newest.stat().st_mtime

    # Only reload if newest file is less than 5 minutes old (active request)
    # AND hasn't been processed yet (not in archive)
    archive = PROJECT_ROOT / f"data/bridge/archive/{newest.name}"
    if archive.exists():
        return False  # already processed

    # If file is old (> 5min) and not processed, still process it (deadline recovery)
    log(f"Found request: {newest.name} (age={age:.0f}s)")
    return True


def process_via_mt5_restart() -> None:
    """Restart MT5 so EA's OnInit processes pending requests."""
    log("⚡ Triggering MT5 restart to process requests...")
    if mt5_restart():
        log("✅ MT5 restarted, EA OnInit will process requests")
    else:
        log("❌ MT5 restart failed")


# ─── Main loop ───────────────────────────────────────────────────────────────
def main() -> int:
    log(f"=== MT5 Reload Loop started ===")
    log(f"Timer: {TIMER_SECONDS}s | Scan: {MT5_FILES}")
    log(f"PID watcher: every cycle")

    cycle = 0
    last_reload = 0
    reload_cooldown = 60  # minimum 60s between restarts

    while True:
        cycle += 1
        now = time.time()

        # ── Check if MT5 is running ──
        if not mt5_is_running():
            log("⚠️  MT5 not running, attempting restart...")
            mt5_restart()
            time.sleep(10)
            continue

        # ── Scan for pending requests ──
        needs_reload = scan_and_reload()

        if needs_reload and (now - last_reload) > reload_cooldown:
            log(f"Cycle {cycle}: request pending → reloading MT5")
            process_via_mt5_restart()
            last_reload = now
            time.sleep(5)  # wait for EA OnInit to finish
        elif needs_reload:
            remaining = int(reload_cooldown - (now - last_reload))
            log(f"Cycle {cycle}: request pending but cooldown {remaining}s, skipping")
        else:
            log(f"Cycle {cycle}: no pending requests, sleeping {TIMER_SECONDS}s")

        time.sleep(TIMER_SECONDS)


if __name__ == "__main__":
    try:
        raise SystemExit(main())
    except KeyboardInterrupt:
        log("=== MT5 Reload Loop stopped ===")
