#!/usr/bin/env python3
"""
Auto-generate a complete Horizon SDK reference from live introspection.

Run after `maturin develop --features skip-auth` to get the real API.
Output is a markdown file suitable as LLM system context.

Usage:
    python tools/generate_sdk_ref.py > sdk_reference.md
    python tools/generate_sdk_ref.py -o tools/sdk_reference.md
"""

import argparse
import dataclasses
import inspect
import sys
import textwrap
from collections import defaultdict
from typing import Any


def get_signature(obj: Any, name: str) -> str | None:
    """Extract signature from any object, with PyO3 fallbacks."""
    # 1. Try standard inspect
    try:
        sig = inspect.signature(obj)
        return f"{name}{sig}"
    except (ValueError, TypeError):
        pass

    # 2. Try __text_signature__ (PyO3 sets this)
    text_sig = getattr(obj, "__text_signature__", None)
    if text_sig:
        return f"{name}{text_sig}"

    # 3. Try __init__.__text_signature__ for classes
    init = getattr(obj, "__init__", None)
    if init:
        text_sig = getattr(init, "__text_signature__", None)
        if text_sig:
            return f"{name}{text_sig}"

    # 4. Try parsing __doc__ for signature line (PyO3 pattern: "name(args)\n--\n\ndoc")
    doc = getattr(obj, "__doc__", None) or ""
    if doc:
        first_line = doc.split("\n")[0].strip()
        if first_line.startswith(name + "(") or first_line.startswith("("):
            if first_line.startswith("("):
                first_line = name + first_line
            return first_line

    # 5. Try __init__.__doc__
    init_doc = getattr(getattr(obj, "__init__", None), "__doc__", None) or ""
    if init_doc:
        first_line = init_doc.split("\n")[0].strip()
        if "(" in first_line:
            return first_line

    return None


def get_docstring(obj: Any) -> str:
    """Extract clean docstring, skipping the signature line PyO3 puts at top."""
    doc = getattr(obj, "__doc__", None) or ""
    lines = doc.strip().split("\n")

    # PyO3 pattern: "name(args)\n--\n\nActual docstring"
    if len(lines) >= 3 and lines[1].strip() == "--":
        return "\n".join(lines[3:]).strip()

    # Skip first line if it looks like a signature
    name = getattr(obj, "__name__", "")
    if lines and (lines[0].startswith(f"{name}(") or lines[0].startswith("(")):
        rest = "\n".join(lines[1:]).strip()
        if rest.startswith("--"):
            rest = rest[2:].strip()
        return rest

    return doc.strip()


def get_class_attrs(obj: Any) -> list[tuple[str, str]]:
    """Get public attributes of a PyO3 class (properties and methods)."""
    attrs = []
    for attr_name in sorted(dir(obj)):
        if attr_name.startswith("_"):
            continue
        try:
            member = getattr(obj, attr_name)
        except Exception:
            continue

        if callable(member):
            sig = get_signature(member, attr_name)
            if sig:
                attrs.append((attr_name, f"method: `{sig}`"))
            else:
                attrs.append((attr_name, "method"))
        elif isinstance(member, property):
            attrs.append((attr_name, "property"))
        else:
            # Could be enum variant or class attribute
            attrs.append((attr_name, f"= `{repr(member)}`"))

    return attrs


def get_dataclass_fields(obj: Any) -> list[tuple[str, str, str]]:
    """Get (name, type_hint, default) for dataclass fields."""
    fields = []
    for f in dataclasses.fields(obj):
        type_str = ""
        if f.type and f.type != inspect.Parameter.empty:
            type_str = str(f.type) if not isinstance(f.type, str) else f.type
            # Clean up type annotations
            type_str = type_str.replace("typing.", "").replace("<class '", "").replace("'>", "")

        default_str = ""
        if f.default is not dataclasses.MISSING:
            default_str = repr(f.default)
        elif f.default_factory is not dataclasses.MISSING:
            default_str = f"{f.default_factory.__name__}()" if hasattr(f.default_factory, "__name__") else "factory"

        fields.append((f.name, type_str, default_str))
    return fields


def classify_export(obj: Any, name: str) -> str:
    """Classify an export into a category."""
    if isinstance(obj, type):
        if dataclasses.is_dataclass(obj):
            return "dataclass"
        # Check if it's an enum
        if hasattr(obj, "__members__"):
            return "enum"
        # Check if it's a PyO3 class (builtins module = Rust)
        module = getattr(obj, "__module__", "")
        if "horizon._horizon" in module or "builtins" in module:
            return "rust_class"
        return "python_class"
    elif callable(obj):
        module = getattr(obj, "__module__", "")
        if "horizon._horizon" in module or "builtins" in module:
            return "rust_function"
        return "python_function"
    elif isinstance(obj, str):
        return "constant_str"
    elif isinstance(obj, (int, float)):
        return "constant_num"
    elif isinstance(obj, list):
        return "constant_list"
    elif isinstance(obj, dict):
        return "constant_dict"
    else:
        return "other"


def get_enum_variants(obj: Any) -> list[str]:
    """Get enum variant names."""
    members = getattr(obj, "__members__", None)
    if members:
        return list(members.keys())
    # Fallback: scan dir for uppercase
    return [a for a in dir(obj) if not a.startswith("_") and a[0].isupper()]


# Logical grouping of exports
GROUPS = {
    "Core Entry Points": [
        "run", "backtest", "BacktestResult", "quotes",
    ],
    "Core Types": [
        "Engine", "Market", "Quote", "OrderRequest", "Order", "Position", "Fill",
        "EngineStatus", "FeedSnapshot", "RiskConfig", "Context", "Risk",
    ],
    "Enums": [
        "Side", "OrderSide", "OrderType", "TimeInForce", "OrderStatus", "AlertLevel",
        "TriggerType",
    ],
    "Multi-Outcome Events": [
        "Outcome", "Event",
    ],
    "Exchanges": [
        "Polymarket", "Kalshi", "Alpaca",
    ],
    "Feeds": [
        "BinanceWS", "RESTFeed", "PolymarketBook", "KalshiBook",
        "PredictItFeed", "ManifoldFeed", "ESPNFeed", "NWSFeed",
        "RESTJsonPathFeed", "ChainlinkFeed", "MempoolFeed", "AlpacaFeed",
        "CalendarFeed", "TreasuryFeed",
    ],
    "Kelly Criterion & Sizing": [
        "edge", "kelly", "kelly_no", "fractional_kelly", "kelly_size",
        "multi_kelly", "liquidity_adjusted_kelly",
        "kelly_sizer", "kelly_sizer_with_liquidity",
        "yield_adjusted_kelly", "opportunity_cost", "breakeven_edge",
        "yield_adjusted_kelly_size", "yield_adjusted_sizer",
        "opportunity_cost_filter", "collateral_optimizer",
    ],
    "Signals": [
        "Signal", "signal_combiner", "price_signal", "imbalance_signal",
        "spread_signal", "momentum_signal", "flow_signal",
        "combine_signals", "ema", "zscore", "decay_weight",
    ],
    "Market Making": [
        "reservation_price", "optimal_spread", "competitive_spread",
        "mm_size", "estimate_volatility", "market_maker",
        "inventory_skewer", "adaptive_spread", "SpreadMetrics",
        "toxicity_adjusted_spread",
    ],
    "Volatility": [
        "parkinson_vol", "garman_klass_vol", "yang_zhang_vol",
        "ewma_vol", "rolling_vol", "VolatilitySnapshot", "volatility",
    ],
    "Monte Carlo & Simulation": [
        "SimPosition", "SimulationResult", "monte_carlo", "simulate",
    ],
    "Information Theory": [
        "shannon_entropy", "joint_entropy", "kl_divergence",
        "mutual_information", "transfer_entropy",
    ],
    "Microstructure": [
        "kyles_lambda", "amihud_ratio", "roll_spread",
        "effective_spread", "realized_spread", "lob_imbalance", "weighted_mid",
    ],
    "Risk Analytics": [
        "cornish_fisher_var", "cornish_fisher_cvar",
        "prediction_greeks", "PredictionGreeks",
    ],
    "Signal Analysis": [
        "information_coefficient", "signal_half_life",
        "hurst_exponent", "variance_ratio",
    ],
    "Statistical Testing": [
        "deflated_sharpe", "bonferroni_threshold", "benjamini_hochberg",
    ],
    "Streaming Detectors": [
        "VpinDetector", "CusumDetector", "OfiTracker",
        "HawkesProcess", "ledoit_wolf_shrinkage",
    ],
    "Arbitrage": [
        "ArbitrageOpportunity", "ParityArbitrageOpportunity",
        "LatencyArbOpportunity", "EventArbitrageOpportunity",
        "ArbResult", "EventArbResult", "SpreadSignal",
        "StatArbResult", "CompositeArbResult", "ParityResult",
        "arb_scanner", "arb_sweep",
        "parity_arb_scanner", "parity_arb_sweep",
        "event_arb_scanner", "event_arb_sweep",
        "spread_convergence", "StatArbConfig", "stat_arb",
        "MMArbConfig", "mm_arb", "latency_arb",
        "ArbMethodConfig", "composite_arb",
        "implication_arb_scanner", "contradiction_arb_scanner",
        "cointegration_test", "spread_zscore",
    ],
    "AFML Bars & Labeling": [
        "Bar", "tick_bars", "volume_bars", "dollar_bars",
        "tick_imbalance_bars", "volume_imbalance_bars",
        "tick_run_bars", "volume_run_bars",
        "BarrierLabel", "get_daily_vol", "cusum_filter",
        "triple_barrier_labels", "meta_labels", "drop_labels",
        "frac_diff_weights", "frac_diff_ffd", "frac_diff_expanding",
        "adf_statistic", "min_frac_diff",
    ],
    "Hierarchical Risk Parity": [
        "HRPResult", "DenoisedCov", "hrp_weights",
        "denoise_covariance", "detone_covariance",
    ],
    "Calibration & Analytics": [
        "CalibrationResult", "EdgeDecayResult", "FeedMetrics",
        "calibration_curve", "log_loss", "edge_decay",
        "CalibrationTracker",
        "Metrics", "Tearsheet", "DrawdownRecord",
        "compute_metrics", "generate_tearsheet",
    ],
    "Discovery": [
        "discover_markets", "discover_events", "top_markets", "get_market_detail",
    ],
    "Historical Data": [
        "HistoricalTrade", "OHLCBar",
        "fetch_polymarket_prices", "fetch_polymarket_trades",
        "fetch_kalshi_book", "fetch_polymarket_book", "fetch_kalshi_market",
        "fetch_prices", "fetch_trades", "ohlc_to_ticks",
    ],
    "Flow Analytics (Polymarket)": [
        "Trade", "PolymarketPosition", "Holder", "WalletProfile", "MarketFlow",
        "get_market_trades", "get_wallet_trades", "get_wallet_positions",
        "get_wallet_value", "get_top_holders", "get_wallet_profile",
        "analyze_market_flow",
    ],
    "Logit-Space Pricing": [
        "LogitGreeks", "LogitPnlAttribution",
        "logit", "sigmoid", "sigmoid_prime", "sigmoid_double_prime",
        "logit_reservation_price", "logit_optimal_spread",
        "logit_greeks", "logit_pnl_decompose",
        "logit_market_maker", "logit_greeks_pipeline",
        "logit_pnl_attribution_pipeline",
    ],
    "Bootstrap Methods": [
        "BootstrapResult", "BootstrapHypothesisResult", "JackknifeResult",
        "bootstrap_mean", "bootstrap_sharpe", "bootstrap_var",
        "bootstrap_correlation", "bootstrap_edge",
        "block_bootstrap", "circular_block_bootstrap",
        "stationary_bootstrap", "bootstrap_hypothesis_test",
        "bootstrap_prediction_interval", "jackknife_bias",
        "bootstrap_edge_tracker", "bootstrap_strategy_test",
    ],
    "Belief-Vol & Breeden-Litzenberger": [
        "BeliefVolSurface", "belief_vol_tracker",
        "RiskNeutralDensity", "CrossAssetEdge",
        "risk_neutral_density", "implied_probability_above",
        "implied_probability_below", "implied_probability_between",
        "cross_asset_edge", "risk_premium_adjustment", "cross_asset_signal",
    ],
    "Kalman & Particle Filters": [
        "KalmanFilter", "UnscentedKF",
        "kalman_tracker", "kalman_hedge_ratio", "kalman_spread",
        "ParticleFilter", "PFState", "particle_tracker",
    ],
    "BOCPD (Change Point Detection)": [
        "BocpdDetector", "BocpdResult", "bocpd_detector",
    ],
    "Lead-Lag Detection": [
        "LeadLagResult", "GrangerResult", "LeadLagNetwork",
        "hayashi_yoshida", "cross_correlation_lags",
        "granger_causality", "lead_lag_network", "lead_lag_detector",
    ],
    "Graph & Network Analysis": [
        "GraphNode", "GraphEdge", "Community", "MarketGraph",
        "build_correlation_graph", "minimum_spanning_tree",
        "detect_communities", "contagion_risk", "market_network",
    ],
    "Optimal Execution": [
        "GPTrajectory", "gp_optimal_trajectory", "gp_execution_cost",
        "gp_urgency", "gp_executor",
        "ACTrajectory", "ac_optimal_schedule", "ac_efficient_frontier",
        "ac_kappa", "ac_liquidator",
    ],
    "Queue Modeling": [
        "QueueModel", "FillProbResult", "queue_fill_prob", "queue_tracker",
    ],
    "Copulas": [
        "CopulaFamily", "CopulaFit", "fit_copula", "best_copula",
        "copula_sample", "copula_cdf", "rank_transform", "copula_dependence",
        "VineType", "PairCopula", "VineCopulaResult",
        "fit_vine", "vine_sample", "vine_tail_risk", "vine_risk_monitor",
    ],
    "Cross-Impact": [
        "CrossImpactMatrix", "CrossImpactEstimator",
        "estimate_cross_impact", "cross_impact_monitor",
    ],
    "Entropy Pooling": [
        "EntropyPoolResult", "ViewConstraint",
        "entropy_pool", "posterior_mean", "posterior_covariance",
        "entropy_pooling",
    ],
    "Robust Portfolio": [
        "RobustPortfolioResult", "robust_optimize",
        "worst_case_return", "robust_efficient_frontier", "robust_allocator",
    ],
    "Optimal Stopping": [
        "StopPolicy", "generate_gbm_paths", "longstaff_schwartz",
        "should_exit", "exit_optimizer",
    ],
    "ACD Duration Models": [
        "AcdModel", "AcdState", "acd_log_likelihood", "fit_acd",
        "duration_monitor",
    ],
    "Elastic Net / LASSO / Ridge": [
        "ElasticNetResult", "elastic_net_fit", "elastic_net_predict",
        "lasso_fit", "ridge_fit", "elastic_net_cv", "signal_selector",
    ],
    "Microstructure Invariance": [
        "InvarianceResult", "trading_activity", "invariant_bet_size",
        "kyle_obizhaeva_impact", "is_informed_trade",
        "invariance_analysis", "invariance_monitor",
    ],
    "Volatility Signature": [
        "SignaturePlot", "volatility_signature",
        "two_scale_realized_vol", "noise_variance_estimate",
        "optimal_sampling_frequency", "vol_signature_analyzer",
    ],
    "Characteristic Function Pricing": [
        "PricingModel", "HestonParams", "MertonParams", "VGParams",
        "BinaryPrice", "binary_price_heston", "binary_price_merton",
        "binary_price_vg", "implied_vol_from_binary", "pricing_signal",
    ],
    "HJB Market Making": [
        "HJBConfig", "HJBSolution", "HJBQuote",
        "solve_hjb", "hjb_quote", "hjb_arrival_rate", "hjb_market_maker",
    ],
    "Stock Hedging": [
        "HedgeSensitivities", "MultiHedgeResult", "ScenarioOutcome",
        "HedgeCostTracker", "hedge_ratio_ols", "rolling_correlation",
        "hedge_effectiveness", "basis_risk", "optimal_hedge_size",
        "compute_hedge_sensitivities", "multi_ticker_hedge", "hedge_scenario",
        "HedgeConfig", "hedge_monitor", "hedge_executor",
        "correlation_tracker", "stock_hedge", "compute_hedge_report", "run_scenario",
    ],
    "Synthetic Index": [
        "WeightingMethod", "IndexWeight", "IndexLevel",
        "IndexRebalanceAction", "IndexArbSignal",
        "compute_index_level", "compute_weights_equal",
        "compute_weights_inverse_vol", "compute_weights_liquidity",
        "rebalance_weights", "index_tracking_error", "index_arb_signal",
        "IndexConfig", "index_pipeline", "track_index", "index_arb",
        "CRYPTO_PREDICTION_INDEX", "POLITICS_INDEX",
    ],
    "Correlation Regime": [
        "CorrelationEntry", "RegimeShiftResult", "ContagionAlert",
        "DecorrelationResult", "live_correlation_matrix",
        "detect_regime_shift", "contagion_score", "find_decorrelated",
        "incremental_correlation_update",
        "correlation_matrix", "regime_shift", "contagion_alert",
        "decorrelation_finder",
    ],
    "Portable Alpha": [
        "FactorExposure", "AlphaDecomposition", "NeutralityTarget",
        "NeutralityAdjustment", "beta_decompose", "compute_factor_exposures",
        "neutrality_adjustment", "incremental_beta_update",
        "FactorConfig", "PortableAlphaConfig",
        "beta_decompose_pipeline", "market_neutral",
        "portable_alpha_pipeline", "factor_model",
    ],
    "Trigger System": [
        "TriggerCondition", "TriggerActionType", "TriggerRule", "TriggerResult",
        "evaluate_trigger", "create_trigger",
        "TriggerConfig", "trigger_pipeline", "conditional_hedge", "oracle_pipeline",
    ],
    "Options Overlay": [
        "SyntheticOption", "VolSurfacePoint", "TermStructurePoint", "SpreadStrategy",
        "build_options_chain", "vol_surface", "term_structure",
        "synthetic_butterfly", "synthetic_straddle", "calendar_spread_legs",
        "options_chain", "delta_hedge_continuous", "vol_surface_monitor",
    ],
    "Liquidity Aggregation": [
        "AggregatedLevel", "AggregatedBook", "SmartRouteDecision", "VenueMapping",
        "aggregate_books", "smart_route_rust", "best_venue", "merged_mid_price",
        "aggregate_book", "smart_route_pipeline", "cross_venue_mm",
    ],
    "Multi-Leg Execution": [
        "FillPolicy", "LegStatus", "Leg", "LegResult", "MultiLegResult",
        "validate_legs", "aggregate_leg_risk", "build_mock_result", "multi_leg",
    ],
    "Evolution Optimizer": [
        "EvolutionConfig", "Individual", "ParamBound", "EvolutionResult",
        "GenerationStats", "ConvergenceState", "AdaptiveMutationState",
        "IslandConfig", "NsgaIndividual", "NsgaResult",
        "SelectionMethod", "MigrationTopology", "ObjectiveDirection",
        "tournament_select", "uniform_select", "roulette_select", "rank_select",
        "ucb1_select", "epsilon_greedy_select",
        "gaussian_mutate", "uniform_crossover", "init_population",
        "non_dominated_sort", "crowding_distance_assign",
        "nsga2_select", "pareto_front",
        "island_migrate", "island_selection_methods",
        "check_convergence", "adapt_mutation_sigma",
        "population_diversity", "compute_generation_stats",
        "evolve", "evolve_nsga2", "evolve_islands",
    ],
    "Adversarial Simulation": [
        "AdversaryType", "AdversaryConfig", "AdversaryAction",
        "SimulationMetrics", "AdversarialResult",
        "whale_action", "front_runner_action", "spoofing_action",
        "liquidity_drainer_action", "compute_simulation_metrics",
        "vulnerability_score",
        "AdversarialSimConfig", "adversarial_sim", "anti_fragile",
        "WHALE_PRESET", "FRONT_RUNNER_PRESET", "SPOOFING_PRESET", "DRAINER_PRESET",
    ],
    "Execution Algorithms": [
        "ExecAlgo", "TWAP", "VWAP", "Iceberg",
        "execution_tracker", "ExecQuality",
    ],
    "Contingent Orders": [
        "ContingentOrder", "TriggerType",
    ],
    "Alerts & Monitoring": [
        "AlertManager", "AlertType", "Alert",
        "WebhookChannel", "TelegramChannel", "DiscordChannel", "LogChannel",
        "MetricsCollector", "MetricsServer", "update_engine_metrics",
        "PriceAggregator", "AggregatedPrice",
        "OrderbookSnapshot",
    ],
    "Feed Health & Guards": [
        "feed_guard", "FeedHealthReport", "FeedHealthStatus",
    ],
    "Regime Detection": [
        "MarkovRegimeModel", "prices_to_returns",
        "regime_signal", "markov_regime",
    ],
    "Portfolio Management": [
        "Portfolio", "PortfolioAsset", "PortfolioMetrics",
        "risk_parity_weights", "min_variance_weights",
    ],
    "Alpha Decay": [
        "AlphaDecayTracker", "AlphaDecayReport", "alpha_decay_pipeline",
    ],
    "Feature Importance (AFML)": [
        "FeatureImportance", "mda_importance", "sfi_importance", "clustered_mda",
    ],
    "Meta-Labeling": [
        "MetaLabel", "compute_meta_labels", "meta_label_pipeline",
    ],
    "Multi-Strategy Book": [
        "StrategyBook", "StrategySlot", "StrategyCorrelation", "MultiStrategyReport",
    ],
    "PnL Attribution": [
        "PnLBreakdown", "TimeBreakdown", "FactorBreakdown", "AttributionReport",
        "GreeksAttribution", "attribute_pnl", "attribute_by_time",
        "attribute_by_factor", "pnl_attribution_pipeline", "greeks_attribution",
    ],
    "Reconciliation": [
        "PositionBreak", "ReconciliationReport", "reconcile", "auto_reconcile",
    ],
    "Copy-Trading": [
        "CopyTraderConfig", "CopyTradeResult", "copy_trades", "copy_trader",
    ],
    "Wallet Intelligence": [
        "WalletScore", "WalletPattern", "WalletAnalysis", "BotDetection",
        "score_wallet", "analyze_wallet", "scan_bots",
        "reverse_copy", "reverse_copier", "bot_scanner",
    ],
    "Wallet Profiler": [
        "FullWalletProfile", "ProfitStrategy", "TemporalProfile",
        "MarketSelectionProfile", "OrderFlowProfile", "EdgeDetection",
        "profile_wallet", "hunt", "hunter",
    ],
    "Wallet Tracker": [
        "TrackerConfig", "WalletMarketSignal", "ConsensusSignal",
        "WalletRegimeChange", "compute_wallet_signal", "compute_consensus",
        "track_wallet", "scan_wallets", "wallet_tracker",
    ],
    "Whale Galaxy": [
        "RankedWallet", "WalletCluster", "WhaleAlert", "GalaxySnapshot",
        "GalaxyConfig", "scan_galaxy", "detect_clusters", "auto_target",
        "galaxy_tracker", "galaxy_hunt",
    ],
    "Data Collector": [
        "Collector", "CollectorConfig", "CollectorStatus",
        "DataCollectorConfig", "collect", "collector",
        "export_parquet", "query_orderbooks", "query_collected_trades",
    ],
    "Stealth Execution": [
        "ImpactEstimate", "SmartRoute", "ExecutionPlan", "StealthConfig",
        "AdaptiveTWAP", "IcebergPlus", "SniperAlgo",
        "estimate_impact", "smart_route", "stealth_execute", "stealth_executor",
    ],
    "Sentinel (Risk Monitor)": [
        "CorrelationAlert", "DrawdownLevel", "HedgeSuggestion",
        "RiskBudget", "SentinelReport", "SentinelConfig",
        "sentinel_report", "suggest_hedges", "sentinel",
    ],
    "Oracle (Forecasting)": [
        "SignalReading", "MarketForecast", "EdgeOpportunity",
        "OracleReport", "OracleConfig",
        "forecast_market", "scan_edges", "oracle", "oracle_report",
    ],
    "LLM Signal": [
        "NewsItem", "LLMForecast", "LLMConfig",
        "llm_forecast", "llm_scan", "llm_signal",
    ],
    "Resolution Analysis": [
        "ResolutionCondition", "ResolutionAnalysis", "ResolutionConfig",
        "analyze_resolution", "batch_analyze", "resolution_guard",
        "ResolutionSignal", "SniperConfig",
        "scan_resolutions", "resolution_sniper",
    ],
    "Stress Testing": [
        "StressScenario", "ScenarioResult", "StressTestResult",
        "stress_test", "CORRELATION_SPIKE", "ALL_RESOLVE_NO",
        "LIQUIDITY_SHOCK", "TAIL_RISK",
    ],
    "Walk-Forward & Optimization": [
        "walk_forward", "WalkForwardResult", "WalkForwardWindow",
        "bayesian_optimize", "BayesianOptResult",
        "CPCVResult", "cpcv", "probability_of_overfitting",
    ],
    "Quant Pipeline Functions": [
        "toxic_flow", "microstructure", "change_detector",
        "hawkes_intensity", "correlation_estimator",
        "strategy_significance", "signal_diagnostics", "market_efficiency",
    ],
    "Calendar Events": [
        "time_decay", "event_countdown", "pre_event_strategy",
        "bundled_events_json", "FOMC_DATES", "CPI_DATES",
    ],
    "Cross-Market Hedging": [
        "cross_hedger",
    ],
    "Rewards": [
        "RewardConfig", "RewardMarket", "fetch_reward_markets",
        "EpochRewards", "RewardsTracker", "rewards_tracker",
    ],
    "Hot Reload": [
        "ParamReloader", "hot_reload", "write_params",
    ],
    "Lifecycle": [
        "register_expiry", "register_market_expiries",
        "check_lifecycle", "time_to_expiry", "approaching_expiry",
    ],
    "Auth": [
        "signup", "login", "generate_sdk_key", "hash_api_key",
        "create_sdk_key", "save_credentials", "load_credentials",
        "resolve_password", "auth_setup",
        "current_tier", "require_pro", "require_ultra",
    ],
}


def generate_reference() -> str:
    """Generate the full SDK reference markdown."""
    import horizon as hz

    lines = []
    lines.append("# Horizon SDK Reference")
    lines.append(f"\nVersion: {hz.__version__}")
    lines.append(f"\nAuto-generated from live module introspection. Every signature below is the real API.")
    lines.append("")

    # Track which exports we've documented
    documented = set()

    # --- Strategy Pattern Section (hardcoded, most important for LLM) ---
    lines.append("## How to Write a Strategy")
    lines.append("")
    lines.append("Every strategy follows this pattern:")
    lines.append("")
    lines.append("```python")
    lines.append("import horizon as hz")
    lines.append("")
    lines.append("def fair_value(ctx: hz.Context) -> float:")
    lines.append('    price = ctx.feeds["feed_name"].price')
    lines.append("    return price")
    lines.append("")
    lines.append("def quoter(ctx: hz.Context, fair: float) -> list[hz.Quote]:")
    lines.append("    return hz.quotes(fair=fair, spread=0.04, size=10)")
    lines.append("")
    lines.append("hz.run(")
    lines.append('    name="my_strategy",')
    lines.append('    markets=["market-slug"],')
    lines.append('    feeds={"feed_name": hz.BinanceWS("btcusdt")},')
    lines.append("    pipeline=[fair_value, quoter],")
    lines.append("    risk=hz.Risk(max_position=100, max_notional=1000),")
    lines.append(")")
    lines.append("```")
    lines.append("")
    lines.append("**Pipeline rules:**")
    lines.append("- Each function takes `ctx: hz.Context` as first arg")
    lines.append("- Return value is passed as kwarg to next function (matched by parameter name)")
    lines.append("- Last function returns `list[Quote]` to submit orders")
    lines.append("- Dict pipeline for per-market logic: `{'market_a': [fn1], '*': [fn2]}`")
    lines.append("")
    lines.append("**Context object:**")
    lines.append("- `ctx.feeds` — dict of FeedData snapshots (price, bid, ask, timestamp)")
    lines.append("- `ctx.feed` — shortcut for first/default feed")
    lines.append("- `ctx.inventory` — InventorySnapshot (positions, net, net_for_market)")
    lines.append("- `ctx.market` — current Market object")
    lines.append("- `ctx.market_id` — current market ID string")
    lines.append("- `ctx.event` — current Event (if multi-outcome)")
    lines.append("- `ctx.status` — EngineStatus (PnL, uptime, kill switch)")
    lines.append("- `ctx.params` — custom dict from hz.run(params={...})")
    lines.append("")

    # --- Generate each group ---
    for group_name, names in GROUPS.items():
        group_items = []
        for name in names:
            obj = getattr(hz, name, None)
            if obj is None:
                continue
            documented.add(name)
            group_items.append((name, obj))

        if not group_items:
            continue

        lines.append(f"## {group_name}")
        lines.append("")

        for name, obj in group_items:
            kind = classify_export(obj, name)

            if kind == "enum":
                variants = get_enum_variants(obj)
                lines.append(f"### `{name}` (enum)")
                lines.append(f"Variants: {', '.join(f'`{v}`' for v in variants)}")
                doc = get_docstring(obj)
                if doc:
                    lines.append(f"\n{doc}")
                lines.append("")

            elif kind == "dataclass":
                lines.append(f"### `{name}` (dataclass)")
                fields = get_dataclass_fields(obj)
                if fields:
                    lines.append("```python")
                    field_strs = []
                    for fname, ftype, fdefault in fields:
                        if fname.startswith("_"):
                            continue
                        s = f"    {fname}"
                        if ftype:
                            s += f": {ftype}"
                        if fdefault:
                            s += f" = {fdefault}"
                        field_strs.append(s)
                    lines.append(f"{name}(")
                    lines.append(",\n".join(field_strs))
                    lines.append(")")
                    lines.append("```")
                doc = get_docstring(obj)
                if doc:
                    lines.append(f"\n{doc}")
                # Show methods if any
                methods = [
                    (n, m) for n, m in inspect.getmembers(obj)
                    if not n.startswith("_") and callable(m)
                    and n not in [f.name for f in dataclasses.fields(obj)]
                ]
                if methods:
                    for mname, method in methods:
                        sig = get_signature(method, mname)
                        if sig:
                            lines.append(f"- `{sig}`")
                lines.append("")

            elif kind in ("rust_class", "python_class"):
                sig = get_signature(obj, name)
                lines.append(f"### `{name}` (class)")
                if sig:
                    lines.append(f"```python\n{sig}\n```")
                doc = get_docstring(obj)
                if doc:
                    # Truncate very long docs
                    if len(doc) > 500:
                        doc = doc[:500] + "..."
                    lines.append(f"\n{doc}")
                # Show public attrs/methods
                attrs = get_class_attrs(obj)
                if attrs:
                    lines.append("\nMembers:")
                    for aname, adesc in attrs[:30]:  # Cap at 30 to avoid huge output
                        lines.append(f"- `{aname}` — {adesc}")
                lines.append("")

            elif kind in ("rust_function", "python_function"):
                sig = get_signature(obj, name)
                if sig:
                    lines.append(f"### `{sig}`")
                else:
                    lines.append(f"### `{name}(...)`")
                doc = get_docstring(obj)
                if doc:
                    if len(doc) > 300:
                        doc = doc[:300] + "..."
                    lines.append(f"\n{doc}")
                lines.append("")

            elif kind.startswith("constant"):
                val = repr(obj)
                if len(val) > 100:
                    val = val[:100] + "..."
                lines.append(f"### `{name}` = `{val}`")
                lines.append("")

            else:
                lines.append(f"### `{name}` ({type(obj).__name__})")
                lines.append("")

    # --- Catch any undocumented exports ---
    all_exports = set(hz.__all__)
    undocumented = all_exports - documented
    if undocumented:
        lines.append("## Other Exports")
        lines.append("")
        for name in sorted(undocumented):
            obj = getattr(hz, name, None)
            if obj is None:
                continue
            kind = classify_export(obj, name)
            sig = get_signature(obj, name) if callable(obj) else None
            if sig:
                lines.append(f"- `{sig}`")
            else:
                lines.append(f"- `{name}` ({kind})")
        lines.append("")

    return "\n".join(lines)


def main():
    parser = argparse.ArgumentParser(description="Generate Horizon SDK reference")
    parser.add_argument("-o", "--output", help="Output file (default: stdout)")
    args = parser.parse_args()

    ref = generate_reference()

    if args.output:
        with open(args.output, "w") as f:
            f.write(ref)
        print(f"Written to {args.output} ({len(ref)} bytes)", file=sys.stderr)
    else:
        print(ref)


if __name__ == "__main__":
    main()
