#!/usr/bin/env python3
"""注入持仓价格数据 + 多周期技术面 + 综合分类 + 大盘指数到 LLM 上下文。
每个tick自动执行，输出直接注入prompt上下文。"""

import json
import sys
import os

from mo_data import read_portfolio, read_watchlist

# PORTFOLIO_PATH / WATCHLIST_PATH 均已废弃，数据在 mofin.db。
# 保留常量仅防止引用报错，新代码请用 mo_data.read_portfolio() / read_watchlist()。
PORTFOLIO_PATH = "/home/hmo/web-dashboard/data/portfolio.json"   # 废弃
WATCHLIST_PATH = "/home/hmo/web-dashboard/data/watchlist.json"   # 废弃
MTF_CACHE_PATH = "/home/hmo/web-dashboard/data/multi_tf_cache.json"


def calc_ma(prices, window):
    if len(prices) < window:
        return None
    return round(sum(prices[-window:]) / window, 2)


def fmt_holding(h):
    name = h.get("name", "?")
    code = h.get("code", "?")
    p = h.get("price", 0)
    chg = h.get("change_pct")
    chg_str = f"{chg:+.2f}%" if chg is not None else "N/A"
    pos = h.get("position_pct", 0)
    cost = h.get("cost")
    if cost and cost > 0:
        pnl = (p - cost) / cost * 100
        pnl_str = f"{pnl:+.1f}%"
    else:
        pnl_str = "N/A"
    analysis = h.get("analysis", {}) or {}
    sl = analysis.get("stop_loss", "N/A")
    tp = analysis.get("take_profit", "N/A")
    return f"  {name:<8}({code:<6}) 现价{p:<8} {chg_str:<8} 仓位{pos:<5.1f}% 盈亏{pnl_str:<8} 止损{sl} 止盈{tp}"


def fmt_mtf(code, mtf_cache):
    stock = mtf_cache.get(code, {})
    daily = stock.get("daily", [])
    if len(daily) < 5:
        return ""

    closes = [d["close"] for d in daily]
    ma5 = calc_ma(closes, 5)
    ma10 = calc_ma(closes, 10) if len(closes) >= 10 else None
    ma20 = calc_ma(closes, 20) if len(closes) >= 20 else None
    ma60 = calc_ma(closes, 60) if len(closes) >= 60 else None
    current = closes[-1]

    up_count = sum(1 for i in range(1, len(closes[-20:])) if closes[-20:][i] > closes[-20:][i-1])
    up_ratio = up_count / min(len(closes), 20) if len(closes) >= 2 else 0.5
    trend = "横盘"
    if up_ratio > 0.6 and ma20 and current > ma20:
        trend = "上升"
    elif up_ratio < 0.4 and ma20 and current < ma20:
        trend = "下降"

    ma_trend = ""
    if ma5 and ma10 and ma20:
        if ma5 > ma10 > ma20:
            ma_trend = "多头"
        elif ma5 < ma10 < ma20:
            ma_trend = "空头"

    above_ma20 = "↑" if ma20 and current > ma20 else ("↓" if ma20 and current < ma20 else "—")
    above_ma60 = "↑" if ma60 and current > ma60 else ("↓" if ma60 and current < ma60 else "—")

    parts = [f"趋势:{trend}"]
    if ma_trend:
        parts.append(ma_trend)
    if ma5:
        parts.append(f"MA5={ma5}")
    if ma20:
        parts.append(f"MA20={ma20}{above_ma20}")
    if ma60:
        parts.append(f"MA60={ma60}{above_ma60}")
    recent_high = max(d["high"] for d in daily[-20:]) if len(daily) >= 20 else max(d["high"] for d in daily)
    recent_low = min(d["low"] for d in daily[-20:]) if len(daily) >= 20 else min(d["low"] for d in daily)
    parts.append(f"近20日:{recent_low}~{recent_high}")
    return " | ".join(parts)


def fmt_index_line(code, name, mtf_cache):
    entry = mtf_cache.get(code)
    if not entry or not isinstance(entry, dict):
        return ""
    daily = entry.get("daily", [])
    if len(daily) < 5:
        return ""
    closes = [d["close"] for d in daily]
    current = closes[-1]
    ma20 = calc_ma(closes, 20) or 0
    ma60 = calc_ma(closes, 60) or 0
    ma20_sig = "↑" if current > ma20 else "↓"
    ma60_sig = "↑" if current > ma60 else "↓"
    up_count = sum(1 for i in range(1, min(21, len(closes))) if closes[-i] > closes[-i-1])
    trend = "上升" if up_count > 12 else ("下降" if up_count < 8 else "横盘")
    return f"{name}{current:.0f} {trend} MA20{ma20_sig} MA60{ma60_sig}"


def classify_from_cache(code, name, mtf_cache):
    stock = mtf_cache.get(code, {})
    daily = stock.get("daily", [])
    fund = stock.get("fundamentals", {})
    if not daily or len(daily) < 5:
        return ""
    closes = [d["close"] for d in daily]
    current = closes[-1]
    ma20 = calc_ma(closes, 20) or 0
    ma60 = calc_ma(closes, 60) or 0
    recent_high = max(d["high"] for d in daily[-20:])
    recent_low = min(d["low"] for d in daily[-20:])
    volatility = ((recent_high - recent_low) / recent_low * 100) if recent_low > 0 else 0
    pe = fund.get("pe") or 0
    eps = fund.get("eps") or 0
    mcap = fund.get("mcap_total") or 0

    is_high_vol = volatility > 30
    is_high_pe = pe > 100 or pe < 0
    is_value = 0 < pe < 20 and eps > 0.5

    category = "中短线"
    time_horizon = "2周~3月"
    suggestion = "中等仓位"

    if is_high_vol and is_high_pe:
        category = "短炒"
        time_horizon = "数日~2周"
        suggestion = "小仓快进快出"
    elif current < ma20 and current < ma60:
        category = "弱势"
        time_horizon = "观望"
        suggestion = "减仓或观望"
    elif is_value and current > ma20:
        category = "中长线"
        time_horizon = "数月~1年"
        suggestion = "正常配置"
    elif mcap > 1000 and eps > 0:
        category = "中长线"
        time_horizon = "数月~1年"
        suggestion = "正常配置"
    elif is_high_vol:
        category = "中短线"
        time_horizon = "2~6周"
        suggestion = "中等仓位"

    ma20_sig = "↑" if current > ma20 else "↓"
    ma60_sig = "↑" if current > ma60 else "↓"

    parts = [f"分类:{category}", f"周期:{time_horizon}", f"MA20{ma20_sig} MA60{ma60_sig}"]
    if pe and pe > 0:
        parts.append(f"PE={pe:.0f}")
    if eps and eps > 0:
        parts.append(f"EPS={eps:.2f}")
    parts.append(f"建议:{suggestion}")
    return " | ".join(parts)


def main():
    try:
        pf = read_portfolio()
    except Exception as e:
        print(f"[ERROR] {e}")
        sys.exit(0)

    mtf_cache = load_mtf_cache()
    holdings = pf.get("holdings", [])
    updated_at = pf.get("updated_at", "unknown")

    # ===== 大盘指数趋势 =====
    index_map = {
        "__index__sh000001": "上证", "__index__sz399001": "深证",
        "__index__sz399006": "创业板", "__index__sh000688": "科创50",
        "__index__hkHSI": "恒指", "__index__hkHSTECH": "恒科",
    }
    parts = []
    for ic, nm in index_map.items():
        line = fmt_index_line(ic, nm, mtf_cache)
        if line:
            parts.append(line)
    if parts:
        print("[大盘] " + " | ".join(parts))
    print()

    # ===== 市场总览 =====
    print(f"[持仓] 更新 {updated_at}")
    print(f"总资产:{pf.get('total_assets',0):.0f} | 市值:{pf.get('stock_value',0):.0f} | 现金:{pf.get('cash',0):.0f} | 仓位:{pf.get('position_pct',0):.1f}% | 日盈亏:{pf.get('day_pnl',0):+.0f}")
    print()

    a_h = [h for h in holdings if h.get("code","").startswith(("6","0","3")) and len(h.get("code",""))==6]
    hk_h = [h for h in holdings if h not in a_h]
    a_h.sort(key=lambda h: h.get("position_pct",0), reverse=True)
    hk_h.sort(key=lambda h: h.get("position_pct",0), reverse=True)

    print("=== A股持仓 ===")
    for h in a_h:
        print(fmt_holding(h))
    print()
    print("=== 港股持仓 ===")
    for h in hk_h:
        print(fmt_holding(h))

    # ===== 多周期技术面 =====
    print()
    print("=== 多周期技术面 ===")
    for h in a_h + hk_h:
        line = fmt_mtf(h["code"], mtf_cache)
        if line:
            print(f"  {h['name']:<8}({h['code']:<6}) {line}")

    # ===== 综合分类 =====
    print()
    print("=== 综合分类 ===")
    for h in a_h + hk_h:
        line = classify_from_cache(h["code"], h["name"], mtf_cache)
        if line:
            print(f"  {h['name']:<8}({h['code']:<6}) {line}")

    # ===== 自选股买入区监控 =====
    try:
        wl = read_watchlist()
        wl_stocks = wl.get("stocks", [])
        # 过滤掉已经在持仓里的
        held_codes = {h["code"] for h in holdings}
        watch_only = [s for s in wl_stocks if s.get("code") not in held_codes]
        # 只列出有买入区的
        with_zone = [s for s in watch_only
                     if s.get("analysis", {}).get("entry_low")]
        if with_zone:
            print()
            print("=== 自选买入区 ===")
            for s in with_zone:
                print(fmt_watchlist_item(s))
    except Exception:
        pass

    print()
    print("--- end data inject ---")


def fmt_watchlist_item(s):
    """格式化自选股条目"""
    name = s.get("name", "?")
    code = s.get("code", "?")
    price = s.get("price", 0)
    analysis = s.get("analysis", {}) or {}
    el = analysis.get("entry_low")
    eh = analysis.get("entry_high")
    sl = analysis.get("stop_loss")
    tp = analysis.get("take_profit")
    action = analysis.get("action", "")[:60]
    in_zone = ""
    if el and eh and price:
        if el <= price <= eh:
            in_zone = "✅在买入区"
        elif price < el:
            off = (el - price) / el * 100
            in_zone = f"⬇低于买入区{off:.0f}%"
        else:
            off = (price - eh) / eh * 100
            in_zone = f"⬆高于买入区+{off:.0f}%"
    return f"  {name:<8}({code:<6}) ¥{price:<8} | {in_zone} | 买{el}~{eh} | 损{sl} 盈{tp} | {action}"


def load_mtf_cache():
    try:
        return json.load(open(MTF_CACHE_PATH))
    except Exception:
        return {}


if __name__ == "__main__":
    main()
