# -*- coding: utf-8 -*-
"""
Telegram 命令处理
- handle_command(cmd, okx, manager) → str | None
"""

import time

from config import INSTRUMENTS, DEFAULT_LEVERAGE
from strategy.formatter import format_analysis


def handle_command(cmd, okx, manager, runner=None):
    """处理 Telegram 命令，返回回复文本，无匹配返回 None"""
    cmd = cmd.strip().lower()

    # ── /s 状态 ──────────────────────────────
    if cmd in ("/status", "/s", "状态"):
        avail, eq = okx.balance()
        pos    = okx.positions()
        active = [p for p in pos if float(p.get('pos', 0)) != 0]
        lines  = [f"💰 权益: ${eq:.4f} | 可用: ${avail:.4f}"]
        lines.append(f"📉 峰值: ${manager.peak_equity:.4f} | 回撤: {(1-eq/manager.peak_equity)*100:.2f}%")
        if active:
            for p in active:
                em = "🟢" if p.get('posSide') == 'long' else "🔴"
                lines.append(f"{em} {p['instId']} {p['posSide']} {p['pos']}张")
                lines.append(f"  avg:{p['avgPx']} last:{p.get('last','?')} upl:{p['upl']} {p['lever']}x")
        else:
            lines.append("📋 无持仓")
        lines.append(f"🔄 连亏: {manager.consecutive_losses} | 今日交易: {manager.trades_today}")
        return "\n".join(lines)

    # ── /c 全平 ──────────────────────────────
    elif cmd in ("/close", "/c", "平仓", "全平"):
        pos    = okx.positions()
        active = [p for p in pos if float(p.get('pos', 0)) != 0]
        if not active:
            return "📋 无持仓可平"
        results = []
        for p in active:
            inst     = p['instId']
            pos_side = p['posSide']
            r  = okx.close_position(inst, pos_side)
            ok = r.get('code') == '0'
            results.append(f"{'✅' if ok else '❌'} {inst} {pos_side} {p['pos']}张")
        return "平仓:\n" + "\n".join(results)

    # ── /sl 止损 ─────────────────────────────
    elif cmd.startswith(("/sl ", "止损 ")):
        parts = cmd.split()
        if len(parts) < 2:
            return "用法: /sl <价格>"
        new_sl = float(parts[1])
        pos    = okx.positions()
        active = [p for p in pos if float(p.get('pos', 0)) != 0]
        if not active:
            return "无持仓"
        p        = active[0]
        inst     = p['instId']
        pos_side = p['posSide']
        sz       = abs(float(p['pos']))
        for a in okx.get_algo_orders(inst):
            okx.cancel_algo(a['algoId'], inst)
            time.sleep(0.3)
        r  = okx.place_algo_tpsl(inst, pos_side, sz, sl=new_sl)
        ok = r.get('code') == '0'
        return f"{'✅' if ok else '❌'} 止损修改为 ${new_sl}" + (f" ({r.get('msg','')})" if not ok else "")

    # ── /tp 止盈 ─────────────────────────────
    elif cmd.startswith(("/tp ", "止盈 ")):
        parts = cmd.split()
        if len(parts) < 2:
            return "用法: /tp <价格>"
        new_tp = float(parts[1])
        pos    = okx.positions()
        active = [p for p in pos if float(p.get('pos', 0)) != 0]
        if not active:
            return "无持仓"
        p        = active[0]
        inst     = p['instId']
        pos_side = p['posSide']
        sz       = abs(float(p['pos']))
        for a in okx.get_algo_orders(inst):
            okx.cancel_algo(a['algoId'], inst)
            time.sleep(0.3)
        r  = okx.place_algo_tpsl(inst, pos_side, sz, tp=new_tp)
        ok = r.get('code') == '0'
        return f"{'✅' if ok else '❌'} 止盈修改为 ${new_tp}" + (f" ({r.get('msg','')})" if not ok else "")

    # ── /tpsl 同时设 ─────────────────────────
    elif cmd.startswith(("/tpsl ", "止盈止损 ")):
        parts = cmd.split()
        if len(parts) < 3:
            return "用法: /tpsl <止盈价> <止损价>"
        new_tp, new_sl = float(parts[1]), float(parts[2])
        pos    = okx.positions()
        active = [p for p in pos if float(p.get('pos', 0)) != 0]
        if not active:
            return "无持仓"
        p        = active[0]
        inst     = p['instId']
        pos_side = p['posSide']
        sz       = abs(float(p['pos']))
        for a in okx.get_algo_orders(inst):
            okx.cancel_algo(a['algoId'], inst)
            time.sleep(0.3)
        r  = okx.place_algo_tpsl(inst, pos_side, sz, tp=new_tp, sl=new_sl)
        ok = r.get('code') == '0'
        return f"{'✅' if ok else '❌'} TP=${new_tp} SL=${new_sl}" + (f" ({r.get('msg','')})" if not ok else "")

    # ── /a 分析 ──────────────────────────────
    elif cmd in ("/analyze", "/a", "分析"):
        result = manager.run_cycle()
        return format_analysis(result)

    # ── /add 加仓 ────────────────────────────
    elif cmd.startswith(("/add ", "加仓 ")):
        parts = cmd.split()
        if len(parts) < 2:
            return "用法: /add <张数>"
        add_sz = float(parts[1])
        pos    = okx.positions()
        active = [p for p in pos if float(p.get('pos', 0)) != 0]
        if not active:
            return "❌ 无持仓，无法加仓"
        p        = active[0]
        inst     = p['instId']
        pos_side = p['posSide']
        side     = "buy" if pos_side == "long" else "sell"
        r  = okx.place_order(inst, side, pos_side, add_sz)
        ok = r.get('code') == '0'
        return f"{'✅' if ok else '❌'} 加仓 {add_sz}张 {pos_side}" + (f" ({r.get('msg','')})" if not ok else "")

    # ── /reduce 减仓 ─────────────────────────
    elif cmd.startswith(("/reduce ", "减仓 ")):
        parts = cmd.split()
        if len(parts) < 2:
            return "用法: /reduce <张数>"
        reduce_sz = float(parts[1])
        pos    = okx.positions()
        active = [p for p in pos if float(p.get('pos', 0)) != 0]
        if not active:
            return "❌ 无持仓"
        p        = active[0]
        inst     = p['instId']
        pos_side = p['posSide']
        cur_sz   = abs(float(p['pos']))
        if reduce_sz >= cur_sz:
            return f"❌ 减仓量{reduce_sz}≥持仓{cur_sz}，请用 /c 全平"
        side = "sell" if pos_side == "long" else "buy"
        r  = okx.post("/api/v5/trade/order", {
            "instId": inst, "tdMode": "isolated",
            "side": side, "posSide": pos_side,
            "ordType": "market", "sz": str(reduce_sz),
            "reduceOnly": True
        })
        ok = r.get('code') == '0'
        return (f"{'✅' if ok else '❌'} 减仓 {reduce_sz}张 (剩余{cur_sz-reduce_sz}张)"
                + (f" ({r.get('msg','')})" if not ok else ""))

    # ── /lever 杠杆 ──────────────────────────
    elif cmd.startswith(("/lever ", "杠杆 ")):
        parts = cmd.split()
        if len(parts) < 2:
            return "用法: /lever <倍数>"
        new_lever = int(parts[1])
        if new_lever < 1 or new_lever > 100:
            return "❌ 杠杆范围 1-100"
        inst = INSTRUMENTS["ETH"]["inst"]
        r1   = okx.set_leverage(inst, new_lever, "long")
        r2   = okx.set_leverage(inst, new_lever, "short")
        ok   = r1.get('code') == '0' and r2.get('code') == '0'
        return f"{'✅' if ok else '❌'} 杠杆调整为 {new_lever}x" + ("" if ok else f" ({r1.get('msg','')})")

    # ── /long 手动做多 ───────────────────────
    elif cmd.startswith(("/long ", "做多 ")):
        parts = cmd.split()
        if len(parts) < 2:
            return "用法: /long <张数>"
        sz   = float(parts[1])
        inst = INSTRUMENTS["ETH"]["inst"]
        okx.set_leverage(inst, DEFAULT_LEVERAGE, "long")
        time.sleep(0.3)
        r  = okx.place_order(inst, "buy", "long", sz)
        ok = r.get('code') == '0'
        return f"{'✅' if ok else '❌'} 手动做多 {sz}张 {DEFAULT_LEVERAGE}x" + (f" ({r.get('msg','')})" if not ok else "")

    # ── /short 手动做空 ──────────────────────
    elif cmd.startswith(("/short ", "做空 ")):
        parts = cmd.split()
        if len(parts) < 2:
            return "用法: /short <张数>"
        sz   = float(parts[1])
        inst = INSTRUMENTS["ETH"]["inst"]
        okx.set_leverage(inst, DEFAULT_LEVERAGE, "short")
        time.sleep(0.3)
        r  = okx.place_order(inst, "sell", "short", sz)
        ok = r.get('code') == '0'
        return f"{'✅' if ok else '❌'} 手动做空 {sz}张 {DEFAULT_LEVERAGE}x" + (f" ({r.get('msg','')})" if not ok else "")

    # ── /news /mem 新闻（交易部不再处理，已移交情报部）──
    elif cmd in ("/news", "/n", "新闻", "/mem", "/memory", "记忆"):
        return "📰 新闻分析已移交情报部，请通过情报部查询"

    # ── /reset 恢复交易（回撤硬暂停后手动恢复）──
    elif cmd in ("/reset", "/r", "恢复"):
        if manager.trading_paused:
            manager.trading_paused = False
            manager.peak_equity = 0  # 重置峰值，以当前权益为起点
            manager.save_state()
            return "✅ 交易已恢复，峰值重置为当前权益"
        return "交易未被暂停，无需恢复"

    # ── /strat 策略管理 ───────────────────────
    elif cmd.startswith(("/strat", "/st")):
        if runner is None:
            return "📋 策略引擎未启动"
        parts = cmd.split()
        if len(parts) < 2:
            # 列表
            lines = ["📋 策略状态:"]
            for s in runner.list_strategies():
                em = "🟢" if s["enabled"] else "⚫"
                lines.append(f"  {em} {s['name']} | bars:{s['bars']} sig:{s['signals']} trades:{s['trades']}")
            return "\n".join(lines)
        sub = parts[1]
        if sub in ("on", "开启", "启用") and len(parts) >= 3:
            return runner.enable(parts[2])
        elif sub in ("off", "关闭", "禁用") and len(parts) >= 3:
            return runner.disable(parts[2])
        elif sub in ("sig", "信号"):
            sigs = runner.get_recent_signals(5)
            if not sigs:
                return "📋 暂无策略信号"
            lines = ["📋 最近信号:"]
            for s in sigs[-10:]:
                em = "🟢" if s["action"] == "LONG" else "🔴" if s["action"] == "SHORT" else "⚪"
                lines.append(f"  {em} [{s['strategy']}] {s['action']} {s['reason'][:40]}")
            return "\n".join(lines)
        elif sub in ("run", "运行"):
            runner.start_background(60)
            return "✅ 策略引擎已启动(后台60s)"
        elif sub in ("stop", "停止"):
            runner.stop_background()
            return "✅ 策略引擎已停止"
        return "用法: /st [on|off <name>|sig|run|stop]"

    # ── /factors 因子表现 ────────────────────
    elif cmd.startswith(("/factor", "/fac", "因子")):
        parts = cmd.split()
        try:
            import my_trader as mt
            fe = getattr(mt, 'factor_engine', None)
        except Exception:
            fe = None
        if fe is None:
            return "因子引擎未启动"

        if len(parts) < 2 or parts[1] in ("状态", "status", "s"):
            st = fe.get_status()
            return (f"⚙️ 因子引擎 [{st['symbols']}]\n"
                    f"周期: {st['cycle']} 快照: {st['snapshots']}\n"
                    f"因子: {st['factors_tracked']}对 更新: {st['last_update']}")

        # /fac perf <symbol> — 查看该品种所有因子表现
        elif parts[1] in ("perf", "performance", "p") and len(parts) >= 3:
            sym = parts[2].upper()
            perfs = fe.get_factor_performance(symbol=sym)
            if not perfs:
                return f"品种 {sym} 无因子数据"
            lines = [f"📊 {sym} 因子表现 (count/avg/accuracy):"]
            for p in perfs[:10]:
                lines.append(f"  {p['factor']:15s}: n={p['count']:4d} avg={p['avg']:+.3f} "
                           f"acc={p['accuracy']:.1%} contrib={p['contribution_pct']:.0f}%")
            return "\n".join(lines)

        # /fac snap <symbol> — 最近快照
        elif parts[1] in ("snap", "快照") and len(parts) >= 3:
            sym = parts[2].upper()
            snaps = fe.get_recent_snapshots(symbol=sym, n=3)
            if not snaps:
                return f"品种 {sym} 无快照"
            lines = [f"📸 {sym} 最近因子快照:"]
            for s in snaps:
                lines.append(f"  [{s['ts']}] {s['direction']:6s} ${s['price']:.1f} score={s['total_score']:+.3f}")
                top = sorted(s['factors'].items(), key=lambda x: -abs(x[1]))[:4]
                lines.append(f"    top: {' '.join(f'{k}={v:+.2f}' for k,v in top)}")
            return "\n".join(lines)
        return "用法: /fac [status|perf <symbol>|snap <symbol>]"

    # ── /flow 主动买卖量 ────────────────────
    elif cmd in ("/flow", "/taker", "主动买卖"):
        try:
            from exchange.taker_flow import fetch_and_analyze, format_taker_flow
            summary = fetch_and_analyze(okx, "ETH", period="5m", limit=12)
            return format_taker_flow(summary)
        except Exception as e:
            return f"主动买卖量分析失败: {e}"

    # ── /stops 止损区 ────────────────────────
    elif cmd in ("/stops", "/stopzone", "止损区"):
        try:
            inst = INSTRUMENTS.get("ETH", {}).get("inst", "ETH-USDT-SWAP")
            candles = okx.candles(inst, bar="5m", limit=100)
            if not candles or len(candles) < 20:
                return "K线数据不足"
            from strategy.stop_loss_zones import StopLossZoneDetector
            detector = StopLossZoneDetector(lookback=100)
            closes = [float(c[4]) for c in reversed(candles)]
            highs = [float(c[2]) for c in reversed(candles)]
            lows = [float(c[3]) for c in reversed(candles)]
            vols = [float(c[5]) for c in reversed(candles)]
            current = closes[-1]
            result = detector.analyze(closes, highs, lows, vols, current, "BTC")
            lines = [f"🛑 止损区分析 (${current:.1f})"]
            if result.nearest_above:
                z = result.nearest_above
                lines.append(f"⬆ 上方: ${z.price:.0f} 强度{z.strength:.0%} 距离+{z.distance_pct:.2f}% [{z.source}]")
            if result.nearest_below:
                z = result.nearest_below
                lines.append(f"⬇ 下方: ${z.price:.0f} 强度{z.strength:.0%} 距离{z.distance_pct:.2f}% [{z.source}]")
            risk_em = "🔴" if result.hunt_risk == "high" else "🟡" if result.hunt_risk == "medium" else "🟢"
            lines.append(f"{risk_em} 扫损风险: {result.hunt_risk} ({result.hunt_risk_score:.1%})")
            lines.append(f"💡 {result.recommendation[:80]}")
            # Top 3
            for z in sorted(result.all_zones, key=lambda z: -z.strength)[:3]:
                lines.append(f"  {z.side} @{z.price:.0f} s={z.strength:.1f} {z.detail[:40]}")
            return "\n".join(lines)
        except Exception as e:
            return f"止损区分析失败: {e}"

    # ── /sources 数据源 ───────────────────────
    elif cmd in ("/sources", "/src", "数据源"):
        from exchange.data_sources import source_report
        return source_report()

    # ── /paper 模拟账户 ───────────────────────
    elif cmd.startswith(("/paper", "/pp", "模拟")):
        parts = cmd.split()
        try:
            import my_trader as mt
            pt = getattr(mt, 'paper_trader', None) or getattr(manager, 'paper_trader', None)
        except Exception:
            pt = getattr(manager, 'paper_trader', None)

        if pt is None:
            return "🧪 模拟交易未启用（设置 PAPER_TRADING=true）"

        if len(parts) < 2 or parts[1] in ("状态", "s", "status"):
            s = pt.get_summary()
            trades = pt.get_trade_history(5)
            lines = [
                f"🧪 模拟账户",
                f"权益: ${s['equity']:.2f} | 可用: ${s['available']:.2f}",
                f"初始: ${s['initial']} | 已实现PnL: ${s['realized_pnl']:.4f}",
                f"交易: {s['total_trades']}次 | 胜率: {s['win_rate']:.1%}",
                f"连亏: {s['consecutive_losses']} | 最大回撤: {s['max_drawdown']}",
                f"挂单: {s['pending_orders']} | 持仓: {s['active_positions']}",
            ]
            if trades:
                lines.append("最近成交:")
                for t in trades:
                    em = "🟢" if t['pnl'] > 0 else "🔴" if t['pnl'] < 0 else "⚪"
                    lines.append(f"  {em} {t['action']} {t['symbol']} {t['size']}张 @{t['price']:.1f} {t['reason']}")
            return "\n".join(lines)

        elif parts[1] in ("reset", "重置") and len(parts) >= 3:
            eq = float(parts[2]) if len(parts) >= 3 else 10000
            pt.reset(eq)
            return f"🧪 模拟账户已重置 权益=${eq}"

        return "用法: /paper [status|reset <金额>]"

    # ── /help ────────────────────────────────
    elif cmd in ("/help", "/h", "帮助"):
        return ("📖 命令:\n"
                "/s — 账户和持仓\n"
                "/a — 详细市场分析\n"
                "/c — 平掉所有仓位\n"
                "/sl <价格> — 修改止损\n"
                "/tp <价格> — 修改止盈\n"
                "/tpsl <TP> <SL> — 同时设止盈止损\n"
                "/add <张数> — 加仓(同方向)\n"
                "/reduce <张数> — 减仓\n"
                "/lever <倍数> — 调整杠杆\n"
                "/long <张数> — 手动做多\n"
                "/short <张数> — 手动做空\n"
                "/news — 新闻分析+记忆\n"
                "/mem — 查看新闻记忆\n"
                "/reset — 恢复交易（回撤暂停后）\n"
                "/st — 策略状态/开关/信号\n"
                "/help — 帮助")

    return None
