#!/usr/bin/env python3
"""
fix_portfolio_prices.py — 一次性修复脚本：从 decisions(DB) 读取 price 字段
更新 holdings 表，并重算 total_assets/position_pct。

背景：strategy_lifecycle.regenerate_all() 在旧版本中会
从 DB query_holdings()（不含 price/change_pct）覆盖写入
portfolio.json，导致 price_monitor 维护的实时价丢失。
该 bug 已在 strategy_lifecycle.py:1790 修复（保留 price 字段），
此脚本用于修复已损坏的 portfolio 数据。

用法：
  python3 fix_portfolio_prices.py          # 修复 DB
  python3 fix_portfolio_prices.py --check  # 只检查，不修改
"""

import sys

from mo_data import read_decisions, read_portfolio
from mofin_db import get_conn, write_holdings_batch, write_portfolio_summary


def build_price_map():
    """从 decisions(DB) 读取 {code: {price, ...}} 映射"""
    try:
        dec = read_decisions()
    except Exception as e:
        print(f"❌ 无法读取 decisions(DB): {e}", file=sys.stderr)
        return {}

    price_map = {}
    for d in dec.get("decisions", []):
        code = d.get("code", "")
        price = d.get("price", 0) or d.get("current_price", 0)
        if code and price:
            price_map[code] = float(price)
    print(f"  decisions(DB): {len(price_map)} 个股票的 price 已加载")
    return price_map


def fix_portfolio(check_only: bool) -> bool:
    """从 DB 读持仓，修复 price，写回 DB"""
    try:
        pf = read_portfolio()
    except Exception as e:
        print(f"❌ 无法读取 portfolio(DB): {e}", file=sys.stderr)
        return False

    holdings = pf.get("holdings", [])
    price_map = build_price_map()
    if not price_map:
        print("❌ decisions 无有效 price 数据，退出")
        return False

    changes = 0
    errors = 0

    for h in holdings:
        code = h.get("code", "")
        if not code:
            continue

        old_price = h.get("price", 0)
        decision_price = price_map.get(code)

        if decision_price and (not old_price or old_price == 0):
            h["price"] = decision_price
            changes += 1
            print(f"  ✅ {code} {h.get('name','')}: price {old_price} \u2192 {decision_price}")
        elif decision_price and old_price and abs(decision_price - old_price) / max(abs(old_price), 1) > 0.05:
            print(f"  ⚠️ {code} {h.get('name','')}: 当前价 {old_price} vs decisions {decision_price} (偏离>{5:.0f}%)，保留当前价")
        elif not decision_price:
            errors += 1
            if old_price == 0 or not old_price:
                print(f"  ❌ {code} {h.get('name','')}: decisions 无此股 price 数据，当前 price={old_price}")

    # 重算 total_assets / position_pct
    total_mv = 0
    for h in holdings:
        price = h.get("price", 0) or 0
        shares = h.get("shares", 0) or 0
        total_mv += price * shares

    cash = pf.get("cash", 0) or 0
    frozen = pf.get("frozen_cash", 0) or 0
    new_total_assets = round(total_mv + cash + frozen, 2)
    new_position_pct = round(total_mv / new_total_assets * 100, 2) if new_total_assets > 0 else 0

    old_total_assets = pf.get("total_assets", 0)
    old_position_pct = pf.get("position_pct", 0)

    if abs(new_total_assets - old_total_assets) > 100:
        print(f"  📊 total_assets: {old_total_assets} \u2192 {new_total_assets} (变动 {new_total_assets-old_total_assets:.0f})")
        changes += 1

    if abs(new_position_pct - old_position_pct) > 0.5:
        print(f"  📊 position_pct: {old_position_pct}% \u2192 {new_position_pct}%")
        changes += 1

    if changes == 0:
        print(f"  ✅ 无需修复（{len(holdings)} 个持仓价格正常）")
        return True

    if check_only:
        print(f"  ⏸️ 检查模式: {changes} 处需修复，未写入")
        return True

    # 写入 DB
    try:
        conn = get_conn()
        ok, msg = write_holdings_batch(conn, holdings)
        if not ok:
            print(f"  ❌ 写入 holdings 失败: {msg}", file=sys.stderr)
            conn.close()
            return False

        summary = {
            "total_assets": new_total_assets,
            "total_mv": round(total_mv, 2),
            "stock_value": round(total_mv, 2),
            "cash": cash,
            "frozen_cash": frozen,
            "position_pct": new_position_pct,
            "currency": pf.get("currency", "CNY"),
        }
        ok, msg = write_portfolio_summary(conn, summary)
        conn.close()
        if not ok:
            print(f"  ❌ 写入 portfolio_summary 失败: {msg}", file=sys.stderr)
            return False

        print(f"  ✅ DB: {changes} 处已修复，已写入")
        return True
    except Exception as e:
        print(f"  ❌ 写入失败: {e}", file=sys.stderr)
        return False


def main():
    check_only = "--check" in sys.argv

    print("=== fix_portfolio_prices.py ===")
    if check_only:
        print("模式：检查（不写入）")
    else:
        print("模式：修复")

    ok = fix_portfolio(check_only)

    if ok:
        print("\n✅ 全部完成")
        return 0
    else:
        print("\n⚠️ 部分失败")
        return 1


if __name__ == "__main__":
    sys.exit(main())
