"""
市场行情概览 - 用于全局配置页展示
拉取 Binance 公开接口（无需 API Key），与策略过滤逻辑对应的数据。
"""
import json
import logging
import ssl
import urllib.request
from typing import Any, Dict, Optional

logger = logging.getLogger(__name__)

BINANCE_FUTURES_BASE = "https://fapi.binance.com"
BINANCE_FUTURES_DATA = "https://fapi.binance.com/futures/data"
REQUEST_TIMEOUT = 10


def _http_get(url: str, params: Optional[dict] = None) -> Optional[Any]:
    """发起 GET 请求，返回 JSON 或 None。"""
    if params:
        qs = "&".join(f"{k}={v}" for k, v in params.items())
        url = f"{url}?{qs}"
    try:
        req = urllib.request.Request(url, headers={"Accept": "application/json"})
        ctx = ssl.create_default_context()
        with urllib.request.urlopen(req, timeout=REQUEST_TIMEOUT, context=ctx) as resp:
            return json.loads(resp.read().decode("utf-8"))
    except Exception as e:
        logger.debug("market_overview HTTP GET 失败 %s: %s", url[:80], e)
        return None


def _fetch_klines(symbol: str, interval: str, limit: int) -> Optional[list]:
    """获取 K 线数据。"""
    data = _http_get(
        f"{BINANCE_FUTURES_BASE}/fapi/v1/klines",
        {"symbol": symbol, "interval": interval, "limit": limit},
    )
    return data if isinstance(data, list) else None


def _compute_change_from_klines(klines: list, periods: int) -> Optional[float]:
    """根据 K 线计算最近 N 根的总涨跌幅（比例，如 -0.0167 表示 -1.67%）。"""
    if not klines or len(klines) < periods + 1:
        return None
    first_close = float(klines[0][4])
    last_close = float(klines[-1][4])
    return (last_close - first_close) / first_close if first_close else None


def fetch_symbol_change_period(symbol: str, interval: str, periods: int) -> Optional[float]:
    """获取指定交易对在指定周期内的涨跌幅（比例）。"""
    klines = _fetch_klines(symbol, interval, periods + 1)
    return _compute_change_from_klines(klines, periods) if klines else None


def fetch_ticker_24h(symbol: str) -> Optional[Dict]:
    """获取 24h ticker。"""
    data = _http_get(f"{BINANCE_FUTURES_BASE}/fapi/v1/ticker/24hr", {"symbol": symbol})
    return data if isinstance(data, dict) else None


def fetch_premium_index(symbol: str) -> Optional[Dict]:
    """获取资金费率等。"""
    data = _http_get(f"{BINANCE_FUTURES_BASE}/fapi/v1/premiumIndex", {"symbol": symbol})
    return data if isinstance(data, dict) else None


def fetch_long_short_ratio(symbol: str = "BTCUSDT", period: str = "1d", limit: int = 1) -> Optional[float]:
    """获取大户多空比。"""
    data = _http_get(
        f"{BINANCE_FUTURES_DATA}/topLongShortPositionRatio",
        {"symbol": symbol, "period": period, "limit": limit},
    )
    if not isinstance(data, list) or len(data) == 0:
        return None
    try:
        return float(data[-1].get("longShortRatio", 1))
    except (TypeError, ValueError):
        return None


def get_market_overview() -> Dict[str, Any]:
    """
    获取市场行情概览，与策略过滤逻辑对应的数据。
    供全局配置页展示，帮助用户确认当前策略方案是否匹配市场。
    """
    result = {
        "btc_24h_change_pct": None,
        "eth_24h_change_pct": None,
        "btc_15m_change_pct": None,
        "btc_1h_change_pct": None,
        "eth_15m_change_pct": None,
        "eth_1h_change_pct": None,
        "btc_funding_rate": None,
        "eth_funding_rate": None,
        "btc_long_short_ratio": None,
        "btc_trend_4h": None,
        "market_regime": None,
        "beta_filter_triggered": None,
    }

    # 24h 涨跌幅
    btc_ticker = fetch_ticker_24h("BTCUSDT")
    if btc_ticker is not None:
        try:
            result["btc_24h_change_pct"] = round(float(btc_ticker.get("priceChangePercent", 0)), 2)
        except (TypeError, ValueError):
            pass
    eth_ticker = fetch_ticker_24h("ETHUSDT")
    if eth_ticker is not None:
        try:
            result["eth_24h_change_pct"] = round(float(eth_ticker.get("priceChangePercent", 0)), 2)
        except (TypeError, ValueError):
            pass

    # 15m / 1h 涨跌幅（大盘共振过滤用）
    btc_15m = fetch_symbol_change_period("BTCUSDT", "15m", 5)
    btc_1h = fetch_symbol_change_period("BTCUSDT", "1h", 3)
    eth_15m = fetch_symbol_change_period("ETHUSDT", "15m", 5)
    eth_1h = fetch_symbol_change_period("ETHUSDT", "1h", 3)
    if btc_15m is not None:
        result["btc_15m_change_pct"] = round(btc_15m * 100, 2)
    if btc_1h is not None:
        result["btc_1h_change_pct"] = round(btc_1h * 100, 2)
    if eth_15m is not None:
        result["eth_15m_change_pct"] = round(eth_15m * 100, 2)
    if eth_1h is not None:
        result["eth_1h_change_pct"] = round(eth_1h * 100, 2)

    # 资金费率
    btc_prem = fetch_premium_index("BTCUSDT")
    if btc_prem is not None:
        try:
            result["btc_funding_rate"] = round(float(btc_prem.get("lastFundingRate", 0)), 6)
        except (TypeError, ValueError):
            pass
    eth_prem = fetch_premium_index("ETHUSDT")
    if eth_prem is not None:
        try:
            result["eth_funding_rate"] = round(float(eth_prem.get("lastFundingRate", 0)), 6)
        except (TypeError, ValueError):
            pass

    # 大户多空比
    lsr = fetch_long_short_ratio("BTCUSDT", "1d", 1)
    if lsr is not None:
        result["btc_long_short_ratio"] = round(lsr, 4)

    # 4H 趋势
    klines_4h = _fetch_klines("BTCUSDT", "4h", 60)
    if klines_4h and len(klines_4h) >= 21:
        try:
            from trading_system.market_regime_detector import compute_trend_4h_from_klines
            result["btc_trend_4h"] = compute_trend_4h_from_klines(klines_4h)
        except Exception:
            result["btc_trend_4h"] = _simple_trend_4h(klines_4h)

    # 市场状态（bull/bear/normal）
    try:
        from trading_system.market_regime_detector import detect_market_regime
        regime, details = detect_market_regime()
        result["market_regime"] = regime
        result["market_regime_details"] = details
    except Exception as e:
        logger.debug("market_overview 获取市场状态失败: %s", e)

    return result


def _simple_trend_4h(klines: list) -> str:
    """简化 4H 趋势：价格 vs 最近一根 K 线前 20 根均价。"""
    if len(klines) < 21:
        return "neutral"
    closes = [float(k[4]) for k in klines]
    price = closes[-1]
    avg20 = sum(closes[-21:-1]) / 20
    if price > avg20 * 1.002:
        return "up"
    if price < avg20 * 0.998:
        return "down"
    return "neutral"


def _g(key: str, default: Any, cfg: dict) -> Any:
    """从配置字典取键，支持 bool/数字/字符串。"""
    v = cfg.get(key, default)
    if v is None:
        return default
    if isinstance(default, bool):
        return str(v).lower() in ("true", "1", "yes")
    return v


def get_strategy_execution_overview() -> Dict[str, Any]:
    """
    生成「策略执行概览」：当前执行方案、配置项执行情况，用易读文字描述整体策略执行标准与机制。
    供全局配置页「策略执行概览」展示。
    返回格式：{ "sections": [ { "title": "小节标题", "content": "正文" } ] }
    """
    sections = []
    cfg = {}
    try:
        from config_manager import GlobalStrategyConfigManager
        mgr = GlobalStrategyConfigManager()
        mgr.reload_from_redis()
        for key in (
            "AUTO_TRADE_ENABLED", "AUTO_TRADE_ONLY_TRENDING", "AUTO_TRADE_ALLOW_4H_NEUTRAL",
            "MIN_SIGNAL_STRENGTH", "LOW_VOLATILITY_MIN_SIGNAL_STRENGTH", "MARKET_REGIME_AUTO",
            "TOP_N_SYMBOLS", "SCAN_INTERVAL", "PRIMARY_INTERVAL", "CONFIRM_INTERVAL",
            "MAX_OPEN_POSITIONS", "MAX_DAILY_ENTRIES", "FIXED_RISK_PERCENT", "USE_FIXED_RISK_SIZING",
            "BETA_FILTER_ENABLED", "BETA_FILTER_THRESHOLD", "MARKET_SCHEME",
            "USE_ATR_STOP_LOSS", "ATR_STOP_LOSS_MULTIPLIER", "STOP_LOSS_PERCENT",
            "TAKE_PROFIT_1_PERCENT", "TAKE_PROFIT_PERCENT", "USE_TRAILING_STOP",
            "TRAILING_STOP_ACTIVATION", "TRAILING_STOP_PROTECT", "PROFIT_PROTECTION_ENABLED",
            "SMART_ENTRY_ENABLED", "USE_TREND_ENTRY_FILTER", "MAX_TREND_MOVE_BEFORE_ENTRY",
            "MAX_RSI_FOR_LONG", "MIN_RSI_FOR_SHORT", "MAX_CHANGE_PERCENT_FOR_LONG", "MAX_CHANGE_PERCENT_FOR_SHORT",
            "MIN_VOLUME_24H", "MIN_VOLATILITY", "MIN_HOLD_TIME_SEC",
        ):
            cfg[key] = mgr.get(key)
    except Exception as e:
        logger.debug("get_strategy_execution_overview 加载配置失败: %s", e)

    def pct(x):
        if x is None:
            return "—"
        try:
            f = float(x)
            if abs(f) < 1 and abs(f) > 0:
                return f"{f * 100:.2f}%"
            return f"{f}%"
        except (TypeError, ValueError):
            return str(x)

    # ---------- 1. 总开关与自动交易条件 ----------
    auto_on = _g("AUTO_TRADE_ENABLED", True, cfg)
    only_trending = _g("AUTO_TRADE_ONLY_TRENDING", True, cfg)
    allow_4h_neutral = _g("AUTO_TRADE_ALLOW_4H_NEUTRAL", False, cfg)
    min_strength = _g("MIN_SIGNAL_STRENGTH", 8, cfg)
    low_vol_strength = _g("LOW_VOLATILITY_MIN_SIGNAL_STRENGTH", 9, cfg)
    regime_auto = _g("MARKET_REGIME_AUTO", True, cfg)

    c1 = []
    c1.append("自动交易总开关：" + ("开启" if auto_on else "关闭"))
    if not auto_on:
        c1.append("关闭时仅生成推荐，不会自动下单。")
    else:
        c1.append("自动下单条件（需同时满足）：")
        c1.append("• 信号强度 ≥ " + str(min_strength) + "（技术指标综合评分）；低波动期自动提高至 " + str(low_vol_strength) + "（" + ("已开启" if regime_auto else "未开启") + "市场节奏识别）。")
        c1.append("• 市场状态：仅当「仅做趋势市」开启时，要求市场状态为 trending 才下单；ranging/unknown 只生成推荐、不自动下单。当前「仅做趋势市」=" + ("是" if only_trending else "否") + "。")
        c1.append("• 4H 趋势：允许 4H 中性时自动交易 = " + ("是" if allow_4h_neutral else "否") + "；为否时 4H 为中性会跳过自动下单。")
    sections.append({
        "title": "一、总开关与自动交易条件",
        "content": "\n".join(c1),
    })

    # ---------- 2. 扫描与候选池 ----------
    top_n = _g("TOP_N_SYMBOLS", 30, cfg)
    scan_interval = _g("SCAN_INTERVAL", 900, cfg)
    primary = _g("PRIMARY_INTERVAL", "4h", cfg)
    confirm = _g("CONFIRM_INTERVAL", "1d", cfg)
    min_vol = _g("MIN_VOLUME_24H", 30000000, cfg)
    min_vol_str = f"{min_vol / 1e6:.0f} 万 USDT" if isinstance(min_vol, (int, float)) and min_vol >= 1e6 else str(min_vol)
    vol_pct = _g("MIN_VOLATILITY", 0.03, cfg)
    vol_pct_str = f"{float(vol_pct) * 100:.1f}%" if isinstance(vol_pct, (int, float)) else str(vol_pct)

    c2 = []
    c2.append("每次扫描取涨跌幅最大的前 " + str(top_n) + " 个交易对进行详细分析；扫描间隔 " + str(scan_interval) + " 秒。")
    c2.append("主周期 " + str(primary) + "，确认周期 " + str(confirm) + "；24h 成交额 ≥ " + min_vol_str + "，最小波动率 " + vol_pct_str + "。")
    sections.append({
        "title": "二、扫描与候选池",
        "content": "\n".join(c2),
    })

    # ---------- 3. 仓位与风控 ----------
    max_pos = _g("MAX_OPEN_POSITIONS", 4, cfg)
    max_daily = _g("MAX_DAILY_ENTRIES", 15, cfg)
    fixed_risk = _g("USE_FIXED_RISK_SIZING", True, cfg)
    risk_pct = _g("FIXED_RISK_PERCENT", 0.01, cfg)
    risk_pct_str = pct(risk_pct) if isinstance(risk_pct, (int, float)) and risk_pct <= 1 else f"{float(risk_pct)}%"

    c3 = []
    c3.append("同时持仓上限 " + str(max_pos) + " 个，每日最多开仓 " + str(max_daily) + " 笔。")
    c3.append("固定风险 sizing：" + ("开启" if fixed_risk else "关闭") + "；每笔最大亏损 " + risk_pct_str + " 账户资金。")
    sections.append({
        "title": "三、仓位与风控",
        "content": "\n".join(c3),
    })

    # ---------- 4. 大盘与市场方案 ----------
    beta_on = _g("BETA_FILTER_ENABLED", True, cfg)
    beta_th = _g("BETA_FILTER_THRESHOLD", -0.005, cfg)
    scheme = str(_g("MARKET_SCHEME", "normal", cfg) or "normal")

    c4 = []
    c4.append("大盘共振过滤：" + ("开启" if beta_on else "关闭") + "；BTC/ETH 短周期跌逾 " + pct(beta_th) + " 时屏蔽多单。")
    c4.append("当前市场方案：" + scheme + "（用于参数预设）。")
    sections.append({
        "title": "四、大盘与市场方案",
        "content": "\n".join(c4),
    })

    # ---------- 5. 止损止盈与保护 ----------
    use_atr = _g("USE_ATR_STOP_LOSS", True, cfg)
    atr_mult = _g("ATR_STOP_LOSS_MULTIPLIER", 2.0, cfg)
    sl_pct = _g("STOP_LOSS_PERCENT", 0.05, cfg)
    tp1 = _g("TAKE_PROFIT_1_PERCENT", 0.12, cfg)
    tp2 = _g("TAKE_PROFIT_PERCENT", 0.25, cfg)
    trail = _g("USE_TRAILING_STOP", True, cfg)
    trail_act = _g("TRAILING_STOP_ACTIVATION", 0.10, cfg)
    trail_prot = _g("TRAILING_STOP_PROTECT", 0.02, cfg)
    profit_prot = _g("PROFIT_PROTECTION_ENABLED", True, cfg)

    c5 = []
    c5.append("止损：ATR 动态止损 " + ("开启" if use_atr else "关闭") + ("，倍数 " + str(atr_mult) if use_atr else "") + "；固定止损 " + pct(sl_pct) + "。")
    c5.append("止盈：第一目标 " + pct(tp1) + "，第二目标 " + pct(tp2) + "。")
    c5.append("盈利保护总开关：" + ("开启" if profit_prot else "关闭") + "；移动止损 " + ("开启" if trail else "关闭") + ("，盈利 " + pct(trail_act) + " 激活、保护 " + pct(trail_prot) + " 利润" if trail else "") + "。")
    sections.append({
        "title": "五、止损止盈与保护",
        "content": "\n".join(c5),
    })

    # ---------- 6. 入场与过滤 ----------
    smart = _g("SMART_ENTRY_ENABLED", True, cfg)
    trend_filter = _g("USE_TREND_ENTRY_FILTER", True, cfg)
    max_trend = _g("MAX_TREND_MOVE_BEFORE_ENTRY", 0.04, cfg)
    max_rsi_long = _g("MAX_RSI_FOR_LONG", 65, cfg)
    min_rsi_short = _g("MIN_RSI_FOR_SHORT", 30, cfg)
    max_ch_long = _g("MAX_CHANGE_PERCENT_FOR_LONG", 25, cfg)
    max_ch_short = _g("MAX_CHANGE_PERCENT_FOR_SHORT", 10, cfg)

    c6 = []
    c6.append("智能入场（限价+追价+市价兜底）：" + ("开启" if smart else "关闭") + "。")
    c6.append("趋势入场过滤：" + ("开启" if trend_filter else "关闭") + "；信号方向已走超 " + pct(max_trend) + " 则不再入场。")
    c6.append("做多：RSI ≤ " + str(max_rsi_long) + "，24h 涨跌幅 ≤ " + str(max_ch_long) + "%。做空：RSI ≥ " + str(min_rsi_short) + "，24h 涨跌幅 ≤ " + str(max_ch_short) + "%。")
    sections.append({
        "title": "六、入场与过滤",
        "content": "\n".join(c6),
    })

    return {"sections": sections}
