#!/usr/bin/env python3
"""
Bridge File Watcher - 替代 MT5 EA 事件触发器
通过轮询 bridge to_ea/ 目录处理请求，直接调用 MT5TradeServer EA 的协议。
适用场景：MT5 via Wine 时 OnTick/OnTimer 不触发。
"""

import argparse
import json
import os
import shutil
import sys
import time
from datetime import datetime, timezone
from pathlib import Path

PROJECT_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(PROJECT_ROOT / "src"))
sys.path.insert(0, str(PROJECT_ROOT))

MT5_FILES = Path.home() / "Library/Application Support/net.metaquotes.wine.metatrader5/drive_c/Program Files/MetaTrader 5/MQL5/Files"
CMD_FILE = MT5_FILES / "mt5_cmd.json"
RSP_FILE = MT5_FILES / "mt5_rsp.json"


def _now():
    return datetime.now(timezone.utc).isoformat()


def _send_ea(cmd: dict, timeout: float = 15.0) -> dict:
    """Send command to MT5TradeServer EA via mt5_cmd.json, read from mt5_rsp.json."""
    if RSP_FILE.exists():
        try:
            RSP_FILE.unlink()
        except Exception:
            pass
    CMD_FILE.write_text(json.dumps(cmd, ensure_ascii=False), encoding="utf-8")
    start = time.time()
    while time.time() - start < timeout:
        if RSP_FILE.exists():
            try:
                return json.loads(RSP_FILE.read_text(encoding="utf-8"))
            except Exception:
                pass
        time.sleep(0.2)
    return {"success": False, "message": "MT5 EA timeout"}


def _ea_order(symbol: str, action: str, side: str, volume: float, sl: float, tp: float) -> dict:
    """Place order via EA protocol."""
    cmd = {
        "action": action,
        "symbol": symbol,
        "volume": volume,
        "price": 0,
        "sl": sl,
        "tp": tp,
        "ts": _now(),
    }
    return _send_ea(cmd, timeout=20.0)


def _process_request(req_path: Path, archive_dir: Path) -> dict:
    """Process one bridge request file."""
    try:
        payload = json.loads(req_path.read_text(encoding="utf-8"))
    except Exception as e:
        return {"status": "REJECTED", "reason": f"INVALID_JSON:{e}"}

    signal = payload.get("signal", {})
    signal_id = payload.get("signal_id", req_path.stem)
    action = str(signal.get("action", "HOLD")).upper()
    side = str(signal.get("side", "NONE")).upper()
    symbol = str(signal.get("symbol", "XAUUSD"))
    volume = float(signal.get("volume_lots", 0.01))
    sl = float(signal.get("sl_price", 0))
    tp = float(signal.get("tp_price", 0))

    if action == "HOLD":
        return {"signal_id": signal_id, "status": "RECONCILED", "reason": "HOLD_NO_ORDER"}

    if action not in {"OPEN", "CLOSE"}:
        return {"signal_id": signal_id, "status": "REJECTED", "reason": f"UNSUPPORTED_ACTION:{action}"}

    if action == "OPEN" and side in {"BUY", "SELL"}:
        ea_result = _ea_order(symbol, action, side, volume, sl, tp)
        if ea_result.get("success"):
            return {
                "signal_id": signal_id,
                "status": "FILLED",
                "reason": "ORDER_FILLED",
                "order_id": ea_result.get("order_id", 0),
                "deal_id": ea_result.get("deal_id", 0),
            }
        else:
            return {
                "signal_id": signal_id,
                "status": "REJECTED",
                "reason": ea_result.get("message", "ORDER_FAILED"),
            }

    if action == "CLOSE":
        ea_result = _ea_order(symbol, "close", side, volume, 0, 0)
        if ea_result.get("success"):
            return {"signal_id": signal_id, "status": "FILLED", "reason": "CLOSED"}
        return {"signal_id": signal_id, "status": "REJECTED", "reason": ea_result.get("message", "CLOSE_FAILED")}

    return {"signal_id": signal_id, "status": "REJECTED", "reason": f"UNHANDLED:{action}"}


def main():
    parser = argparse.ArgumentParser(description="Bridge File Watcher")
    parser.add_argument("--poll-seconds", type=float, default=2.0)
    parser.add_argument("--once", action="store_true")
    parser.add_argument("--verbose", "-v", action="store_true")
    args = parser.parse_args()

    # Bridge paths
    from mt5_execution import _ea_bridge_paths
    paths = _ea_bridge_paths(PROJECT_ROOT)
    to_ea = paths["to_ea"]
    from_ea = paths["from_ea"]
    archive = paths["archive"]

    print(f"[BridgeWatcher] MT5 Files: {MT5_FILES}")
    print(f"[BridgeWatcher] to_ea:   {to_ea}")
    print(f"[BridgeWatcher] from_ea: {from_ea}")
    print(f"[BridgeWatcher] archive: {archive}")

    # Test EA connection
    test = _send_ea({"action": "ping"}, timeout=5)
    if test.get("success"):
        print(f"[BridgeWatcher] ✅ MT5TradeServer EA connected")
    else:
        print(f"[BridgeWatcher] ⚠️  EA not responding: {test.get('message','')}")
        print(f"[BridgeWatcher] (If EA is loaded, this is normal on first run)")

    processed = 0

    def scan():
        nonlocal processed
        for f in to_ea.glob("*.request.json"):
            sig_id = f.stem
            report_path = from_ea / f"{sig_id}.report.json"
            if report_path.exists():
                continue
            if args.verbose:
                print(f"[BridgeWatcher] → Processing {f.name}")
            result = _process_request(f, archive)
            result["ts"] = _now()
            report_path.write_text(json.dumps(result, ensure_ascii=False), encoding="utf-8")
            shutil.move(str(f), str(archive / f.name))
            if args.verbose:
                print(f"[BridgeWatcher] ← {result.get('status')} ({result.get('reason','')})")
            processed += 1

    if args.once:
        scan()
        print(f"[BridgeWatcher] Done. Processed {processed}.")
        return 0

    print(f"[BridgeWatcher] Watching... (Ctrl+C to stop)")
    try:
        while True:
            scan()
            if processed:
                print(f"[BridgeWatcher] {processed} processed")
                processed = 0
            time.sleep(args.poll_seconds)
    except KeyboardInterrupt:
        print(f"\n[BridgeWatcher] Stopped.")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
