#!/usr/bin/env python3
"""
Exit Decision Agent - 本地 Qwen3.5 模型做持仓平仓决策
每10秒由 cron 触发，调用本地模型判断是否平仓
"""
import json, sys, pathlib, urllib.request, subprocess, time

ROOT = pathlib.Path(__file__).parent.parent.absolute()
MT5_COMMON = pathlib.Path.home() / "Library/Application Support/net.metaquotes.wine.metatrader5/drive_c/users/user/AppData/Roaming/MetaQuotes/Terminal/Common/Files"
LOG_DIR = ROOT / "logs"

def get_price():
    try:
        url = "https://forex-data-feed.swissquote.com/public-quotes/bboquotes/instrument/XAU/USD"
        req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
        with urllib.request.urlopen(req, timeout=5) as resp:
            data = json.loads(resp.read())
        profiles = data[0].get("spreadProfilePrices", [])
        if profiles:
            return float(profiles[0]["bid"]), float(profiles[0]["ask"])
        return float(data[0]["bids"][0]["price"]), float(data[0]["asks"][0]["price"])
    except:
        return None, None

def get_position():
    sf = MT5_COMMON / "gold_autotrade/bridge/terminal_status.json"
    if not sf.exists(): return None
    try:
        data = json.loads(sf.read_text())
        positions = data.get("positions", [])
        if not positions: return None
        return positions[0]
    except: return None

def get_trade_state():
    """读取 Python daemon 的状态"""
    state_file = LOG_DIR / ".agent_state.json"
    if not state_file.exists(): return {}
    try: return json.loads(state_file.read_text())
    except: return {}

def call_qwen(prompt, timeout=15):
    """调用本地 Qwen3.5 模型"""
    payload = {
        "model": "Qwen3.5-4B-MLX-4bit",
        "prompt": prompt,
        "max_tokens": 200,
        "temperature": 0.3,
    }
    try:
        req = urllib.request.Request(
            "http://127.0.0.1:8000/v1/completions",
            data=json.dumps(payload).encode(),
            headers={"Authorization": "Bearer 786540", "Content-Type": "application/json"},
            method="POST"
        )
        with urllib.request.urlopen(req, timeout=timeout) as r:
            result = json.loads(r.read())
            return result.get("choices", [{}])[0].get("text", "").strip()
    except Exception as e:
        return f"[ERROR: {e}]"

def format_prompt(position, price_bid, price_ask):
    """构建发送给模型的决策提示"""
    side = position.get("side", "BUY")
    entry = position.get("entry", 0)
    sl = position.get("sl", 0)
    tp = position.get("tp", 0)
    vol = position.get("volume", 0.01)
    ticket = position.get("ticket", 0)

    if side == "BUY":
        pnl_pts = round((price_bid - entry) * 100, 1)
        pnl_dol = round((price_bid - entry) * 100 * vol, 2)
        dist_to_tp = round((tp - price_ask) * 100, 1)
        dist_to_sl = round((price_bid - sl) * 100, 1)
        tp_pct = round((price_bid - entry) / max(tp - entry, 0.001) * 100, 1)
    else:
        pnl_pts = round((entry - price_ask) * 100, 1)
        pnl_dol = round((entry - price_ask) * 100 * vol, 2)
        dist_to_tp = round((price_bid - tp) * 100, 1)
        dist_to_sl = round((sl - price_ask) * 100, 1)
        tp_pct = round((entry - price_ask) / max(entry - tp, 0.001) * 100, 1)

    spread = round((price_ask - price_bid) * 100, 1)

    return f"""你是黄金交易平仓决策Agent。持仓信息：

Ticket: #{ticket}
方向: {side}
开仓价: {entry}
当前报价: bid={price_bid} ask={price_ask}
浮动盈亏: {pnl_pts}点 (约${pnl_dol})
止损: {sl} (距离{dist_to_sl}点)
止盈: {tp} (距离{dist_to_tp}点)
TP覆盖: {tp_pct}%
点差: {spread}

请决定：
- CLOSE = 平仓（止损/止盈/锁定利润/风险过大）
- HOLD = 持仓（趋势仍在/未到目标/风险可控）
- TRAIL = 移动止损

直接输出一个词：CLOSE / HOLD / TRAIL
不要解释，不要理由，只要动作词。"""

def decide_exit():
    """主决策流程"""
    # 1. 获取价格
    bid, ask = get_price()
    if bid is None:
        return {"action": "ERROR", "reason": "无法获取价格"}

    # 2. 获取持仓
    position = get_position()
    if not position:
        return {"action": "NO_POSITION"}

    # 3. 获取 Python daemon 状态
    state = get_trade_state()
    kill_switch = state.get("kill_switch", False)
    if kill_switch:
        return {"action": "HOLD", "reason": "Kill Switch已触发"}

    # 4. 构建提示
    prompt = format_prompt(position, bid, ask)

    # 5. 调用本地 Qwen3.5 模型
    decision = call_qwen(prompt)
    decision = decision.upper().strip()

    if "CLOSE" in decision:
        return {
            "action": "CLOSE",
            "reason": f"模型决策: {decision[:50]}",
            "position": position,
            "price": {"bid": bid, "ask": ask},
        }
    elif "TRAIL" in decision:
        return {
            "action": "TRAIL",
            "reason": f"模型决策: {decision[:50]}",
            "position": position,
            "price": {"bid": bid, "ask": ask},
        }
    else:
        side = position.get("side", "?")
        pnl = (bid - position.get("entry", 0)) * 100 if side == "BUY" else (position.get("entry", 0) - ask) * 100
        return {
            "action": "HOLD",
            "reason": f"模型决策: {decision[:50]}",
            "position": position,
            "price": {"bid": bid, "ask": ask},
            "pnl_points": round(pnl, 1),
        }

if __name__ == "__main__":
    result = decide_exit()
    action = result.get("action", "ERROR")

    if action == "NO_POSITION":
        print("无持仓，静默退出")
        sys.exit(0)
    elif action == "ERROR":
        print(f"错误: {result.get('reason')}")
        sys.exit(1)
    elif action == "HOLD":
        pnl = result.get("pnl_points", 0)
        print(f"HOLD | 浮盈{ pnl}点")
        sys.exit(0)
    elif action == "CLOSE":
        print(f"CLOSE | {result.get('reason')}")
        # 写入平仓信号到 daemon 触发
        from_bridge = MT5_COMMON / "gold_autotrade/bridge/from_ea"
        close_file = from_bridge / f"agent_close_{int(time.time())}.report.json"
        try:
            close_file.parent.mkdir(parents=True, exist_ok=True)
            close_file.write_text(json.dumps({
                "signal_id": f"agent_close_{int(time.time())}",
                "action": "CLOSE",
                "status": "AGENT_DECISION",
                "reason": result.get("reason", ""),
                "ticket": result.get("position", {}).get("ticket", 0),
            }))
            print(f"已写入平仓信号: {close_file.name}")
        except Exception as e:
            print(f"写入平仓信号失败: {e}")
        sys.exit(0)
    elif action == "TRAIL":
        print(f"TRAIL | {result.get('reason')}")
        sys.exit(0)
