"""
Test script for Python code snippets extracted from docs/examples/ batch 2.

Files tested:
  1. docs/examples/bracket-orders.mdx  (EXISTS - 6 snippets)
  2. docs/examples/weather-market.mdx  (DOES NOT EXIST - skipped)
  3. docs/examples/sports-market.mdx   (DOES NOT EXIST - skipped)
  4. docs/examples/crypto-feeds.mdx    (DOES NOT EXIST - skipped)
"""

import sys
import traceback

results = []


def record(name, passed, detail=""):
    status = "PASS" if passed else "FAIL"
    results.append((name, status, detail))
    tag = f"[{status}]"
    msg = f"{tag} {name}"
    if detail:
        msg += f" -- {detail}"
    print(msg)


# =========================================================================
# FILE: docs/examples/bracket-orders.mdx
# =========================================================================

# --- Snippet 1: Full bracket order example (submit_bracket + tick + trigger) ---
def test_bracket_full_example():
    """
    ```python
    from horizon import Engine, OrderRequest, Side, OrderSide, RiskConfig

    engine = Engine(risk_config=RiskConfig(max_position_per_market=1000))

    entry_id, sl_id, tp_id = engine.submit_bracket(
        request=OrderRequest(
            market_id="btc-100k",
            side=Side.Yes,
            order_side=OrderSide.Buy,
            size=10.0,
            price=0.55,
        ),
        stop_trigger=0.45,
        take_profit_trigger=0.70,
    )

    print(f"Entry: {entry_id}")
    print(f"Stop-loss: {sl_id}")
    print(f"Take-profit: {tp_id}")

    pending = engine.pending_contingent_orders()
    print(f"Pending contingent orders: {len(pending)}")

    engine.tick("btc-100k", 0.55)

    triggered = engine.check_contingent_triggers("btc-100k", 0.45)
    print(f"Triggered: {triggered}")

    pending = engine.pending_contingent_orders()
    print(f"Remaining contingent: {len(pending)}")
    ```
    """
    try:
        from horizon import Engine, OrderRequest, Side, OrderSide, RiskConfig

        engine = Engine(risk_config=RiskConfig(max_position_per_market=1000))

        entry_id, sl_id, tp_id = engine.submit_bracket(
            request=OrderRequest(
                market_id="btc-100k",
                side=Side.Yes,
                order_side=OrderSide.Buy,
                size=10.0,
                price=0.55,
            ),
            stop_trigger=0.45,
            take_profit_trigger=0.70,
        )

        assert isinstance(entry_id, str), f"entry_id should be str, got {type(entry_id)}"
        assert isinstance(sl_id, str), f"sl_id should be str, got {type(sl_id)}"
        assert isinstance(tp_id, str), f"tp_id should be str, got {type(tp_id)}"

        pending = engine.pending_contingent_orders()
        assert len(pending) == 2, f"Expected 2 pending contingent orders, got {len(pending)}"

        # Tick to fill the entry
        engine.tick("btc-100k", 0.55)

        # Price drops to 0.45 -> stop-loss triggers
        triggered = engine.check_contingent_triggers("btc-100k", 0.45)
        assert triggered == 1, f"Expected 1 triggered, got {triggered}"

        # OCO should have canceled the take-profit
        pending = engine.pending_contingent_orders()
        assert len(pending) == 0, f"Expected 0 pending after OCO, got {len(pending)}"

        record("bracket-orders/snippet1-full-bracket", True)
    except Exception as e:
        record("bracket-orders/snippet1-full-bracket", False, f"{e}\n{traceback.format_exc()}")


# --- Snippet 2: Standalone stop-loss ---
def test_standalone_stop_loss():
    """
    ```python
    from horizon import Engine, Side, OrderSide, RiskConfig

    engine = Engine(risk_config=RiskConfig(max_position_per_market=1000))

    sl_id = engine.add_stop_loss(
        market_id="btc-100k",
        side=Side.Yes,
        order_side=OrderSide.Sell,
        size=10.0,
        trigger_price=0.45,
    )

    print(f"Stop-loss ID: {sl_id}")

    triggered = engine.check_contingent_triggers("btc-100k", 0.46)
    print(f"Triggered at 0.46: {triggered}")  # 0

    triggered = engine.check_contingent_triggers("btc-100k", 0.44)
    print(f"Triggered at 0.44: {triggered}")  # 1
    ```
    """
    try:
        from horizon import Engine, Side, OrderSide, RiskConfig

        engine = Engine(risk_config=RiskConfig(max_position_per_market=1000))

        sl_id = engine.add_stop_loss(
            market_id="btc-100k",
            side=Side.Yes,
            order_side=OrderSide.Sell,
            size=10.0,
            trigger_price=0.45,
        )

        assert isinstance(sl_id, str), f"sl_id should be str, got {type(sl_id)}"

        triggered = engine.check_contingent_triggers("btc-100k", 0.46)
        assert triggered == 0, f"Expected 0 triggered at 0.46, got {triggered}"

        triggered = engine.check_contingent_triggers("btc-100k", 0.44)
        assert triggered == 1, f"Expected 1 triggered at 0.44, got {triggered}"

        record("bracket-orders/snippet2-standalone-stop-loss", True)
    except Exception as e:
        record("bracket-orders/snippet2-standalone-stop-loss", False, f"{e}\n{traceback.format_exc()}")


# --- Snippet 3: Standalone take-profit (price-based and pnl-based) ---
def test_standalone_take_profit():
    """
    ```python
    from horizon import Engine, Side, OrderSide, RiskConfig

    engine = Engine(risk_config=RiskConfig(max_position_per_market=1000))

    tp_price_id = engine.add_take_profit(
        market_id="btc-100k",
        side=Side.Yes,
        order_side=OrderSide.Sell,
        size=10.0,
        trigger_price=0.70,
    )

    tp_pnl_id = engine.add_take_profit(
        market_id="btc-100k",
        side=Side.Yes,
        order_side=OrderSide.Sell,
        size=10.0,
        trigger_price=0.70,
        trigger_pnl=5.0,
    )
    ```
    """
    try:
        from horizon import Engine, Side, OrderSide, RiskConfig

        engine = Engine(risk_config=RiskConfig(max_position_per_market=1000))

        tp_price_id = engine.add_take_profit(
            market_id="btc-100k",
            side=Side.Yes,
            order_side=OrderSide.Sell,
            size=10.0,
            trigger_price=0.70,
        )

        assert isinstance(tp_price_id, str), f"tp_price_id should be str, got {type(tp_price_id)}"

        tp_pnl_id = engine.add_take_profit(
            market_id="btc-100k",
            side=Side.Yes,
            order_side=OrderSide.Sell,
            size=10.0,
            trigger_price=0.70,
            trigger_pnl=5.0,
        )

        assert isinstance(tp_pnl_id, str), f"tp_pnl_id should be str, got {type(tp_pnl_id)}"

        record("bracket-orders/snippet3-standalone-take-profit", True)
    except Exception as e:
        record("bracket-orders/snippet3-standalone-take-profit", False, f"{e}\n{traceback.format_exc()}")


# --- Snippet 4: Manual OCO linking + cancel ---
def test_manual_oco_linking():
    """
    ```python
    from horizon import Engine, Side, OrderSide, RiskConfig

    engine = Engine(risk_config=RiskConfig(max_position_per_market=1000))

    sl_id = engine.add_stop_loss(
        market_id="btc-100k",
        side=Side.Yes,
        order_side=OrderSide.Sell,
        size=10.0,
        trigger_price=0.40,
    )

    tp_id = engine.add_take_profit(
        market_id="btc-100k",
        side=Side.Yes,
        order_side=OrderSide.Sell,
        size=10.0,
        trigger_price=0.80,
    )

    canceled = engine.cancel_contingent(sl_id)
    print(f"SL canceled: {canceled}")

    pending = engine.pending_contingent_orders()
    print(f"Pending: {len(pending)}")
    ```
    """
    try:
        from horizon import Engine, Side, OrderSide, RiskConfig

        engine = Engine(risk_config=RiskConfig(max_position_per_market=1000))

        sl_id = engine.add_stop_loss(
            market_id="btc-100k",
            side=Side.Yes,
            order_side=OrderSide.Sell,
            size=10.0,
            trigger_price=0.40,
        )

        tp_id = engine.add_take_profit(
            market_id="btc-100k",
            side=Side.Yes,
            order_side=OrderSide.Sell,
            size=10.0,
            trigger_price=0.80,
        )

        canceled = engine.cancel_contingent(sl_id)
        assert canceled is True, f"Expected cancel_contingent to return True, got {canceled}"

        pending = engine.pending_contingent_orders()
        assert len(pending) == 1, f"Expected 1 pending after cancel, got {len(pending)}"

        record("bracket-orders/snippet4-manual-oco-cancel", True)
    except Exception as e:
        record("bracket-orders/snippet4-manual-oco-cancel", False, f"{e}\n{traceback.format_exc()}")


# --- Snippet 5: Amending contingent orders (cancel and replace) ---
def test_amend_contingent():
    """
    ```python
    from horizon import Engine, Side, OrderSide, RiskConfig

    engine = Engine(risk_config=RiskConfig(max_position_per_market=1000))

    sl_id = engine.add_stop_loss(
        market_id="btc-100k",
        side=Side.Yes,
        order_side=OrderSide.Sell,
        size=10.0,
        trigger_price=0.45,
    )

    engine.cancel_contingent(sl_id)

    new_sl_id = engine.add_stop_loss(
        market_id="btc-100k",
        side=Side.Yes,
        order_side=OrderSide.Sell,
        size=10.0,
        trigger_price=0.50,
    )

    print(f"Trailed stop from 0.45 to 0.50: {new_sl_id}")
    ```
    """
    try:
        from horizon import Engine, Side, OrderSide, RiskConfig

        engine = Engine(risk_config=RiskConfig(max_position_per_market=1000))

        sl_id = engine.add_stop_loss(
            market_id="btc-100k",
            side=Side.Yes,
            order_side=OrderSide.Sell,
            size=10.0,
            trigger_price=0.45,
        )

        engine.cancel_contingent(sl_id)

        new_sl_id = engine.add_stop_loss(
            market_id="btc-100k",
            side=Side.Yes,
            order_side=OrderSide.Sell,
            size=10.0,
            trigger_price=0.50,
        )

        assert isinstance(new_sl_id, str), f"new_sl_id should be str, got {type(new_sl_id)}"
        # Verify only one pending contingent order
        pending = engine.pending_contingent_orders()
        assert len(pending) == 1, f"Expected 1 pending after amend, got {len(pending)}"

        record("bracket-orders/snippet5-amend-contingent", True)
    except Exception as e:
        record("bracket-orders/snippet5-amend-contingent", False, f"{e}\n{traceback.format_exc()}")


# --- Snippet 6: Integration with hz.run() ---
def test_hz_run_bracket_integration():
    """
    ```python
    import horizon as hz

    _brackets: dict[str, tuple[str, str, str]] = {}

    def fair_value(ctx: hz.Context) -> float:
        feed = ctx.feeds.get("default", hz.Context.FeedData())
        return feed.price if feed.price > 0 else 0.50

    def quoter(ctx: hz.Context, fair: float) -> list[hz.Quote]:
        if ctx.inventory.net != 0:
            return []
        return hz.quotes(fair, spread=0.04, size=5)

    hz.run(
        name="bracket_mm",
        markets=["btc-100k"],
        pipeline=[fair_value, quoter],
        risk=hz.Risk(max_position=50, max_drawdown_pct=5),
        interval=1.0,
        mode="paper",
    )
    ```

    We verify imports, function definitions, and Market/Quote construction.
    We do NOT actually call hz.run().
    """
    try:
        import horizon as hz

        # Test 1: Check imports exist
        assert hasattr(hz, "Context"), "hz.Context not found"
        assert hasattr(hz, "Quote"), "hz.Quote not found"
        assert hasattr(hz, "quotes"), "hz.quotes not found"
        assert hasattr(hz, "Risk"), "hz.Risk not found"
        assert hasattr(hz, "run"), "hz.run not found"

        # Test 2: The snippet uses hz.Context.FeedData() - check if this works
        # FeedData is defined in horizon.context, not as a nested class of Context
        try:
            _ = hz.Context.FeedData()
            feed_data_on_context = True
        except AttributeError:
            feed_data_on_context = False

        if not feed_data_on_context:
            # FeedData is NOT accessible as hz.Context.FeedData - this is a doc bug
            record(
                "bracket-orders/snippet6-hz-run-integration",
                False,
                "hz.Context.FeedData() raises AttributeError -- FeedData is not a nested "
                "class of Context. It should be imported from horizon.context.FeedData or "
                "added to hz namespace.",
            )
            return

        # Test 3: Verify function definitions work
        _brackets: dict[str, tuple[str, str, str]] = {}

        def fair_value(ctx: hz.Context) -> float:
            feed = ctx.feeds.get("default", hz.Context.FeedData())
            return feed.price if feed.price > 0 else 0.50

        def quoter(ctx: hz.Context, fair: float) -> list[hz.Quote]:
            if ctx.inventory.net != 0:
                return []
            return hz.quotes(fair, spread=0.04, size=5)

        # Test 4: Verify fair_value with a default context
        from horizon.context import FeedData
        ctx = hz.Context()
        val = fair_value(ctx)
        assert val == 0.50, f"Expected 0.50 from empty ctx, got {val}"

        # Test 5: Verify quoter
        quotes = quoter(ctx, 0.55)
        assert isinstance(quotes, list), f"Expected list, got {type(quotes)}"
        assert len(quotes) == 1, f"Expected 1 quote, got {len(quotes)}"

        # Test 6: Verify Risk construction
        risk = hz.Risk(max_position=50, max_drawdown_pct=5)
        assert risk.max_position == 50
        assert risk.max_drawdown_pct == 5

        record("bracket-orders/snippet6-hz-run-integration", True)
    except Exception as e:
        record("bracket-orders/snippet6-hz-run-integration", False, f"{e}\n{traceback.format_exc()}")


# =========================================================================
# SKIPPED FILES (do not exist)
# =========================================================================

print("=" * 70)
print("docs/examples/bracket-orders.mdx  -- EXISTS (6 snippets)")
print("docs/examples/weather-market.mdx  -- DOES NOT EXIST (skipped)")
print("docs/examples/sports-market.mdx   -- DOES NOT EXIST (skipped)")
print("docs/examples/crypto-feeds.mdx    -- DOES NOT EXIST (skipped)")
print("=" * 70)
print()

# Run all tests
test_bracket_full_example()
test_standalone_stop_loss()
test_standalone_take_profit()
test_manual_oco_linking()
test_amend_contingent()
test_hz_run_bracket_integration()

# Summary
print()
print("=" * 70)
print("SUMMARY")
print("=" * 70)
passed = sum(1 for _, s, _ in results if s == "PASS")
failed = sum(1 for _, s, _ in results if s == "FAIL")
total = len(results)
print(f"  Total: {total}  |  PASS: {passed}  |  FAIL: {failed}")
for name, status, detail in results:
    print(f"  [{status}] {name}")
    if detail and status == "FAIL":
        # Print first 3 lines of detail for brevity
        for line in detail.strip().split("\n")[:3]:
            print(f"         {line}")
print("=" * 70)

sys.exit(0 if failed == 0 else 1)
