"""
网格交易策略模块 v1.0
适用于 4350-4450 宽幅震荡区间

网格设置:
  SHORT网格: 入4440-4448 | SL=4475 | TP=4375/4355
  BUY网格:   入4360-4368 | SL=4335 | TP=4390/4410

逻辑: 在区间高抛低吸，每笔0.05-0.1手
"""

import time
import json
import pathlib
import config_manager
from datetime import datetime, timezone, timedelta

try:
    from src.market_context import is_defensive_regime, is_range_regime, is_trend_regime, normalize_regime_label
except ModuleNotFoundError:
    from market_context import is_defensive_regime, is_range_regime, is_trend_regime, normalize_regime_label

# ============ 配置（从 Agent 决策动态加载，不硬编码）============
# 默认值（Agent 无决策时兜底用），实际以 Agent 决策文件为准
_DEFAULT_GRID_ZONES = {
    "short_zone": {
        "enter_min": 4440, "enter_max": 4450,
        "sl": 4470, "tp1": 4370, "tp2": 4350
    },
    "buy_zone": {
        "enter_min": 4350, "enter_max": 4360,
        "sl": 4330, "tp1": 4390, "tp2": 4410
    }
}

# 动态加载的 zones（每次 check_grid_signals 调用时从 Agent 决策读取）
_GRID_ZONES_CACHE = None
_GRID_ZONES_LOADED_AT = 0


def _load_grid_zones_from_agent():
    """从 Agent 决策文件读取网格区域，缓存5分钟，避免每次都读文件"""
    global _GRID_ZONES_CACHE, _GRID_ZONES_LOADED_AT
    now = time.time()
    if _GRID_ZONES_CACHE is not None and (now - _GRID_ZONES_LOADED_AT) < 300:
        return _GRID_ZONES_CACHE
    try:
        import sys
        from frozen_path import LOGS_DIR
        AGENT_DIR = LOGS_DIR / "agent_decisions"
        today = time.strftime("%Y%m%d")
        decision_file = AGENT_DIR / f"direction_{today}.json"
        if decision_file.exists():
            data = json.loads(decision_file.read_text(encoding="utf-8"))
            trade_plan = data.get("trade_plan", {})
            grid_zones = trade_plan.get("grid_zones", {})
            if grid_zones:
                # 解析 "4440-4450" 格式
                def parse_zone(z_str):
                    parts = str(z_str).split("-")
                    if len(parts) == 2:
                        return float(parts[0]), float(parts[1])
                    return None, None
                sell_min, sell_max = parse_zone(grid_zones.get("sell_zone", ""))
                buy_min, buy_max = parse_zone(grid_zones.get("buy_zone", ""))
                stop_above = float(grid_zones.get("stop_above", 4470))
                if sell_min and sell_max:
                    _GRID_ZONES_CACHE = {
                        "short_zone": {
                            "enter_min": sell_min, "enter_max": sell_max,
                            "sl": stop_above, "tp1": sell_min - 70, "tp2": sell_min - 100
                        },
                        "buy_zone": {
                            "enter_min": buy_min, "enter_max": buy_max,
                            "sl": buy_min - 30, "tp1": buy_max + 40, "tp2": buy_max + 60
                        }
                    }
                    _GRID_ZONES_LOADED_AT = now
                    _log(f"网格区域从Agent决策加载: SHORT={sell_min}-{sell_max} BUY={buy_min}-{buy_max}", "INFO")
                    return _GRID_ZONES_CACHE
    except Exception as e:
        _log(f"加载Agent网格区域失败: {e}", "WARN")
    _GRID_ZONES_CACHE = _DEFAULT_GRID_ZONES
    _GRID_ZONES_LOADED_AT = now
    return _GRID_ZONES_CACHE


GRID_CONFIG = {
    "enabled": True,
    "position_size": 0.05,
    "max_grid_positions": 3,
    "cooldown_seconds": 180,
    "stop_on_position": True
}

_logger = None

# ROOT now resolved via frozen_path
from frozen_path import APP_ROOT as ROOT
ARCHIVE_DIR = ROOT / "data" / "bridge" / "archive"
MT5_FILES = config_manager.get_mt5_dir()
BRIDGE_TO_EA = MT5_FILES / "gold_autotrade" / "bridge" / "to_ea"

def _log(msg, level="INFO"):
    if _logger:
        _logger.log(msg, level)
    else:
        ts = time.strftime("%H:%M:%S")
        print(f"[{ts}] [GRID] {msg}")


def _has_recent_grid_activity(side, zone_name, cooldown_seconds):
    now = time.time()
    signal_prefix = f"grid_{zone_name}_"
    reason = f"grid_{zone_name}"

    try:
        if BRIDGE_TO_EA.exists():
            for req_file in sorted(BRIDGE_TO_EA.glob("*.request.json"), key=lambda x: x.stat().st_mtime, reverse=True)[:30]:
                if now - req_file.stat().st_mtime > cooldown_seconds:
                    continue
                try:
                    payload = json.loads(req_file.read_text(encoding="utf-8"))
                except Exception:
                    continue
                if (
                    str(payload.get("action", "")).upper() == "OPEN"
                    and str(payload.get("side", "")).upper() == side
                    and (
                        str(payload.get("reason", "")) == reason
                        or str(payload.get("signal_id", "")).startswith(signal_prefix)
                    )
                ):
                    return True
    except Exception:
        pass

    try:
        if ARCHIVE_DIR.exists():
            for rep_file in sorted(ARCHIVE_DIR.glob("*.report*.json"), key=lambda x: x.stat().st_mtime, reverse=True)[:60]:
                if now - rep_file.stat().st_mtime > cooldown_seconds:
                    continue
                try:
                    payload = json.loads(rep_file.read_text(encoding="utf-8"))
                except Exception:
                    continue
                if (
                    str(payload.get("action", "")).upper() == "OPEN"
                    and str(payload.get("status", "")).upper() == "FILLED"
                    and str(payload.get("side", "")).upper() == side
                    and str(payload.get("signal_id", "")).startswith(signal_prefix)
                ):
                    return True
    except Exception:
        pass

    return False


def check_grid_signals(bid, ask, state, agent_dir, symbol: str = None):
    """
    检查是否应该挂网格单。
    symbol=None 时自动跟随 EA 图表品种。
    趋势单优先：agent_dir=SHORT 时禁止网格开 BUY，agent_dir=LONG 时禁止网格开 SELL。
    网格区域从 Agent 决策文件动态读取，不再硬编码。
    """
    if not symbol:
        from bridge.gold_trader import _detect_active_symbol
        symbol = _detect_active_symbol()
    if not GRID_CONFIG["enabled"]:
        return []

    positions_all = state.get("positions_all", []) or []
    if len(positions_all) >= int(GRID_CONFIG.get("max_grid_positions", 3) or 3):
        return []

    # 有持仓时不挂网格（网格和主趋势分开）
    if state.get("position_open") and GRID_CONFIG.get("stop_on_position", True):
        return []

    regime = normalize_regime_label(state.get("regime", "RANGING"))
    # 状态机：当 regime 是趋势、突破或动能突变时，停止开新网格单
    # 大周期过滤：结合 Agent 的宏观方向
    if is_trend_regime(regime) or regime in {"VOLATILITY_SPIKE", "EXTREME", "FAKE_TREND"}:
        _log(f"Regime is {regime}, grid suspended")
        return []
    if agent_dir in ("LONG", "SHORT") and not is_range_regime(regime):
        _log(f"Agent {agent_dir}, grid suspended")
        return []

    # 从 Agent 决策动态加载网格区域
    grid_zones = _load_grid_zones_from_agent()
    
    # ── 震荡市收紧网格条件 ──
    # 在极高波动率的震荡市（如截图中的乱砍阶段），我们必须收紧网格条件。
    # 既然主程序有了 COOLDOWN，网格本身也必须检查是否在频繁触发
    if hasattr(state, "_last_close_time"):
        # 依赖主程序的冷却锁，如果刚平过仓，网格也不许动
        pass

    # Agent 方向过滤（核心修复：趋势单优先，网格不能逆势）
    if agent_dir == "LONG":
        # 只允许 BUY 网格
        allowed_zones = [("buy_zone", grid_zones["buy_zone"])]
        _log(f"[Grid方向] Agent=LONG，仅允许 BUY 网格", "INFO")
    elif agent_dir == "SHORT":
        # 只允许 SHORT 网格，禁止 BUY
        allowed_zones = [("short_zone", grid_zones["short_zone"])]
        _log(f"[Grid方向] Agent=SHORT，仅允许 SHORT 网格", "INFO")
    else:
        # NEUTRAL: 双向都可以
        allowed_zones = [
            ("short_zone", grid_zones["short_zone"]),
            ("buy_zone", grid_zones["buy_zone"])
        ]

    orders = []
    for zone_name, zone in allowed_zones:
        enter_min = zone["enter_min"]
        enter_max = zone["enter_max"]
        side = "SELL" if "short" in zone_name else "BUY"

        # 检查价格是否在网格区间内
        price = bid if side == "SELL" else ask

        if enter_min <= price <= enter_max:
            if _has_recent_grid_activity(side, zone_name, int(GRID_CONFIG.get("cooldown_seconds", 180) or 180)):
                _log(f"Grid cooldown active: {zone_name} {side}", "INFO")
                continue
            order = {
                "signal_id": f"grid_{zone_name}_{int(time.time())}",
                "action": "OPEN",
                "side": side,
                "symbol": symbol,
                "volume": GRID_CONFIG["position_size"],
                "entry_price": price,
                "sl_price": zone["sl"],
                "tp_price": zone["tp1"],
                "tp2_price": zone["tp2"],
                "reason": f"grid_{zone_name}",
                "confidence": 0.70,
                "grid_zone": zone_name,
                "enter_zone": f"{enter_min}-{enter_max}",
                "timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ")
            }
            orders.append(order)
            _log(f"Grid signal: {zone_name} {side} @ {price} (zone {enter_min}-{enter_max})")

    return orders


def write_grid_order(order):
    from bridge.gold_trader import write_trade_request
    try:
        sig_id = write_trade_request(
            order["action"],
            order["side"],
            order["entry_price"],
            order["sl_price"],
            order["tp_price"],
            vol=order["volume"],
            sig_id=order["signal_id"],
        )
        if sig_id:
            _log(f"Grid order written: {sig_id} {order['side']} vol={order['volume']}")
            return True
        return False
    except Exception as e:
        _log(f"Grid order write failed: {e}", "ERROR")
        return False


def format_grid_summary():
    """格式化网格配置供查看"""
    grid_zones = _load_grid_zones_from_agent()
    lines = ["=== 网格配置 ==="]
    lines.append(f"启用: {GRID_CONFIG['enabled']}")
    for name, zone in grid_zones.items():
        lines.append(f"  {name}: 入{zone['enter_min']}-{zone['enter_max']} SL={zone['sl']} TP={zone['tp1']}/{zone['tp2']}")
    lines.append(f"  每笔: {GRID_CONFIG['position_size']}手 | 最多{GRID_CONFIG['max_grid_positions']}笔 | 冷却{GRID_CONFIG['cooldown_seconds']}s")
    return "\n".join(lines)
