"""
Test script for all Python code snippets in docs/discovery.mdx.

Each snippet is wrapped in a function with try/except to report PASS/FAIL.
Live API calls are attempted but failures due to network/API issues are reported.
"""

import sys
import traceback

results = []


def report(name, passed, detail=""):
    status = "PASS" if passed else "FAIL"
    results.append((name, passed, detail))
    print(f"  [{status}] {name}")
    if detail:
        for line in detail.strip().splitlines():
            print(f"         {line}")


# ============================================================================
# Snippet 1: hz.discover_markets (Polymarket election search)
# Source: docs/discovery.mdx lines 38-56
# ============================================================================
def test_snippet_1():
    """hz.discover_markets - Polymarket election search"""
    try:
        import horizon as hz

        # Search Polymarket for election markets
        markets = hz.discover_markets(
            exchange="polymarket",
            query="election",
            active=True,
            limit=10,
            min_volume=10000.0,
            sort_by="volume",
        )

        # Verify return type
        assert isinstance(markets, list), f"Expected list, got {type(markets)}"

        if len(markets) == 0:
            report("Snippet 1: discover_markets(polymarket, election)", True,
                   "Returned empty list (API may have no matching markets or network issue)")
            return

        for m in markets:
            # Test the attribute access shown in the docs
            _ = m.name
            _ = m.id
            _ = m.yes_token_id
            _ = m.no_token_id
            _ = m.condition_id

        # Print like the docs do
        m = markets[0]
        detail = (f"Found {len(markets)} markets. First: {m.name}\n"
                  f"  ID: {m.id}\n"
                  f"  Token IDs: yes={m.yes_token_id}, no={m.no_token_id}\n"
                  f"  Condition: {m.condition_id}")
        report("Snippet 1: discover_markets(polymarket, election)", True, detail)

    except Exception as e:
        report("Snippet 1: discover_markets(polymarket, election)", False,
               f"{type(e).__name__}: {e}\n{traceback.format_exc()}")


# ============================================================================
# Snippet 2: hz.top_markets (Polymarket + Kalshi)
# Source: docs/discovery.mdx lines 103-109
# ============================================================================
def test_snippet_2a():
    """hz.top_markets - Polymarket top 10"""
    try:
        import horizon as hz

        # Top 10 Polymarket markets by volume
        markets = hz.top_markets(limit=10)

        assert isinstance(markets, list), f"Expected list, got {type(markets)}"
        detail = f"Returned {len(markets)} markets"
        if markets:
            detail += f". First: {markets[0].name}"
        report("Snippet 2a: top_markets(polymarket, limit=10)", True, detail)

    except Exception as e:
        report("Snippet 2a: top_markets(polymarket, limit=10)", False,
               f"{type(e).__name__}: {e}\n{traceback.format_exc()}")


def test_snippet_2b():
    """hz.top_markets - Kalshi crypto category"""
    try:
        import horizon as hz

        # Top Kalshi markets in crypto category
        markets = hz.top_markets(exchange="kalshi", category="KXBTC", limit=5)

        assert isinstance(markets, list), f"Expected list, got {type(markets)}"
        detail = f"Returned {len(markets)} markets"
        if markets:
            detail += f". First: {markets[0].name} (ticker={markets[0].ticker})"
        report("Snippet 2b: top_markets(kalshi, KXBTC, limit=5)", True, detail)

    except Exception as e:
        report("Snippet 2b: top_markets(kalshi, KXBTC, limit=5)", False,
               f"{type(e).__name__}: {e}\n{traceback.format_exc()}")


# ============================================================================
# Snippet 3: hz.discover_events (Polymarket election events)
# Source: docs/discovery.mdx lines 126-140
# ============================================================================
def test_snippet_3():
    """hz.discover_events - election events"""
    try:
        import horizon as hz

        events = hz.discover_events(
            query="election",
            active=True,
            limit=5,
            min_volume=50000.0,
        )

        assert isinstance(events, list), f"Expected list, got {type(events)}"

        if len(events) == 0:
            report("Snippet 3: discover_events(election)", True,
                   "Returned empty list (API may have no matching multi-outcome events)")
            return

        detail_lines = []
        for event in events:
            # Test attribute access as shown in docs
            count = event.outcome_count()
            detail_lines.append(f"{event.name} ({count} outcomes)")
            for outcome in event.outcomes:
                detail_lines.append(f"  {outcome.name}: {outcome.yes_price:.2f}")

            # Test to_markets() conversion
            markets = event.to_markets()
            assert isinstance(markets, list), "to_markets() should return list"
            detail_lines.append(f"  -> to_markets() returned {len(markets)} Market objects")

        report("Snippet 3: discover_events(election)", True, "\n".join(detail_lines[:20]))

    except Exception as e:
        report("Snippet 3: discover_events(election)", False,
               f"{type(e).__name__}: {e}\n{traceback.format_exc()}")


# ============================================================================
# Snippet 4: Using discovered markets with hz.run
# Source: docs/discovery.mdx lines 161-181
# NOTE: We test discovery + function definitions but skip hz.run() (needs live feeds)
# ============================================================================
def test_snippet_4():
    """Using discovered markets with hz.run - discovery + function defs"""
    try:
        import horizon as hz

        # Discover a market
        markets = hz.discover_markets(query="bitcoin", limit=1)

        assert isinstance(markets, list), f"Expected list, got {type(markets)}"

        if len(markets) == 0:
            report("Snippet 4: discover_markets + hz.run pattern", True,
                   "Discovery returned empty list; cannot test hz.run pattern but discovery call works")
            return

        market = markets[0]

        # Test that the function definitions from docs are valid
        def fair_value(ctx):
            return ctx.feeds["book"].price or 0.5

        def quoter(ctx, fair):
            return hz.quotes(fair, spread=0.04, size=5)

        # Verify hz.quotes works
        quotes = hz.quotes(0.5, spread=0.04, size=5)
        assert isinstance(quotes, list), f"quotes() should return list, got {type(quotes)}"
        assert len(quotes) > 0, "quotes() should return non-empty list"

        # Verify PolymarketBook can be instantiated
        feed = hz.PolymarketBook(market.id)
        assert feed is not None

        # Verify Risk can be instantiated
        risk = hz.Risk(max_position=100)
        assert risk is not None

        # NOTE: We skip actually calling hz.run() as it requires live WebSocket feeds
        detail = (f"Market: {market.name} (id={market.id})\n"
                  f"quotes(0.5, 0.04, 5) returned {len(quotes)} Quote(s)\n"
                  f"PolymarketBook and Risk instantiation OK\n"
                  f"(hz.run() skipped - requires live feeds)")
        report("Snippet 4: discover_markets + hz.run pattern", True, detail)

    except Exception as e:
        report("Snippet 4: discover_markets + hz.run pattern", False,
               f"{type(e).__name__}: {e}\n{traceback.format_exc()}")


# ============================================================================
# Snippet 5: Using discovered events with hz.run
# Source: docs/discovery.mdx lines 185-202
# NOTE: We test discovery + event.to_markets() but skip hz.run()
# ============================================================================
def test_snippet_5():
    """Using discovered events with hz.run - event discovery + to_markets"""
    try:
        import horizon as hz

        events = hz.discover_events(query="bitcoin", limit=1)

        assert isinstance(events, list), f"Expected list, got {type(events)}"

        if len(events) == 0:
            report("Snippet 5: discover_events + hz.run pattern", True,
                   "No events found for 'bitcoin'; discovery call works but no multi-outcome events available")
            return

        event = events[0]

        # Register the event and trade all outcomes
        markets = event.to_markets()
        market_ids = [m.id for m in markets]

        assert isinstance(markets, list), "to_markets() should return list"
        assert len(market_ids) == len(markets), "IDs should match markets count"

        # Verify feed dict comprehension from docs works
        feeds = {m.id: hz.PolymarketBook(m.id) for m in markets}
        assert isinstance(feeds, dict)

        detail = (f"Event: {event.name} ({event.outcome_count()} outcomes)\n"
                  f"to_markets() returned {len(markets)} Market objects\n"
                  f"Feed dict has {len(feeds)} entries\n"
                  f"(hz.run() skipped - requires live feeds)")
        report("Snippet 5: discover_events + hz.run pattern", True, detail)

    except Exception as e:
        report("Snippet 5: discover_events + hz.run pattern", False,
               f"{type(e).__name__}: {e}\n{traceback.format_exc()}")


# ============================================================================
# Snippet 6: Scanning for High-Volume Opportunities
# Source: docs/discovery.mdx lines 211-224
# ============================================================================
def test_snippet_6():
    """Scanning for High-Volume Opportunities - both exchanges"""
    try:
        import horizon as hz

        # Find liquid markets on both exchanges
        poly_markets = hz.top_markets("polymarket", limit=20)
        kalshi_markets = hz.top_markets("kalshi", limit=20)

        assert isinstance(poly_markets, list), f"Expected list, got {type(poly_markets)}"
        assert isinstance(kalshi_markets, list), f"Expected list, got {type(kalshi_markets)}"

        detail_lines = [f"Polymarket: {len(poly_markets)} markets, Kalshi: {len(kalshi_markets)} markets"]

        detail_lines.append("=== Polymarket (first 3) ===")
        for m in poly_markets[:3]:
            # Docs use m.name[:60] and m.id
            detail_lines.append(f"  {m.name[:60]} ({m.id})")

        detail_lines.append("=== Kalshi (first 3) ===")
        for m in kalshi_markets[:3]:
            # Docs use m.name[:60] and m.ticker
            detail_lines.append(f"  {m.name[:60]} ({m.ticker})")

        report("Snippet 6: Scanning high-volume (both exchanges)", True,
               "\n".join(detail_lines))

    except Exception as e:
        report("Snippet 6: Scanning high-volume (both exchanges)", False,
               f"{type(e).__name__}: {e}\n{traceback.format_exc()}")


# ============================================================================
# Snippet 7: Category-Based Discovery
# Source: docs/discovery.mdx lines 229-245
# ============================================================================
def test_snippet_7a():
    """Category-Based Discovery - Polymarket crypto"""
    try:
        import horizon as hz

        # Polymarket categories: politics, crypto, sports, etc.
        crypto_markets = hz.discover_markets(
            exchange="polymarket",
            category="crypto",
            min_volume=5000,
            limit=10,
        )

        assert isinstance(crypto_markets, list), f"Expected list, got {type(crypto_markets)}"
        detail = f"Returned {len(crypto_markets)} markets"
        if crypto_markets:
            detail += f". First: {crypto_markets[0].name}"
        report("Snippet 7a: discover_markets(polymarket, category=crypto)", True, detail)

    except Exception as e:
        report("Snippet 7a: discover_markets(polymarket, category=crypto)", False,
               f"{type(e).__name__}: {e}\n{traceback.format_exc()}")


def test_snippet_7b():
    """Category-Based Discovery - Kalshi KXBTC series"""
    try:
        import horizon as hz

        # Kalshi series: KXBTC, KXETH, etc.
        btc_markets = hz.discover_markets(
            exchange="kalshi",
            category="KXBTC",
            limit=10,
        )

        assert isinstance(btc_markets, list), f"Expected list, got {type(btc_markets)}"
        detail = f"Returned {len(btc_markets)} markets"
        if btc_markets:
            detail += f". First: {btc_markets[0].name} (ticker={btc_markets[0].ticker})"
        report("Snippet 7b: discover_markets(kalshi, category=KXBTC)", True, detail)

    except Exception as e:
        report("Snippet 7b: discover_markets(kalshi, category=KXBTC)", False,
               f"{type(e).__name__}: {e}\n{traceback.format_exc()}")


# ============================================================================
# Snippet 8: Finding Cross-Exchange Pairs
# Source: docs/discovery.mdx lines 249-266
# ============================================================================
def test_snippet_8():
    """Finding Cross-Exchange Pairs"""
    try:
        import horizon as hz

        # Find matching markets on both exchanges
        poly = hz.discover_markets("polymarket", query="bitcoin", limit=5)
        kalshi = hz.discover_markets("kalshi", query="KXBTC", limit=5)

        assert isinstance(poly, list), f"Expected list for poly, got {type(poly)}"
        assert isinstance(kalshi, list), f"Expected list for kalshi, got {type(kalshi)}"

        detail_lines = []
        detail_lines.append("Polymarket:")
        for m in poly[:3]:
            detail_lines.append(f"  {m.id}: {m.name}")

        detail_lines.append("Kalshi:")
        for m in kalshi[:3]:
            detail_lines.append(f"  {m.ticker}: {m.name}")

        report("Snippet 8: Cross-exchange pairs (bitcoin)", True,
               "\n".join(detail_lines))

    except Exception as e:
        report("Snippet 8: Cross-exchange pairs (bitcoin)", False,
               f"{type(e).__name__}: {e}\n{traceback.format_exc()}")


# ============================================================================
# Snippet 9: Event Parity Check
# Source: docs/discovery.mdx lines 270-283
# ============================================================================
def test_snippet_9():
    """Event Parity Check"""
    try:
        import horizon as hz

        events = hz.discover_events(limit=5, min_volume=100000)

        assert isinstance(events, list), f"Expected list, got {type(events)}"

        if len(events) == 0:
            report("Snippet 9: Event parity check", True,
                   "No events found with min_volume=100000; discovery works but no data")
            return

        detail_lines = []
        for event in events:
            prices = [o.yes_price for o in event.outcomes]
            total = sum(prices)
            detail_lines.append(f"{event.name}")
            detail_lines.append(f"  Outcomes: {event.outcome_count()}")
            detail_lines.append(f"  Price sum: {total:.3f} (should be ~1.0)")
            if abs(total - 1.0) > 0.03:
                detail_lines.append(f"  *** Potential arb: {abs(total - 1.0):.3f} overround ***")

        report("Snippet 9: Event parity check", True, "\n".join(detail_lines[:30]))

    except Exception as e:
        report("Snippet 9: Event parity check", False,
               f"{type(e).__name__}: {e}\n{traceback.format_exc()}")


# ============================================================================
# Additional: Test imports referenced in docs
# ============================================================================
def test_imports():
    """Verify all imports used across doc snippets exist"""
    try:
        import horizon as hz

        # Functions used in snippets
        assert callable(hz.discover_markets), "discover_markets should be callable"
        assert callable(hz.top_markets), "top_markets should be callable"
        assert callable(hz.discover_events), "discover_events should be callable"
        assert callable(hz.run), "run should be callable"
        assert callable(hz.quotes), "quotes should be callable"

        # Types used in snippets
        assert hz.PolymarketBook is not None, "PolymarketBook should exist"
        assert hz.Risk is not None, "Risk should exist"
        assert hz.Market is not None, "Market should exist"
        assert hz.Event is not None, "Event should exist"

        report("Imports: all doc-referenced symbols exist", True,
               "discover_markets, top_markets, discover_events, run, quotes, PolymarketBook, Risk, Market, Event")

    except Exception as e:
        report("Imports: all doc-referenced symbols exist", False,
               f"{type(e).__name__}: {e}\n{traceback.format_exc()}")


# ============================================================================
# Additional: Test function signatures match docs
# ============================================================================
def test_signatures():
    """Verify function signatures match documented parameters"""
    try:
        import inspect
        import horizon as hz

        # discover_markets signature
        sig = inspect.signature(hz.discover_markets)
        params = list(sig.parameters.keys())
        expected = ["exchange", "query", "active", "limit", "min_volume", "category", "sort_by"]
        # The function may have extra internal params (gamma_url, kalshi_url), but docs params must be present
        for p in expected:
            assert p in params, f"discover_markets missing param: {p}"

        # top_markets signature
        sig = inspect.signature(hz.top_markets)
        params = list(sig.parameters.keys())
        expected_top = ["exchange", "limit", "category"]
        for p in expected_top:
            assert p in params, f"top_markets missing param: {p}"

        # discover_events signature
        sig = inspect.signature(hz.discover_events)
        params = list(sig.parameters.keys())
        expected_events = ["exchange", "query", "active", "limit", "min_volume"]
        for p in expected_events:
            assert p in params, f"discover_events missing param: {p}"

        report("Signatures: all documented params present", True,
               f"discover_markets: {expected}\ntop_markets: {expected_top}\ndiscover_events: {expected_events}")

    except Exception as e:
        report("Signatures: all documented params present", False,
               f"{type(e).__name__}: {e}\n{traceback.format_exc()}")


# ============================================================================
# Run all tests
# ============================================================================
if __name__ == "__main__":
    print("=" * 70)
    print("Testing all code snippets from docs/discovery.mdx")
    print("=" * 70)
    print()

    print("--- Prerequisite checks ---")
    test_imports()
    test_signatures()
    print()

    print("--- Snippet 1: discover_markets (Polymarket election) ---")
    test_snippet_1()
    print()

    print("--- Snippet 2a: top_markets (Polymarket) ---")
    test_snippet_2a()
    print()

    print("--- Snippet 2b: top_markets (Kalshi KXBTC) ---")
    test_snippet_2b()
    print()

    print("--- Snippet 3: discover_events (election) ---")
    test_snippet_3()
    print()

    print("--- Snippet 4: discover_markets + hz.run pattern ---")
    test_snippet_4()
    print()

    print("--- Snippet 5: discover_events + hz.run pattern ---")
    test_snippet_5()
    print()

    print("--- Snippet 6: Scanning high-volume (both exchanges) ---")
    test_snippet_6()
    print()

    print("--- Snippet 7a: Category discovery (Polymarket crypto) ---")
    test_snippet_7a()
    print()

    print("--- Snippet 7b: Category discovery (Kalshi KXBTC) ---")
    test_snippet_7b()
    print()

    print("--- Snippet 8: Cross-exchange pairs ---")
    test_snippet_8()
    print()

    print("--- Snippet 9: Event parity check ---")
    test_snippet_9()
    print()

    # Summary
    print("=" * 70)
    print("SUMMARY")
    print("=" * 70)
    passed = sum(1 for _, p, _ in results if p)
    failed = sum(1 for _, p, _ in results if not p)
    total = len(results)
    print(f"  Total: {total}  |  Passed: {passed}  |  Failed: {failed}")
    print()
    if failed:
        print("FAILED tests:")
        for name, p, detail in results:
            if not p:
                print(f"  - {name}")
                if detail:
                    for line in detail.strip().splitlines()[:5]:
                        print(f"      {line}")
    else:
        print("All tests passed!")

    sys.exit(1 if failed else 0)
