import unittest

from lib.trade.entry_model_lib import (
    ENTRY_MODEL_BIG_DIV_A,
    ENTRY_MODEL_BIG_DIV_B,
    ENTRY_MODEL_LAUNCH_A,
    ENTRY_MODEL_LAUNCH_B,
    ENTRY_MODEL_SMALL_DIV,
    ENTRY_CATEGORY_REVERSAL,
    ENTRY_CATEGORY_TREND,
    build_intraday_entry_reason_options,
    build_trend_div_entry_reason_options,
    entry_model_categories,
    entry_model_category,
    entry_model_display_label,
    entry_model_label,
    format_entry_type_display,
    hub_meta_entry_context,
    intraday_entry_model_options,
    is_intraday_trading_profile,
    open_position_button_label,
    parse_manual_order_style_fields,
    resolve_trade_record_entry_reason,
    trade_style_for_entry_model,
    trend_manual_entry_reason_count,
)
from lib.trade.trade_policy_lib import TradePolicy, load_trade_policy


class TestEntryModelLib(unittest.TestCase):
    def test_intraday_profile_btc_eth_whitelist(self):
        policy = load_trade_policy(
            {
                "TRADE_SYMBOL_RESTRICT_ENABLED": "true",
                "TRADE_SYMBOL_WHITELIST": "BTC,ETH",
            }
        )
        self.assertTrue(is_intraday_trading_profile(policy))
        self.assertEqual(trend_manual_entry_reason_count(policy), 2)

    def test_trend_div_profile_alt(self):
        policy = load_trade_policy(
            {
                "TRADE_SYMBOL_RESTRICT_ENABLED": "false",
                "TRADE_SYMBOL_WHITELIST": "BTC,ETH",
            }
        )
        self.assertFalse(is_intraday_trading_profile(policy))
        self.assertEqual(trend_manual_entry_reason_count(policy), 5)

    def test_entry_model_maps_trade_style(self):
        self.assertEqual(trade_style_for_entry_model(ENTRY_MODEL_LAUNCH_A), "trend")
        self.assertEqual(trade_style_for_entry_model(ENTRY_MODEL_BIG_DIV_A), "trend")
        self.assertEqual(trade_style_for_entry_model(ENTRY_MODEL_SMALL_DIV), "swing")
        self.assertEqual(entry_model_label(ENTRY_MODEL_LAUNCH_B), "启动B")
        self.assertEqual(entry_model_category(ENTRY_MODEL_LAUNCH_A), ENTRY_CATEGORY_REVERSAL)
        self.assertEqual(entry_model_category(ENTRY_MODEL_BIG_DIV_B), ENTRY_CATEGORY_TREND)

    def test_entry_model_categories_two_level(self):
        cats = entry_model_categories()
        keys = [c["key"] for c in cats]
        self.assertEqual(keys, ["reversal", "trend", "swing"])
        reversal = cats[0]["options"]
        self.assertEqual([o["code"] for o in reversal], ["launch_a", "launch_b"])
        self.assertEqual(len(cats[2]["options"]), 1)

    def test_parse_trend_div_requires_entry_model(self):
        policy = TradePolicy(False, "both", False, ())
        style, code, err = parse_manual_order_style_fields(policy, {})
        self.assertTrue(err)
        self.assertEqual(code, None)

        style, code, err = parse_manual_order_style_fields(
            policy, {"entry_model": ENTRY_MODEL_SMALL_DIV}
        )
        self.assertIsNone(err)
        self.assertEqual(code, ENTRY_MODEL_SMALL_DIV)
        self.assertEqual(style, "swing")

        style, code, err = parse_manual_order_style_fields(
            policy, {"entry_model": ENTRY_MODEL_LAUNCH_A}
        )
        self.assertIsNone(err)
        self.assertEqual(code, ENTRY_MODEL_LAUNCH_A)
        self.assertEqual(style, "trend")

    def test_hub_meta_intraday(self):
        policy = load_trade_policy(
            {
                "TRADE_SYMBOL_RESTRICT_ENABLED": "true",
                "TRADE_SYMBOL_WHITELIST": "BTC,ETH",
            }
        )
        ctx = hub_meta_entry_context(policy)
        self.assertTrue(ctx["intraday_discipline"])
        self.assertEqual(ctx["order_entry_profile"], "intraday")
        policy = TradePolicy(False, "both", True, ("BTC", "ETH"))
        style, code, err = parse_manual_order_style_fields(policy, {"trade_style": "swing"})
        self.assertIsNone(err)
        self.assertIsNone(code)
        self.assertEqual(style, "swing")

    def test_intraday_entry_model_options(self):
        opts = intraday_entry_model_options()
        codes = [o.code for o in opts]
        self.assertEqual(codes, ["liquidity_false_break", "structure_breakout"])
        self.assertEqual(entry_model_label("liquidity_false_break"), "假破")

    def test_parse_intraday_requires_entry_model(self):
        policy = TradePolicy(True, "both", True, ("BTC", "ETH"))
        style, code, err = parse_manual_order_style_fields(policy, {})
        self.assertTrue(err)
        style, code, err = parse_manual_order_style_fields(
            policy, {"entry_model": "structure_breakout"}
        )
        self.assertIsNone(err)
        self.assertEqual(code, "structure_breakout")
        self.assertEqual(style, "trend")

    def test_open_position_button_intraday(self):
        policy = TradePolicy(True, "both", True, ("BTC", "ETH"))
        self.assertEqual(
            open_position_button_label(policy, "full_margin"),
            "开仓(日内·全仓杠杆)",
        )

    def test_resolve_entry_reason_from_model(self):
        er = resolve_trade_record_entry_reason(entry_model=ENTRY_MODEL_BIG_DIV_B)
        self.assertEqual(er, "顺势/大分歧B")
        er2 = resolve_trade_record_entry_reason(entry_model=ENTRY_MODEL_LAUNCH_A)
        self.assertEqual(er2, "反转/启动A")

    def test_entry_model_display_label(self):
        self.assertEqual(entry_model_display_label(ENTRY_MODEL_LAUNCH_A), "反转/启动A")
        self.assertEqual(entry_model_display_label(ENTRY_MODEL_SMALL_DIV), "波段单/小分歧")
        self.assertEqual(entry_model_display_label("liquidity_false_break"), "波段单/假破")
        self.assertEqual(format_entry_type_display("启动A"), "反转/启动A")
        self.assertEqual(entry_model_label(ENTRY_MODEL_LAUNCH_B), "启动B")

    def test_resolve_entry_reason_trade_style_fallback(self):
        er = resolve_trade_record_entry_reason(trade_style="swing")
        self.assertEqual(er, "波段单")
        er2 = resolve_trade_record_entry_reason(trade_style="trend")
        self.assertEqual(er2, "趋势单")

    def test_build_trend_div_journal_options(self):
        opts = build_trend_div_entry_reason_options(("趋势回调",))
        self.assertEqual(opts[:5], ("反转/启动A", "反转/启动B", "顺势/大分歧A", "顺势/大分歧B", "波段单/小分歧"))
        self.assertIn("趋势单", opts)
        self.assertIn("波段单", opts)
        self.assertIn("趋势回调", opts)

    def test_build_intraday_journal_options_only_four(self):
        opts = build_intraday_entry_reason_options(
            (
                "关键位箱体突破",
                "关键位回调触价开仓",
                "关键位突破触价开仓",
            ),
            ("趋势回调", "顺势加仓"),
        )
        self.assertEqual(
            opts,
            (
                "波段单/假破",
                "波段单/结构突破",
                "关键位回调触价开仓",
                "关键位突破触价开仓",
            ),
        )

    def test_normalize_review_entry_reason(self):
        from lib.trade.entry_model_lib import normalize_review_entry_reason

        allowed = build_trend_div_entry_reason_options(())
        self.assertEqual(normalize_review_entry_reason("反转/启动A", allowed), "反转/启动A")
        self.assertEqual(normalize_review_entry_reason("启动A", allowed), "反转/启动A")
        self.assertEqual(normalize_review_entry_reason("趋势单", allowed), "趋势单")


if __name__ == "__main__":
    unittest.main()
