#!/usr/bin/env python3
"""Patch binance/okx/gate app.py for entry_model support."""
from __future__ import annotations

import os
import re

REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

IMPORT_BLOCK = """from lib.trade.entry_model_lib import (
    build_intraday_entry_reason_options,
    build_trend_div_entry_reason_options,
    enrich_entry_model_display,
    migrate_entry_model_columns,
    order_entry_template_context,
    parse_manual_order_style_fields,
    resolve_trade_record_entry_reason,
    trend_manual_entry_reason_count,
)
"""

KEY_IMPORT = "from lib.key_monitor.key_auto_order_lib import (\n    check_monitor_type_add_allowed,\n    effective_entry_reason_options,\n    effective_stats_segment_defs,\n    load_key_auto_order_enabled,"

KEY_IMPORT_WITH_KEY_OPTS = "from lib.key_monitor.key_auto_order_lib import (\n    KEY_ENTRY_REASON_OPTIONS,\n    check_monitor_type_add_allowed,\n    effective_entry_reason_options,\n    effective_stats_segment_defs,\n    load_key_auto_order_enabled,"


def patch_file(path: str, exchange: str) -> bool:
    with open(path, "r", encoding="utf-8") as f:
        text = f.read()
    orig = text

    if "from lib.trade.entry_model_lib import" not in text:
        text = text.replace(
            "from lib.trade.trade_policy_app_lib import (",
            IMPORT_BLOCK + "from lib.trade.trade_policy_app_lib import (",
            1,
        )

    if exchange == "gate":
        old_er = '''# 与用户约定的固定开仓类型
ENTRY_REASON_OPTIONS = (
    "趋势多头:4h大结构突破前进场,确认条件:三次探顶,5m收敛不创新低",
    "趋势空头:4h大结构突破前进场,确认条件:三次探底,5m收敛不创新高",
    "趋势多头:小分歧低吸入场(左侧),确认条件:二次探底",
    "趋势空头:小分歧高吸入场(左侧),确认条件:二次探顶",
    "波段单:5m顺势突破,确认条件:2根k线+成交量放大+4h同向+日成交量前20",
    "关键位箱体突破",
    "关键位收敛突破",
    "关键位斐波0.618",
    "关键位斐波0.786",
    "关键位假突破",
    "关键位回调触价开仓",
    "关键位突破触价开仓",
) + STRATEGY_ENTRY_REASON_OPTIONS'''
        new_er = """# 日内户:长句开仓类型 + 关键位 + 策略(大分歧 A/B/小分歧 仅趋势户)
ENTRY_REASON_OPTIONS = build_intraday_entry_reason_options(
    KEY_ENTRY_REASON_OPTIONS,
    STRATEGY_ENTRY_REASON_OPTIONS,
)"""
        text = text.replace(old_er, new_er)
        if "KEY_ENTRY_REASON_OPTIONS," not in text.split("load_key_auto_order_enabled")[0]:
            text = text.replace(KEY_IMPORT, KEY_IMPORT_WITH_KEY_OPTS, 1)
    else:
        old_er = '''# 与用户约定的固定开仓类型(仅做这几类单子)
ENTRY_REASON_OPTIONS = (
    "趋势多头:4h大结构突破前进场,确认条件:三次探顶,5m收敛不创新低",
    "趋势空头:4h大结构突破前进场,确认条件:三次探底,5m收敛不创新高",
    "趋势多头:小分歧低吸入场(左侧),确认条件:二次探底",
    "趋势空头:小分歧高吸入场(左侧),确认条件:二次探顶",
    "波段单:5m顺势突破,确认条件:2根k线+成交量放大+4h同向+日成交量前20",
    "关键位箱体突破",
    "关键位收敛突破",
    "关键位斐波0.618",
    "关键位斐波0.786",
    "关键位假突破",
    "关键位回调触价开仓",
    "关键位突破触价开仓",
) + STRATEGY_ENTRY_REASON_OPTIONS'''
        new_er = """# 趋势户:大分歧A/B/小分歧 + 策略(关键位本实例关闭)
ENTRY_REASON_OPTIONS = build_trend_div_entry_reason_options(STRATEGY_ENTRY_REASON_OPTIONS)"""
        text = text.replace(old_er, new_er)

    if "migrate_entry_model_columns(conn)" not in text:
        text = text.replace(
            "    conn.commit()\n    conn.close()\n\n\ndef get_db",
            "    migrate_entry_model_columns(conn)\n    conn.commit()\n    conn.close()\n\n\ndef get_db",
            1,
        )

    text = re.sub(
        r"    er = \(\n        \(entry_reason or \"\"\)\.strip\(\)\n        or entry_reason_from_key_signal\(kst\)\n        or entry_reason_for_monitor_type\(monitor_type\)\n        or \"\"\n    \)",
        """    er = resolve_trade_record_entry_reason(
        entry_reason=entry_reason,
        entry_model=entry_model,
        key_signal_type=kst,
        monitor_type=monitor_type,
        entry_reason_from_key_signal=entry_reason_from_key_signal,
        entry_reason_for_monitor_type=entry_reason_for_monitor_type,
    )""",
        text,
        count=1,
    )

    if "entry_model=None," not in text:
        text = text.replace(
            "    entry_reason=None,\n    trend_plan_id=None,",
            "    entry_reason=None,\n    entry_model=None,\n    trend_plan_id=None,",
            1,
        )

    if "enrich_entry_model_display(item)" not in text:
        text = text.replace(
            "    enrich_order_display_fields(item, calc_rr_ratio)\n    try:",
            "    enrich_order_display_fields(item, calc_rr_ratio)\n    enrich_entry_model_display(item)\n    try:",
            1,
        )

    text = text.replace(
        """    trade_style = (d.get("trade_style") or DEFAULT_TRADE_STYLE or "trend").strip().lower()
    if trade_style not in ("trend", "swing"):
        trade_style = "trend"
    available_usdt = get_available_trading_usdt()""",
        """    trade_style, entry_model, style_err = parse_manual_order_style_fields(
        TRADE_POLICY, d, default_trade_style=DEFAULT_TRADE_STYLE or "trend"
    )
    if style_err:
        conn.close()
        flash(style_err)
        return redirect("/trade")
    available_usdt = get_available_trading_usdt()""",
        1,
    )

    old_insert = (
        '"INSERT INTO order_monitors (symbol, exchange_symbol, direction, trigger_price, stop_loss, initial_stop_loss, take_profit, margin_capital, leverage, trade_style, risk_percent, risk_amount, breakeven_rr_trigger, breakeven_offset_pct, breakeven_step_r, breakeven_armed, breakeven_price, breakeven_enabled, notional_value, position_ratio, base_amount, order_amount, exchange_order_id, opened_at, opened_at_ms, session_date, monitor_type, time_close_enabled, time_close_hours, time_close_at_ms) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",\n'
        "        (\n"
        "            symbol, exchange_symbol, direction, trigger_price, stop_loss, stop_loss, take_profit,\n"
        "            margin_capital, leverage, trade_style, risk_percent_db, risk_amount_final, breakeven_rr_trigger, breakeven_offset_pct, breakeven_step_r, 0, breakeven_price,\n"
        "            breakeven_enabled,\n"
        "            notional_value, position_ratio, base_amount, amount, open_order_id, opened_at_bj, opened_at_ms, trading_day,\n"
        "            ORDER_MONITOR_TYPE_MANUAL,\n"
        "            tc_en, tc_h, tc_at,\n"
        "        )"
    )
    new_insert = (
        '"INSERT INTO order_monitors (symbol, exchange_symbol, direction, trigger_price, stop_loss, initial_stop_loss, take_profit, margin_capital, leverage, trade_style, entry_model, risk_percent, risk_amount, breakeven_rr_trigger, breakeven_offset_pct, breakeven_step_r, breakeven_armed, breakeven_price, breakeven_enabled, notional_value, position_ratio, base_amount, order_amount, exchange_order_id, opened_at, opened_at_ms, session_date, monitor_type, time_close_enabled, time_close_hours, time_close_at_ms) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",\n'
        "        (\n"
        "            symbol, exchange_symbol, direction, trigger_price, stop_loss, stop_loss, take_profit,\n"
        "            margin_capital, leverage, trade_style, entry_model, risk_percent_db, risk_amount_final, breakeven_rr_trigger, breakeven_offset_pct, breakeven_step_r, 0, breakeven_price,\n"
        "            breakeven_enabled,\n"
        "            notional_value, position_ratio, base_amount, amount, open_order_id, opened_at_bj, opened_at_ms, trading_day,\n"
        "            ORDER_MONITOR_TYPE_MANUAL,\n"
        "            tc_en, tc_h, tc_at,\n"
        "        )"
    )
    text = text.replace(old_insert, new_insert)

    text = text.replace(
        """            effective_entry_reason_options(
                ENTRY_REASON_OPTIONS,
                POSITION_SIZING_MODE,
                KEY_AUTO_ORDER_ENABLED,
            )""",
        """            effective_entry_reason_options(
                ENTRY_REASON_OPTIONS,
                POSITION_SIZING_MODE,
                KEY_AUTO_ORDER_ENABLED,
                trend_manual_count=trend_manual_entry_reason_count(TRADE_POLICY),
            )""",
        1,
    )

    if "**order_entry_template_context(TRADE_POLICY)," not in text:
        text = text.replace(
            "        trade_policy=trade_policy_template_context(TRADE_POLICY),",
            "        trade_policy=trade_policy_template_context(TRADE_POLICY),\n        **order_entry_template_context(TRADE_POLICY),",
            1,
        )

    # insert_trade_record from order row: add entry_model
    text = re.sub(
        r"(insert_trade_record\(\n\s+conn,\n(?:[^\n]+\n)+?\s+trade_style=r\[\"trade_style\"\],\n)",
        r"\1            entry_model=(r[\"entry_model\"] if \"entry_model\" in r.keys() else None),\n",
        text,
    )

    if text != orig:
        with open(path, "w", encoding="utf-8", newline="\n") as f:
            f.write(text)
        return True
    return False


def main():
    for ex in ("binance", "okx", "gate"):
        path = os.path.join(REPO, f"crypto_monitor_{ex}", "app.py")
        changed = patch_file(path, ex)
        print(f"{ex}: {'patched' if changed else 'no change'}")


if __name__ == "__main__":
    main()
