#!/usr/bin/env python3
"""Read-only market data examples. Python 3.10+, standard library only.
Run: python3 market_data.py polymarket|kalshi|kalshi-history
No credentials, wallet access, order creation or account endpoints.
"""
import argparse
import json
import sys
from decimal import Decimal, InvalidOperation
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen

ENDPOINTS = {
    'polymarket': 'https://gamma-api.polymarket.com/markets?limit=5&active=true&closed=false&order=volume24hr&ascending=false',
    'kalshi': 'https://external-api.kalshi.com/trade-api/v2/markets?limit=5&status=open&series_ticker=KXHIGHNY',
    'kalshi-history': 'https://external-api.kalshi.com/trade-api/v2/historical/cutoff',
}

def read_json(url):
    request = Request(url, headers={'Accept': 'application/json', 'User-Agent': 'PMTools-read-only-example/1.0'})
    try:
        with urlopen(request, timeout=20) as response:
            return json.load(response)
    except HTTPError as error:
        if error.code == 429:
            raise RuntimeError('Rate limited. Wait for Retry-After: ' + str(error.headers.get('Retry-After', 'unspecified')) + '; do not retry in a tight loop.') from error
        raise RuntimeError(f'HTTP {error.code}; check the endpoint and current venue documentation.') from error
    except (URLError, TimeoutError, json.JSONDecodeError) as error:
        raise RuntimeError(f'Network or response error: {error}') from error


def array(value):
    return json.loads(value) if isinstance(value, str) else value


def main(venue):
    data = read_json(ENDPOINTS[venue])
    if venue == 'kalshi-history':
        if not isinstance(data, dict):
            raise RuntimeError('Unexpected cutoff response.')
        print(json.dumps(data, indent=2))
        print('Use the cutoff for the required data type, then query the matching /historical endpoint.')
        return
    rows = data if venue == 'polymarket' else data.get('markets', [])
    if not isinstance(rows, list):
        raise RuntimeError('Unexpected market-list response; check for a schema change.')
    if not rows:
        print('No matching markets. An empty set is not a price of zero.')
    for market in rows:
        if venue == 'polymarket':
            outcomes, prices = array(market.get('outcomes', [])), array(market.get('outcomePrices', []))
            if len(outcomes) != len(prices):
                raise RuntimeError('Outcome labels and prices have different lengths.')
            print(market['id'], market['question'])
            for label, price in zip(outcomes, prices):
                print(f'  {label}: {Decimal(str(price)) * 100}% quoted probability')
            print('  Updated:', market.get('updatedAt', 'unknown'))
        else:
            print(market['ticker'], market['title'])
            # _dollars values are decimal strings, not legacy integer cents.
            bid, ask = market.get('yes_bid_dollars'), market.get('yes_ask_dollars')
            print('  YES bid/ask ($):', bid, '/', ask)
            print('  A zero/missing side can mean no quote; inspect the order book.')
    if venue == 'kalshi' and data.get('cursor'):
        print('More results available: pass the returned cursor in the next request.')
    print('Snapshot only. Quotes are not guaranteed fills.')

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('venue', choices=ENDPOINTS)
    args = parser.parse_args()
    try:
        main(args.venue)
    except (RuntimeError, KeyError, TypeError, InvalidOperation, json.JSONDecodeError) as error:
        print(str(error), file=sys.stderr)
        sys.exit(1)
