"""Replay test for the live bar-high breakout helper.

Verifies that ORBTradingEngine._post_orb_bar_high_breakout correctly identifies
spike-and-retrace breakouts using today's actual 2026-05-07 intraday bars
(SHAK, SNDK, ARM). Standalone — does not require the live broker, DB, or
network — bars and breakout levels are pinned in the test data.
"""
from __future__ import annotations

from unittest.mock import MagicMock

import pytest

from apps.orb_trader.engine import ORBTradingEngine
from libs.intraday.domain import ORBStrategyParams


# Bar-high reference data from /api/v1/alpaca/intraday/today on 2026-05-07.
# Only fields needed by the helper (timestamp, high, low) are kept.
SHAK_BARS = [
    {"timestamp": "2026-05-07T13:30:00+00:00", "high": 71.53,  "low": 69.075},   # ORB bar
    {"timestamp": "2026-05-07T13:35:00+00:00", "high": 70.09,  "low": 68.505},
    {"timestamp": "2026-05-07T13:40:00+00:00", "high": 70.325, "low": 68.75},
    {"timestamp": "2026-05-07T13:45:00+00:00", "high": 70.575, "low": 68.85},
    {"timestamp": "2026-05-07T13:50:00+00:00", "high": 69.03,  "low": 67.965},
    {"timestamp": "2026-05-07T13:55:00+00:00", "high": 68.29,  "low": 67.92},
]

ARM_BARS = [
    {"timestamp": "2026-05-07T13:30:00+00:00", "high": 231.475, "low": 218.25},  # ORB bar
    {"timestamp": "2026-05-07T13:35:00+00:00", "high": 232.19,  "low": 223.69},  # crosses inside timeout
    {"timestamp": "2026-05-07T13:40:00+00:00", "high": 224.04,  "low": 218.25},
    {"timestamp": "2026-05-07T13:45:00+00:00", "high": 220.35,  "low": 216.95},
    {"timestamp": "2026-05-07T13:50:00+00:00", "high": 222.87,  "low": 219.21},
    {"timestamp": "2026-05-07T13:55:00+00:00", "high": 222.65,  "low": 220.29},
]

SNDK_BARS = [
    {"timestamp": "2026-05-07T13:30:00+00:00", "high": 1396.475, "low": 1355.3},  # ORB bar
    {"timestamp": "2026-05-07T13:35:00+00:00", "high": 1388.94,  "low": 1370.69},
    {"timestamp": "2026-05-07T13:40:00+00:00", "high": 1373.57,  "low": 1342.75},
    {"timestamp": "2026-05-07T13:45:00+00:00", "high": 1348.2,   "low": 1328.12},
    {"timestamp": "2026-05-07T13:50:00+00:00", "high": 1359.66,  "low": 1344.165},
    {"timestamp": "2026-05-07T13:55:00+00:00", "high": 1367.69,  "low": 1352.4},
    # Below: post-timeout (after 09:55 ET = 13:55 UTC + 5min boundary).
    # SNDK's only crossing of 1396.475 is at 14:20 UTC (10:20 ET), past timeout.
    {"timestamp": "2026-05-07T14:00:00+00:00", "high": 1354.98,  "low": 1344.62},
    {"timestamp": "2026-05-07T14:05:00+00:00", "high": 1366.06,  "low": 1345.93},
    {"timestamp": "2026-05-07T14:10:00+00:00", "high": 1369.58,  "low": 1359.39},
    {"timestamp": "2026-05-07T14:15:00+00:00", "high": 1389.71,  "low": 1362.02},
    {"timestamp": "2026-05-07T14:20:00+00:00", "high": 1399.98,  "low": 1377.18},  # crosses, post-timeout
]


def _make_engine(orb_minutes: int = 5, order_timeout_minutes: int = 25) -> ORBTradingEngine:
    """Build an engine instance with only the fields the helper touches."""
    params = ORBStrategyParams(
        orb_minutes=orb_minutes,
        order_timeout_minutes=order_timeout_minutes,
        live_breakout_use_bar_high=True,
    )
    eng = ORBTradingEngine.__new__(ORBTradingEngine)
    eng._params = params
    eng._session = MagicMock(session_id="test")
    return eng


@pytest.mark.parametrize(
    "name,bars,direction,breakout_level,expected,reason",
    [
        ("ARM_inside_timeout", ARM_BARS, "bullish", 231.475, True,
         "13:35 UTC bar high 232.19 crosses 231.475 inside 25-min timeout"),
        ("SNDK_post_timeout",  SNDK_BARS, "bullish", 1396.475, False,
         "Only crossing (14:20 UTC high 1399.98) is past 25-min timeout"),
        ("SHAK_no_breakout",   SHAK_BARS, "bullish", 71.53,    False,
         "No post-ORB bar high reaches 71.53"),
    ],
)
def test_bar_high_breakout_replay(name, bars, direction, breakout_level, expected, reason):
    eng = _make_engine(orb_minutes=5, order_timeout_minutes=25)
    actual = eng._post_orb_bar_high_breakout(
        bars, direction, breakout_level, "2026-05-07"
    )
    assert actual is expected, f"{name}: expected {expected} ({reason}), got {actual}"


def test_bar_high_breakout_skips_orb_bar():
    """The ORB bar's own high must not be allowed to trigger its own breakout
    (breakout_level is set FROM that bar, so it always equals it)."""
    eng = _make_engine(orb_minutes=5, order_timeout_minutes=25)
    # Only the ORB bar — no post-ORB bars at all.
    bars = [{"timestamp": "2026-05-07T13:30:00+00:00", "high": 100.0, "low": 90.0}]
    assert eng._post_orb_bar_high_breakout(bars, "bullish", 100.0, "2026-05-07") is False


def test_bar_high_breakout_short_direction():
    """Bearish direction triggers when bar low <= breakout_level (the ORB low)."""
    eng = _make_engine(orb_minutes=5, order_timeout_minutes=25)
    bars = [
        {"timestamp": "2026-05-07T13:30:00+00:00", "high": 100.0, "low": 95.0},  # ORB
        {"timestamp": "2026-05-07T13:35:00+00:00", "high": 99.0,  "low": 94.5},  # crosses
    ]
    assert eng._post_orb_bar_high_breakout(bars, "bearish", 95.0, "2026-05-07") is True
