"""
Test all Python code snippets from:
  1. docs/simulation.mdx
  2. docs/backtesting.mdx
  3. docs/execution-algos.mdx

Each snippet is tested as-is from the docs (or with minimal scaffolding
like data generation when the snippet relies on external state).
We do NOT fix code -- just report PASS/FAIL.
"""

import sys
import traceback
import time
import os

# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
results = []


def run_test(name, fn):
    """Run fn(), print PASS/FAIL, collect result."""
    try:
        fn()
        print(f"PASS  {name}")
        results.append((name, True, None))
    except Exception as exc:
        tb = traceback.format_exc()
        print(f"FAIL  {name}")
        print(f"      {exc}")
        print(f"      {tb.splitlines()[-2].strip() if len(tb.splitlines()) >= 2 else ''}")
        results.append((name, False, str(exc)))


# ===================================================================
# SIMULATION (docs/simulation.mdx)
# ===================================================================

def test_sim_quick_start():
    """simulation.mdx - Quick Start"""
    import horizon as hz

    positions = [
        hz.SimPosition("election-winner", "yes", 100.0, 0.50, 0.60),
        hz.SimPosition("fed-rate-cut", "no", 50.0, 0.40, 0.30),
    ]

    result = hz.simulate(positions=positions, scenarios=50000, seed=42)

    print(f"  Mean PnL:    ${result.mean_pnl:.2f}")
    print(f"  VaR 95:      ${result.var_95:.2f}")
    print(f"  CVaR 95:     ${result.cvar_95:.2f}")
    print(f"  Win Prob:    {result.win_probability:.1%}")
    print(f"  Max Loss:    ${result.max_loss:.2f}")
    print(f"  Max Gain:    ${result.max_gain:.2f}")


def test_sim_position_constructor():
    """simulation.mdx - SimPosition constructor"""
    import horizon as hz

    pos = hz.SimPosition(
        market_id="election-winner",
        side="yes",
        size=100.0,
        entry_price=0.50,
        current_price=0.60,
    )
    assert pos.market_id == "election-winner"


def test_sim_monte_carlo_result_table():
    """simulation.mdx - SimulationResult via hz.monte_carlo"""
    import horizon as hz

    positions = [
        hz.SimPosition("election-winner", "yes", 100.0, 0.50, 0.60),
    ]
    result = hz.monte_carlo(positions, scenarios=10000, seed=42)
    # Access all documented fields
    _ = result.mean_pnl
    _ = result.median_pnl
    _ = result.std_dev
    _ = result.var_95
    _ = result.var_99
    _ = result.cvar_95
    _ = result.max_loss
    _ = result.max_gain
    _ = result.win_probability
    _ = result.scenario_pnl
    _ = result.percentiles


def test_sim_monte_carlo_direct():
    """simulation.mdx - hz.monte_carlo() direct call"""
    import horizon as hz

    positions = [
        hz.SimPosition("election-winner", "yes", 100.0, 0.50, 0.60),
    ]
    result = hz.monte_carlo(
        positions=positions,
        scenarios=10000,
        correlation_matrix=None,
        seed=42,
    )
    assert result.mean_pnl is not None


def test_sim_simulate_wrapper():
    """simulation.mdx - hz.simulate() wrapper signature"""
    import horizon as hz

    positions = [
        hz.SimPosition("election-winner", "yes", 100.0, 0.50, 0.60),
    ]
    result = hz.simulate(
        engine=None,
        positions=positions,
        scenarios=10000,
        correlations=None,
        prices=None,
        seed=None,
    )
    assert result.mean_pnl is not None


def test_sim_correlation_dict():
    """simulation.mdx - simulate with correlation dict"""
    import horizon as hz

    positions = [
        hz.SimPosition("election-winner", "yes", 100.0, 0.50, 0.60),
        hz.SimPosition("fed-rate-cut", "no", 50.0, 0.40, 0.30),
        hz.SimPosition("recession", "yes", 75.0, 0.35, 0.40),
    ]
    result = hz.simulate(
        positions=positions,
        correlations={
            ("election-winner", "fed-rate-cut"): 0.3,
            ("election-winner", "recession"): -0.5,
        },
    )
    assert result.mean_pnl is not None


def test_sim_portfolio_risk_assessment():
    """simulation.mdx - Portfolio Risk Assessment example"""
    import horizon as hz

    positions = [
        hz.SimPosition("trump-win", "yes", 200.0, 0.45, 0.55),
        hz.SimPosition("fed-cut-march", "yes", 100.0, 0.60, 0.65),
        hz.SimPosition("btc-100k", "no", 150.0, 0.30, 0.25),
    ]

    result = hz.simulate(
        positions=positions,
        correlations={
            ("trump-win", "fed-cut-march"): 0.2,
            ("trump-win", "btc-100k"): -0.1,
        },
        scenarios=50000,
        seed=42,
    )

    print(f"  Expected PnL:  ${result.mean_pnl:+.2f}")
    print(f"  Std Dev:        ${result.std_dev:.2f}")
    print(f"  VaR 95:         ${result.var_95:+.2f}")
    print(f"  CVaR 95:        ${result.cvar_95:+.2f}")
    print(f"  Win Rate:       {result.win_probability:.1%}")
    print(f"  Best Case:      ${result.max_gain:+.2f}")
    print(f"  Worst Case:     ${result.max_loss:+.2f}")


def test_sim_from_live_engine():
    """simulation.mdx - Simulate from Live Engine"""
    import horizon as hz

    engine = hz.Engine(risk_config=hz.RiskConfig(max_position_per_market=200))
    # No positions -- just test the call works
    result = hz.simulate(engine=engine, scenarios=50000)
    print(f"  Portfolio VaR 95: ${result.var_95:.2f}")


def test_sim_correlated_vs_uncorrelated():
    """simulation.mdx - Comparing Correlated vs Uncorrelated Risk"""
    import horizon as hz

    positions = [
        hz.SimPosition("mkt1", "yes", 100.0, 0.50, 0.60),
        hz.SimPosition("mkt2", "yes", 100.0, 0.50, 0.60),
    ]

    r_uncorr = hz.simulate(positions=positions, scenarios=50000, seed=42)

    r_corr = hz.simulate(
        positions=positions,
        correlations={("mkt1", "mkt2"): 0.9},
        scenarios=50000,
        seed=42,
    )

    print(f"  Uncorrelated StdDev: ${r_uncorr.std_dev:.2f}")
    print(f"  Correlated StdDev:   ${r_corr.std_dev:.2f}")


def test_sim_seed_determinism():
    """simulation.mdx - Seed Determinism"""
    import horizon as hz

    positions = [
        hz.SimPosition("mkt1", "yes", 100.0, 0.50, 0.60),
    ]

    r1 = hz.monte_carlo(positions, 10000, None, 42)
    r2 = hz.monte_carlo(positions, 10000, None, 42)
    assert r1.mean_pnl == r2.mean_pnl  # Exact match

    r3 = hz.monte_carlo(positions, 10000, None, 99)
    # r3.mean_pnl will be close to r1.mean_pnl but not identical


# ===================================================================
# BACKTESTING (docs/backtesting.mdx)
# ===================================================================

def test_bt_quick_start():
    """backtesting.mdx - Quick Start"""
    import horizon as hz
    from horizon import Side, OrderSide, OrderRequest

    def model(ctx):
        return {"fair": ctx.feed.price * 1.02}

    def quoter(ctx, fair):
        if fair > ctx.feed.price:
            return OrderRequest(
                market_id=ctx.market_id,
                side=Side.Yes,
                order_side=OrderSide.Buy,
                price=ctx.feed.price,
                size=10.0,
            )

    result = hz.backtest(
        name="simple-backtest",
        markets=["my-market"],
        data=[
            {"timestamp": 1000, "price": 0.50},
            {"timestamp": 1001, "price": 0.52},
            {"timestamp": 1002, "price": 0.48},
            {"timestamp": 1003, "price": 0.55},
            {"timestamp": 1004, "price": 0.60},
        ],
        pipeline=[model, quoter],
    )

    print(f"  {result.summary()[:80]}...")


def test_bt_data_list_of_dicts():
    """backtesting.mdx - Data format: List of Dicts"""
    import horizon as hz
    from horizon import Side, OrderSide, OrderRequest

    def model(ctx):
        return {"fair": ctx.feed.price * 1.01}

    def quoter(ctx, fair):
        if fair > ctx.feed.price:
            return OrderRequest(
                market_id=ctx.market_id,
                side=Side.Yes,
                order_side=OrderSide.Buy,
                price=ctx.feed.price,
                size=10.0,
            )

    data = [
        {"timestamp": 1700000000, "price": 0.55, "bid": 0.54, "ask": 0.56, "volume": 100},
        {"timestamp": 1700000001, "price": 0.56, "bid": 0.55, "ask": 0.57, "volume": 150},
        {"timestamp": 1700000002, "price": 0.54, "bid": 0.53, "ask": 0.55, "volume": 80},
    ]

    result = hz.backtest(
        markets=["my-market"],
        data=data,
        pipeline=[model, quoter],
    )
    assert result is not None


def test_bt_data_pandas_dataframe():
    """backtesting.mdx - Data format: Pandas DataFrame"""
    try:
        import pandas as pd
    except ImportError:
        raise RuntimeError("pandas not installed, skipping")

    import horizon as hz
    from horizon import Side, OrderSide, OrderRequest

    def model(ctx):
        return {"fair": ctx.feed.price * 1.01}

    def quoter(ctx, fair):
        if fair > ctx.feed.price:
            return OrderRequest(
                market_id=ctx.market_id,
                side=Side.Yes,
                order_side=OrderSide.Buy,
                price=ctx.feed.price,
                size=10.0,
            )

    df = pd.DataFrame({
        "timestamp": [1700000000, 1700000001, 1700000002],
        "price": [0.55, 0.56, 0.54],
        "bid": [0.54, 0.55, 0.53],
        "ask": [0.56, 0.57, 0.55],
        "volume": [100, 150, 80],
    })

    result = hz.backtest(
        markets=["my-market"],
        data=df,
        pipeline=[model, quoter],
    )
    assert result is not None


def test_bt_data_multi_feed_dict():
    """backtesting.mdx - Data format: Multi-Feed Dict"""
    import horizon as hz
    from horizon import Side, OrderSide, OrderRequest

    def model(ctx):
        return {"fair": ctx.feed.price * 1.01}

    def quoter(ctx, fair):
        if fair > ctx.feed.price:
            return OrderRequest(
                market_id=ctx.market_id,
                side=Side.Yes,
                order_side=OrderSide.Buy,
                price=ctx.feed.price,
                size=10.0,
            )

    data = {
        "polymarket_book": [
            {"timestamp": 1700000000, "price": 0.55, "bid": 0.54, "ask": 0.56},
            {"timestamp": 1700000001, "price": 0.56, "bid": 0.55, "ask": 0.57},
        ],
        "binance": [
            {"timestamp": 1700000000, "price": 95000.0},
            {"timestamp": 1700000001, "price": 95100.0},
        ],
    }

    result = hz.backtest(
        markets=["btc-above-100k"],
        data=data,
        feeds={"btc-above-100k": "polymarket_book"},
        pipeline=[model, quoter],
    )
    assert result is not None


def test_bt_l2_book_data():
    """backtesting.mdx - L2 Orderbook Simulation (book_data)"""
    import horizon as hz
    from horizon import Side, OrderSide, OrderRequest

    def my_strategy(ctx):
        return OrderRequest(
            market_id=ctx.market_id,
            side=Side.Yes,
            order_side=OrderSide.Buy,
            price=ctx.feed.price,
            size=5.0,
        )

    tick_data = [
        {"timestamp": 1700000000, "price": 0.55},
        {"timestamp": 1700000001, "price": 0.56},
    ]

    book_data = {
        "election-winner": [
            {
                "timestamp": 1700000000,
                "bids": [(0.54, 100), (0.53, 200), (0.52, 500)],
                "asks": [(0.56, 100), (0.57, 200), (0.58, 500)],
            },
            {
                "timestamp": 1700000001,
                "bids": [(0.55, 150), (0.54, 250)],
                "asks": [(0.57, 150), (0.58, 250)],
            },
        ],
    }

    result = hz.backtest(
        data=tick_data,
        markets=["election-winner"],
        pipeline=[my_strategy],
        book_data=book_data,
    )
    assert result is not None


def test_bt_fill_model_deterministic():
    """backtesting.mdx - Fill Model: deterministic"""
    import horizon as hz
    from horizon import Side, OrderSide, OrderRequest

    def my_strategy(ctx):
        return OrderRequest(
            market_id=ctx.market_id,
            side=Side.Yes,
            order_side=OrderSide.Buy,
            price=ctx.feed.price,
            size=5.0,
        )

    data = [
        {"timestamp": 1700000000, "price": 0.55},
        {"timestamp": 1700000001, "price": 0.56},
    ]

    book_data = {
        "market": [
            {
                "timestamp": 1700000000,
                "bids": [(0.54, 100)],
                "asks": [(0.56, 100)],
            },
            {
                "timestamp": 1700000001,
                "bids": [(0.55, 150)],
                "asks": [(0.57, 150)],
            },
        ],
    }

    result = hz.backtest(
        data=data,
        pipeline=[my_strategy],
        book_data=book_data,
        fill_model="deterministic",
    )
    assert result is not None


def test_bt_fill_model_probabilistic():
    """backtesting.mdx - Fill Model: probabilistic"""
    import horizon as hz
    from horizon import Side, OrderSide, OrderRequest

    def my_strategy(ctx):
        return OrderRequest(
            market_id=ctx.market_id,
            side=Side.Yes,
            order_side=OrderSide.Buy,
            price=ctx.feed.price,
            size=5.0,
        )

    data = [
        {"timestamp": 1700000000, "price": 0.55},
        {"timestamp": 1700000001, "price": 0.56},
    ]

    book_data = {
        "market": [
            {
                "timestamp": 1700000000,
                "bids": [(0.54, 100)],
                "asks": [(0.56, 100)],
            },
            {
                "timestamp": 1700000001,
                "bids": [(0.55, 150)],
                "asks": [(0.57, 150)],
            },
        ],
    }

    result = hz.backtest(
        data=data,
        pipeline=[my_strategy],
        book_data=book_data,
        fill_model="probabilistic",
        fill_model_params={"lambda": 1.0, "queue_frac": 0.5},
        rng_seed=42,
    )
    assert result is not None


def test_bt_fill_model_glft():
    """backtesting.mdx - Fill Model: GLFT"""
    import horizon as hz
    from horizon import Side, OrderSide, OrderRequest

    def my_strategy(ctx):
        return OrderRequest(
            market_id=ctx.market_id,
            side=Side.Yes,
            order_side=OrderSide.Buy,
            price=ctx.feed.price,
            size=5.0,
        )

    data = [
        {"timestamp": 1700000000, "price": 0.55},
        {"timestamp": 1700000001, "price": 0.56},
    ]

    book_data = {
        "market": [
            {
                "timestamp": 1700000000,
                "bids": [(0.54, 100)],
                "asks": [(0.56, 100)],
            },
            {
                "timestamp": 1700000001,
                "bids": [(0.55, 150)],
                "asks": [(0.57, 150)],
            },
        ],
    }

    result = hz.backtest(
        data=data,
        pipeline=[my_strategy],
        book_data=book_data,
        fill_model="glft",
        fill_model_params={"intensity": 1.0, "kappa": 1.5},
        rng_seed=42,
    )
    assert result is not None


def test_bt_market_impact():
    """backtesting.mdx - Market Impact"""
    import horizon as hz
    from horizon import Side, OrderSide, OrderRequest

    def my_strategy(ctx):
        return OrderRequest(
            market_id=ctx.market_id,
            side=Side.Yes,
            order_side=OrderSide.Buy,
            price=ctx.feed.price,
            size=5.0,
        )

    data = [
        {"timestamp": 1700000000, "price": 0.55},
        {"timestamp": 1700000001, "price": 0.56},
    ]

    book_data = {
        "market": [
            {
                "timestamp": 1700000000,
                "bids": [(0.54, 100)],
                "asks": [(0.56, 100)],
            },
            {
                "timestamp": 1700000001,
                "bids": [(0.55, 150)],
                "asks": [(0.57, 150)],
            },
        ],
    }

    result = hz.backtest(
        data=data,
        pipeline=[my_strategy],
        book_data=book_data,
        impact_temporary_bps=5.0,
        impact_permanent_fraction=0.3,
    )
    assert result is not None


def test_bt_latency_simulation():
    """backtesting.mdx - Latency Simulation"""
    import horizon as hz
    from horizon import Side, OrderSide, OrderRequest

    def my_strategy(ctx):
        return OrderRequest(
            market_id=ctx.market_id,
            side=Side.Yes,
            order_side=OrderSide.Buy,
            price=ctx.feed.price,
            size=5.0,
        )

    data = [
        {"timestamp": 1700000000, "price": 0.55},
        {"timestamp": 1700000001, "price": 0.56},
    ]

    book_data = {
        "market": [
            {
                "timestamp": 1700000000,
                "bids": [(0.54, 100)],
                "asks": [(0.56, 100)],
            },
            {
                "timestamp": 1700000001,
                "bids": [(0.55, 150)],
                "asks": [(0.57, 150)],
            },
        ],
    }

    result = hz.backtest(
        data=data,
        pipeline=[my_strategy],
        book_data=book_data,
        latency_ms=50.0,
    )
    assert result is not None


def test_bt_calibration_curve():
    """backtesting.mdx - Calibration Curve"""
    from horizon._horizon import calibration_curve

    result = calibration_curve(
        predictions=[0.3, 0.7, 0.9, 0.1, 0.6, 0.8],
        outcomes=[0.0, 1.0, 1.0, 0.0, 1.0, 0.0],
        n_bins=5,
    )

    print(f"  Brier Score: {result.brier_score:.4f}")
    print(f"  Log Loss:    {result.log_loss:.4f}")
    print(f"  ECE:         {result.ece:.4f}")

    for center, freq, count in result.bins:
        print(f"    Predicted ~{center:.1%}: Actual {freq:.1%} (n={count})")


def test_bt_log_loss():
    """backtesting.mdx - Log Loss"""
    from horizon._horizon import log_loss

    ll = log_loss(
        predictions=[0.7, 0.3, 0.9],
        outcomes=[1.0, 0.0, 1.0],
    )
    print(f"  Log Loss: {ll:.4f}")


def test_bt_edge_decay():
    """backtesting.mdx - Edge Decay"""
    from horizon._horizon import edge_decay

    result = edge_decay(
        entry_prices=[0.45, 0.55, 0.40, 0.60],
        outcomes=[1.0, 1.0, 0.0, 0.0],
        entry_ts=[1000.0, 2000.0, 3000.0, 4000.0],
        resolution_ts=[5000.0, 5000.0, 5000.0, 5000.0],
        n_buckets=10,
    )

    print(f"  Edge half-life: {result.half_life_hours:.1f} hours")
    for hours, avg_edge in result.decay_curve:
        print(f"    {hours:.0f}h before resolution: {avg_edge:.4f} avg edge")


def test_bt_walk_forward():
    """backtesting.mdx - Walk-Forward Optimization"""
    import horizon as hz
    from horizon.walkforward import walk_forward
    from horizon import Side, OrderSide, OrderRequest

    # Generate sample tick data
    tick_data = [
        {"timestamp": 1700000000 + i, "price": 0.50 + (i % 10) * 0.01}
        for i in range(200)
    ]

    def pipeline_factory(params):
        spread = params["spread"]
        size = params["size"]

        def quoter(ctx):
            fair = ctx.feed.price
            return hz.quotes(fair=fair, spread=spread, size=size)

        return [quoter]

    result = walk_forward(
        data=tick_data,
        pipeline_factory=pipeline_factory,
        param_grid={
            "spread": [0.02, 0.04],
            "size": [5, 10],
        },
        n_splits=3,
        train_ratio=0.7,
        expanding=True,
        objective="sharpe_ratio",
    )

    for i, (window, params) in enumerate(zip(result.windows, result.best_params_per_window)):
        test_res = result.test_results[i]
        print(f"  Window {i}: best params={params}, OOS Sharpe={test_res.metrics.sharpe_ratio:.3f}")

    m = result.aggregate_metrics
    print(f"  Aggregate OOS: Return={m.total_return_pct:.2f}%, Sharpe={m.sharpe_ratio:.3f}")


def test_bt_result_metrics():
    """backtesting.mdx - BacktestResult.metrics (all fields)"""
    import horizon as hz
    from horizon import Side, OrderSide, OrderRequest

    def model(ctx):
        return {"fair": ctx.feed.price * 1.02}

    def quoter(ctx, fair):
        if fair > ctx.feed.price:
            return OrderRequest(
                market_id=ctx.market_id,
                side=Side.Yes,
                order_side=OrderSide.Buy,
                price=ctx.feed.price,
                size=10.0,
            )

    import random
    random.seed(42)
    price = 0.50
    data = []
    for i in range(200):
        price += random.gauss(0, 0.01)
        price = max(0.01, min(0.99, price))
        data.append({"timestamp": 1700000000 + i, "price": round(price, 4)})

    result = hz.backtest(
        data=data,
        pipeline=[model, quoter],
    )

    m = result.metrics
    print(f"  Total Return:     ${m.total_return:.2f}")
    print(f"  Total Return %:   {m.total_return_pct:.2f}%")
    print(f"  CAGR:             {m.cagr:.2f}")
    print(f"  Sharpe Ratio:     {m.sharpe_ratio:.3f}")
    print(f"  Sortino Ratio:    {m.sortino_ratio:.3f}")
    print(f"  Calmar Ratio:     {m.calmar_ratio:.3f}")
    print(f"  Max Drawdown:     ${m.max_drawdown:.2f}")
    print(f"  Max Drawdown %:   {m.max_drawdown_pct:.2f}%")
    print(f"  Max DD Duration:  {m.max_drawdown_duration_secs:.0f}s")
    print(f"  Total Trades:     {m.total_trades}")
    print(f"  Win Rate:         {m.win_rate:.2f}%")
    print(f"  Profit Factor:    {m.profit_factor:.2f}")
    print(f"  Expectancy:       ${m.expectancy:.4f}")
    print(f"  Avg Win:          ${m.avg_win:.4f}")
    print(f"  Avg Loss:         ${m.avg_loss:.4f}")
    print(f"  Largest Win:      ${m.largest_win:.4f}")
    print(f"  Largest Loss:     ${m.largest_loss:.4f}")
    print(f"  Total Fees:       ${m.total_fees:.4f}")


def test_bt_result_summary():
    """backtesting.mdx - result.summary()"""
    import horizon as hz
    from horizon import Side, OrderSide, OrderRequest

    def model(ctx):
        return {"fair": ctx.feed.price * 1.02}

    def quoter(ctx, fair):
        if fair > ctx.feed.price:
            return OrderRequest(
                market_id=ctx.market_id,
                side=Side.Yes,
                order_side=OrderSide.Buy,
                price=ctx.feed.price,
                size=10.0,
            )

    data = [
        {"timestamp": 1000 + i, "price": 0.50 + (i % 5) * 0.02}
        for i in range(50)
    ]

    result = hz.backtest(data=data, pipeline=[model, quoter])
    output = result.summary()
    assert isinstance(output, str)
    print(f"  summary() returned {len(output)} chars")


def test_bt_result_pnl_by_market():
    """backtesting.mdx - result.pnl_by_market()"""
    import horizon as hz
    from horizon import Side, OrderSide, OrderRequest

    def model(ctx):
        return {"fair": 0.60}

    def quoter(ctx, fair):
        if fair > ctx.feed.price + 0.03:
            return OrderRequest(
                market_id=ctx.market_id,
                side=Side.Yes,
                order_side=OrderSide.Buy,
                price=ctx.feed.price,
                size=10.0,
            )

    data = [
        {"timestamp": 1000 + i, "price": 0.50 + (i % 5) * 0.02}
        for i in range(50)
    ]

    result = hz.backtest(data=data, pipeline=[model, quoter])
    pnl = result.pnl_by_market()
    for market, realized in pnl.items():
        print(f"  {market}: ${realized:.2f}")


def test_bt_result_equity_curve():
    """backtesting.mdx - result.equity_curve"""
    import horizon as hz
    from horizon import Side, OrderSide, OrderRequest

    def model(ctx):
        return {"fair": ctx.feed.price * 1.02}

    def quoter(ctx, fair):
        if fair > ctx.feed.price:
            return OrderRequest(
                market_id=ctx.market_id,
                side=Side.Yes,
                order_side=OrderSide.Buy,
                price=ctx.feed.price,
                size=10.0,
            )

    data = [
        {"timestamp": 1000 + i, "price": 0.50 + (i % 5) * 0.02}
        for i in range(50)
    ]

    result = hz.backtest(data=data, pipeline=[model, quoter])
    curve = result.equity_curve
    for ts, equity in curve[:5]:
        print(f"  t={ts}: ${equity:.2f}")


def test_bt_result_trades():
    """backtesting.mdx - result.trades"""
    import horizon as hz
    from horizon import Side, OrderSide, OrderRequest

    def model(ctx):
        return {"fair": ctx.feed.price * 1.02}

    def quoter(ctx, fair):
        if fair > ctx.feed.price:
            return OrderRequest(
                market_id=ctx.market_id,
                side=Side.Yes,
                order_side=OrderSide.Buy,
                price=ctx.feed.price,
                size=10.0,
            )

    data = [
        {"timestamp": 1000 + i, "price": 0.50 + (i % 5) * 0.02}
        for i in range(50)
    ]

    result = hz.backtest(data=data, pipeline=[model, quoter])
    for fill in result.trades[:5]:
        print(f"  {fill.side} {fill.order_side} {fill.size} @ {fill.price}")


def test_bt_result_to_csv():
    """backtesting.mdx - result.to_csv()"""
    import horizon as hz
    import tempfile
    from horizon import Side, OrderSide, OrderRequest

    def model(ctx):
        return {"fair": ctx.feed.price * 1.02}

    def quoter(ctx, fair):
        if fair > ctx.feed.price:
            return OrderRequest(
                market_id=ctx.market_id,
                side=Side.Yes,
                order_side=OrderSide.Buy,
                price=ctx.feed.price,
                size=10.0,
            )

    data = [
        {"timestamp": 1000 + i, "price": 0.50 + (i % 5) * 0.02}
        for i in range(50)
    ]

    result = hz.backtest(data=data, pipeline=[model, quoter])

    with tempfile.NamedTemporaryFile(suffix=".csv", delete=False) as f:
        eq_path = f.name
    with tempfile.NamedTemporaryFile(suffix=".csv", delete=False) as f:
        tr_path = f.name

    try:
        result.to_csv(eq_path, what="equity")
        result.to_csv(tr_path, what="trades")
        eq_size = os.path.getsize(eq_path)
        tr_size = os.path.getsize(tr_path)
        print(f"  equity CSV: {eq_size} bytes, trades CSV: {tr_size} bytes")
    finally:
        os.unlink(eq_path)
        os.unlink(tr_path)


def test_bt_basic_backtest_example():
    """backtesting.mdx - Examples: Basic Backtest"""
    import horizon as hz
    from horizon import Side, OrderSide, OrderRequest

    def mean_reversion(ctx):
        price = ctx.feed.price
        fair = 0.50
        edge = fair - price
        return {"fair": fair, "edge": edge}

    def quoter(ctx, fair, edge):
        if abs(edge) < 0.03:
            return None

        if edge > 0:
            return OrderRequest(
                market_id=ctx.market_id,
                side=Side.Yes,
                order_side=OrderSide.Buy,
                price=ctx.feed.price,
                size=10.0,
            )
        else:
            return OrderRequest(
                market_id=ctx.market_id,
                side=Side.Yes,
                order_side=OrderSide.Sell,
                price=ctx.feed.price,
                size=10.0,
            )

    import random
    random.seed(42)
    price = 0.50
    data = []
    for i in range(1000):
        price += random.gauss(0, 0.01)
        price = max(0.01, min(0.99, price))
        data.append({"timestamp": 1700000000 + i, "price": round(price, 4)})

    result = hz.backtest(
        name="mean-reversion",
        markets=["test-market"],
        data=data,
        pipeline=[mean_reversion, quoter],
        initial_capital=1000.0,
        paper_fee_rate=0.002,
    )

    print(f"  {result.summary()[:100]}...")


def test_bt_multi_feed_backtest():
    """backtesting.mdx - Examples: Multi-Feed Backtest"""
    import horizon as hz
    from horizon import Side, OrderSide, OrderRequest

    def cross_market_model(ctx):
        btc_price = ctx.feeds["binance"].price
        market_price = ctx.feed.price

        if btc_price > 100000:
            fair = 0.75
        else:
            fair = 0.35

        return {"fair": fair}

    def quoter(ctx, fair):
        edge = fair - ctx.feed.price
        if edge > 0.05:
            return OrderRequest(
                market_id=ctx.market_id,
                side=Side.Yes,
                order_side=OrderSide.Buy,
                price=ctx.feed.price,
                size=20.0,
            )
        elif edge < -0.05:
            return OrderRequest(
                market_id=ctx.market_id,
                side=Side.Yes,
                order_side=OrderSide.Sell,
                price=ctx.feed.price,
                size=20.0,
            )

    data = {
        "polymarket_book": [
            {"timestamp": t, "price": 0.50 + (t % 10) * 0.02, "bid": 0.49, "ask": 0.53}
            for t in range(1700000000, 1700000500)
        ],
        "binance": [
            {"timestamp": t, "price": 99000 + (t % 20) * 100}
            for t in range(1700000000, 1700000500)
        ],
    }

    result = hz.backtest(
        name="cross-market",
        markets=["btc-above-100k"],
        data=data,
        feeds={"btc-above-100k": "polymarket_book"},
        pipeline=[cross_market_model, quoter],
        initial_capital=5000.0,
    )

    print(f"  {result.summary()[:80]}...")
    print("  PnL by market:")
    for market, pnl in result.pnl_by_market().items():
        print(f"    {market}: ${pnl:.2f}")


def test_bt_brier_score_with_outcomes():
    """backtesting.mdx - Examples: Brier Score with Outcomes"""
    import horizon as hz
    from horizon import Side, OrderSide, OrderRequest

    def probability_model(ctx):
        return {"fair": 0.65}

    def quoter(ctx, fair):
        if fair > ctx.feed.price + 0.03:
            return OrderRequest(
                market_id=ctx.market_id,
                side=Side.Yes,
                order_side=OrderSide.Buy,
                price=ctx.feed.price,
                size=10.0,
            )

    data = [
        {"timestamp": t, "price": 0.55 + (t % 5) * 0.01}
        for t in range(1700000000, 1700000200)
    ]

    result = hz.backtest(
        name="calibration-test",
        markets=["will-it-rain"],
        data=data,
        pipeline=[probability_model, quoter],
        outcomes={"will-it-rain": 1.0},
    )

    m = result.metrics
    if m.brier_score is not None:
        print(f"  Brier Score: {m.brier_score:.4f}")
    if m.avg_edge is not None:
        print(f"  Avg Edge:    {m.avg_edge:.4f}")
    print(f"  {result.summary()[:80]}...")


def test_bt_with_risk_config():
    """backtesting.mdx - Examples: With Risk Configuration"""
    import horizon as hz
    from horizon import Side, OrderSide, OrderRequest, RiskConfig

    risk = RiskConfig(
        max_position_size=100.0,
        max_order_size=20.0,
        max_notional=5000.0,
        max_drawdown_pct=0.10,
        max_orders_per_second=5,
    )

    def model(ctx):
        return {"fair": 0.60}

    def aggressive_quoter(ctx, fair):
        return OrderRequest(
            market_id=ctx.market_id,
            side=Side.Yes,
            order_side=OrderSide.Buy,
            price=ctx.feed.price,
            size=50.0,
        )

    data = [
        {"timestamp": t, "price": 0.50 + (t % 10) * 0.005}
        for t in range(1700000000, 1700001000)
    ]

    result = hz.backtest(
        name="risk-limited",
        markets=["test-market"],
        data=data,
        pipeline=[model, aggressive_quoter],
        risk=risk,
        initial_capital=2000.0,
    )

    print(f"  {result.summary()[:80]}...")


# ===================================================================
# EXECUTION ALGOS (docs/execution-algos.mdx)
# ===================================================================

def test_exec_import_base_class():
    """execution-algos.mdx - ExecAlgo base class import"""
    from horizon.algos import ExecAlgo
    assert ExecAlgo is not None


def test_exec_twap_import():
    """execution-algos.mdx - TWAP import"""
    from horizon.algos import TWAP
    assert TWAP is not None


def test_exec_twap_full_example():
    """execution-algos.mdx - TWAP Full Example (adapted: no hz.engine())"""
    import horizon as hz
    from horizon import Side, OrderSide, OrderRequest
    from horizon.algos import TWAP

    # The docs use hz.engine(...) which doesn't exist; use Engine() directly
    engine = hz.Engine(risk_config=hz.RiskConfig())

    twap = TWAP(engine, duration_secs=300, num_slices=10)

    request = OrderRequest(
        market_id="election-winner",
        side=Side.Yes,
        order_side=OrderSide.Buy,
        price=0.55,
        size=500.0,
    )

    twap.start(request)

    # Simulate just 1 tick instead of real-time loop
    current_time = time.time()
    current_price = 0.55

    twap.on_tick(current_price, current_time)

    print(f"  Filled: {twap.total_filled} / 500.0")
    print(f"  Child orders: {len(twap.child_order_ids)}")
    print(f"  Is complete: {twap.is_complete}")


def test_exec_twap_full_example_as_documented():
    """execution-algos.mdx - TWAP Full Example (exact hz.engine() call)"""
    import horizon as hz

    # Test exact documented call: hz.engine(markets=[...], exchange="paper")
    engine = hz.engine(
        markets=["election-winner"],
        exchange="paper",
    )


def test_exec_vwap_import():
    """execution-algos.mdx - VWAP import"""
    from horizon.algos import VWAP
    assert VWAP is not None


def test_exec_vwap_full_example():
    """execution-algos.mdx - VWAP Full Example (adapted: no hz.engine())"""
    import horizon as hz
    from horizon import Side, OrderSide, OrderRequest
    from horizon.algos import VWAP

    engine = hz.Engine(risk_config=hz.RiskConfig())

    volume_profile = [3.0, 2.0, 1.0, 1.0, 2.0, 4.0]

    vwap = VWAP(engine, duration_secs=600, volume_profile=volume_profile)

    request = OrderRequest(
        market_id="fed-rate-decision",
        side=Side.Yes,
        order_side=OrderSide.Buy,
        price=0.62,
        size=1000.0,
    )

    vwap.start(request)

    # Simulate 1 tick
    vwap.on_tick(0.62, time.time())

    print(f"  Filled: {vwap.total_filled}")
    print(f"  Is complete: {vwap.is_complete}")
    print(f"  Child orders: {len(vwap.child_order_ids)}")


def test_exec_vwap_full_example_as_documented():
    """execution-algos.mdx - VWAP Full Example (exact hz.engine() call)"""
    import horizon as hz

    engine = hz.engine(
        markets=["fed-rate-decision"],
        exchange="paper",
    )


def test_exec_iceberg_import():
    """execution-algos.mdx - Iceberg import"""
    from horizon.algos import Iceberg
    assert Iceberg is not None


def test_exec_iceberg_full_example():
    """execution-algos.mdx - Iceberg Full Example (adapted: no hz.engine())"""
    import horizon as hz
    from horizon import Side, OrderSide, OrderRequest
    from horizon.algos import Iceberg

    engine = hz.Engine(risk_config=hz.RiskConfig())

    iceberg = Iceberg(engine, show_size=15.0)

    request = OrderRequest(
        market_id="btc-above-100k",
        side=Side.Yes,
        order_side=OrderSide.Buy,
        price=0.70,
        size=200.0,
    )

    iceberg.start(request)

    # Simulate 1 tick
    iceberg.on_tick(0.70, time.time())

    print(f"  Filled: {iceberg.total_filled} / 200.0")
    print(f"  Visible orders placed: {len(iceberg.child_order_ids)}")
    print(f"  Is complete: {iceberg.is_complete}")


def test_exec_iceberg_full_example_as_documented():
    """execution-algos.mdx - Iceberg Full Example (exact hz.engine() call)"""
    import horizon as hz

    engine = hz.engine(
        markets=["btc-above-100k"],
        exchange="paper",
    )


def test_exec_algo_in_hz_run_import():
    """execution-algos.mdx - Using Algos in hz.run() (import check only)"""
    import horizon as hz
    from horizon import Side, OrderSide, OrderRequest
    from horizon.algos import TWAP

    # The docs show a full hz.run() loop which would block -- just test imports
    active_algo = None

    def model(ctx):
        fair_value = 0.60
        return {"fair": fair_value}

    def algo_manager(ctx, engine, fair):
        nonlocal active_algo

        if active_algo is None or active_algo.is_complete:
            positions = engine.positions()
            current_pos = positions.get(ctx.market_id)
            current_size = current_pos.net_size if current_pos else 0.0

            if current_size < 100.0:
                request = OrderRequest(
                    market_id=ctx.market_id,
                    side=Side.Yes,
                    order_side=OrderSide.Buy,
                    price=fair,
                    size=100.0 - current_size,
                )
                active_algo = TWAP(engine, duration_secs=120, num_slices=6)
                active_algo.start(request)

        if active_algo and not active_algo.is_complete:
            active_algo.on_tick(ctx.feed.price, time.time())

    # Don't actually call hz.run() since it blocks; just confirm the functions are valid
    assert callable(model)
    assert callable(algo_manager)
    print("  Pipeline functions defined successfully (hz.run() not invoked)")


# ===================================================================
# BACKTESTING - additional snippet tests for RiskConfig field names
# ===================================================================

def test_bt_risk_config_field_names():
    """backtesting.mdx - RiskConfig constructor field names as documented"""
    import horizon as hz

    # The docs use: max_position_size, max_order_size, max_notional, max_drawdown_pct, max_orders_per_second
    # Check which ones actually exist
    risk = hz.RiskConfig(
        max_position_size=100.0,
        max_order_size=20.0,
        max_notional=5000.0,
        max_drawdown_pct=0.10,
        max_orders_per_second=5,
    )
    assert risk is not None


# ===================================================================
# MAIN
# ===================================================================

if __name__ == "__main__":
    print("=" * 70)
    print("Testing code snippets from docs/simulation.mdx, docs/backtesting.mdx,")
    print("and docs/execution-algos.mdx")
    print("=" * 70)
    print()

    # --- simulation.mdx ---
    print("--- docs/simulation.mdx ---")
    run_test("sim_quick_start", test_sim_quick_start)
    run_test("sim_position_constructor", test_sim_position_constructor)
    run_test("sim_monte_carlo_result_table", test_sim_monte_carlo_result_table)
    run_test("sim_monte_carlo_direct", test_sim_monte_carlo_direct)
    run_test("sim_simulate_wrapper", test_sim_simulate_wrapper)
    run_test("sim_correlation_dict", test_sim_correlation_dict)
    run_test("sim_portfolio_risk_assessment", test_sim_portfolio_risk_assessment)
    run_test("sim_from_live_engine", test_sim_from_live_engine)
    run_test("sim_correlated_vs_uncorrelated", test_sim_correlated_vs_uncorrelated)
    run_test("sim_seed_determinism", test_sim_seed_determinism)
    print()

    # --- docs/backtesting.mdx ---
    print("--- docs/backtesting.mdx ---")
    run_test("bt_quick_start", test_bt_quick_start)
    run_test("bt_data_list_of_dicts", test_bt_data_list_of_dicts)
    run_test("bt_data_pandas_dataframe", test_bt_data_pandas_dataframe)
    run_test("bt_data_multi_feed_dict", test_bt_data_multi_feed_dict)
    run_test("bt_l2_book_data", test_bt_l2_book_data)
    run_test("bt_fill_model_deterministic", test_bt_fill_model_deterministic)
    run_test("bt_fill_model_probabilistic", test_bt_fill_model_probabilistic)
    run_test("bt_fill_model_glft", test_bt_fill_model_glft)
    run_test("bt_market_impact", test_bt_market_impact)
    run_test("bt_latency_simulation", test_bt_latency_simulation)
    run_test("bt_calibration_curve", test_bt_calibration_curve)
    run_test("bt_log_loss", test_bt_log_loss)
    run_test("bt_edge_decay", test_bt_edge_decay)
    run_test("bt_walk_forward", test_bt_walk_forward)
    run_test("bt_result_metrics", test_bt_result_metrics)
    run_test("bt_result_summary", test_bt_result_summary)
    run_test("bt_result_pnl_by_market", test_bt_result_pnl_by_market)
    run_test("bt_result_equity_curve", test_bt_result_equity_curve)
    run_test("bt_result_trades", test_bt_result_trades)
    run_test("bt_result_to_csv", test_bt_result_to_csv)
    run_test("bt_basic_backtest_example", test_bt_basic_backtest_example)
    run_test("bt_multi_feed_backtest", test_bt_multi_feed_backtest)
    run_test("bt_brier_score_with_outcomes", test_bt_brier_score_with_outcomes)
    run_test("bt_with_risk_config", test_bt_with_risk_config)
    run_test("bt_risk_config_field_names", test_bt_risk_config_field_names)
    print()

    # --- docs/execution-algos.mdx ---
    print("--- docs/execution-algos.mdx ---")
    run_test("exec_import_base_class", test_exec_import_base_class)
    run_test("exec_twap_import", test_exec_twap_import)
    run_test("exec_twap_full_example", test_exec_twap_full_example)
    run_test("exec_twap_full_example_as_documented", test_exec_twap_full_example_as_documented)
    run_test("exec_vwap_import", test_exec_vwap_import)
    run_test("exec_vwap_full_example", test_exec_vwap_full_example)
    run_test("exec_vwap_full_example_as_documented", test_exec_vwap_full_example_as_documented)
    run_test("exec_iceberg_import", test_exec_iceberg_import)
    run_test("exec_iceberg_full_example", test_exec_iceberg_full_example)
    run_test("exec_iceberg_full_example_as_documented", test_exec_iceberg_full_example_as_documented)
    run_test("exec_algo_in_hz_run_import", test_exec_algo_in_hz_run_import)
    print()

    # --- Summary ---
    print("=" * 70)
    total = len(results)
    passed = sum(1 for _, ok, _ in results if ok)
    failed = sum(1 for _, ok, _ in results if not ok)
    print(f"TOTAL: {total}  |  PASSED: {passed}  |  FAILED: {failed}")
    print("=" * 70)

    if failed > 0:
        print("\nFailed tests:")
        for name, ok, err in results:
            if not ok:
                print(f"  - {name}: {err}")

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