"""IB DATA PROBE — enumera cosa un conto paper Interactive Brokers espone (dati storici).

NON e' una strategia: e' lo scan di FATTIBILITA' DATI, gemello di certify_feed.py per il mondo IB.
Disciplina v2.0.0: prima il dato (cosa c'e', quanto indietro, che qualita', cosa costa), poi la
strategia. "Solo dati, decido dopo".

PREREQUISITO: una sessione IB Gateway/TWS PAPER loggata e raggiungibile (default 127.0.0.1:4002).
  Tipico: avvii IB Gateway (Paper) sul tuo PC con API abilitata su 4002, poi reverse-tunnel
  SSH verso questo server:  ssh -R 4002:localhost:4002 utente@server
ESECUZIONE (senza sporcare le dipendenze del progetto):
  uv run --with ib_async python scripts/research/ib_probe.py
  uv run --with ib_async python scripts/research/ib_probe.py --port 7497   # TWS paper

Cosa fa, in ordine, e si ferma con diagnosi chiara al primo errore:
  (1) connette e stampa server version + account paper;
  (2) risolve un set di contratti rilevanti per QUESTO progetto:
        - CME crypto: BTC (5 BTC), MBT (micro 0.1 BTC), ETH, MET (micro);  -> per il BASIS/carry
        - spot crypto Paxos (se abilitato): BTC, ETH;
        - 2 azioni/ETF di riferimento (SPY, AAPL) per tarare durate/qualita';
  (3) per ogni contratto risolto: chiede un piccolo storico (durata breve, 1 day bars) e riporta
      n barre, range, e se scatta un errore di SUBSCRIPTION mancante (codice 354/10089/10090...);
  (4) sintesi: cosa e' scaricabile GRATIS su paper vs cosa richiede market-data a pagamento.
"""
import argparse, sys

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--host", default="127.0.0.1")
    ap.add_argument("--port", type=int, default=7497, help="7497=TWS paper (default), 4002=GW paper, 4001/7496=live")
    ap.add_argument("--client-id", type=int, default=77)
    args = ap.parse_args()

    try:
        from ib_async import IB, Future, Stock, Crypto, util
    except Exception:
        print("ib_async non importabile. Esegui con:  uv run --with ib_async python scripts/research/ib_probe.py")
        sys.exit(2)

    ib = IB()
    try:
        ib.connect(args.host, args.port, clientId=args.client_id, timeout=15)
    except Exception as e:
        print(f"[CONNESSIONE FALLITA] {args.host}:{args.port} -> {repr(e)[:160]}")
        print("  Verifica: IB Gateway/TWS Paper acceso, API abilitata, porta giusta, tunnel attivo.")
        sys.exit(1)

    print("=" * 90)
    print(f"  IB DATA PROBE — connesso {args.host}:{args.port} | serverVersion={ib.client.serverVersion()}")
    try:
        accts = ib.managedAccounts()
        print(f"  account: {accts}  (paper se inizia per 'D' tipicamente)")
    except Exception as e:
        print(f"  account: ? ({repr(e)[:60]})")
    print("=" * 90)

    # (2) contratti rilevanti
    candidates = []
    # CME crypto futures: lasciamo che IB scelga il front-month (no expiry -> reqContractDetails)
    candidates += [("CME BTC fut", Future("BTC", exchange="CME")),
                   ("CME MBT micro", Future("MBT", exchange="CME")),
                   ("CME ETH fut", Future("ETH", exchange="CME")),
                   ("CME MET micro", Future("MET", exchange="CME"))]
    # spot crypto Paxos (puo' non essere abilitato su paper)
    candidates += [("Paxos BTC", Crypto("BTC", exchange="PAXOS", currency="USD")),
                   ("Paxos ETH", Crypto("ETH", exchange="PAXOS", currency="USD"))]
    # riferimenti equity
    candidates += [("SPY ETF", Stock("SPY", "SMART", "USD")),
                   ("AAPL", Stock("AAPL", "SMART", "USD"))]

    resolved = []
    print("\n  (A) RISOLUZIONE CONTRATTI")
    for label, c in candidates:
        try:
            cds = ib.reqContractDetails(c)
            if not cds:
                print(f"    {label:16} -> NESSUN match")
                continue
            # per i futures prendi la scadenza piu' vicina disponibile
            cd = sorted(cds, key=lambda d: getattr(d.contract, "lastTradeDateOrContractMonth", "") or "")[0]
            con = cd.contract
            extra = f" exp={con.lastTradeDateOrContractMonth}" if getattr(con, "lastTradeDateOrContractMonth", "") else ""
            print(f"    {label:16} -> OK  {con.localSymbol or con.symbol} {con.exchange}{extra}  (n match={len(cds)})")
            resolved.append((label, con))
        except Exception as e:
            print(f"    {label:16} -> ERR {repr(e)[:70]}")

    # (3) prova storico breve
    print("\n  (B) STORICO DI PROVA (durata 10 D, barre 1 day)")
    for label, con in resolved:
        try:
            bars = ib.reqHistoricalData(con, endDateTime="", durationStr="10 D",
                                        barSizeSetting="1 day", whatToShow="TRADES",
                                        useRTH=False, formatDate=1, timeout=30)
            if not bars:
                print(f"    {label:16} -> 0 barre (forse serve subscription o whatToShow diverso)")
            else:
                print(f"    {label:16} -> {len(bars)} barre  {bars[0].date} .. {bars[-1].date}  close={bars[-1].close}")
        except Exception as e:
            print(f"    {label:16} -> ERR {repr(e)[:90]}")

    print("\n  (C) NOTE")
    print("    - errori 354/10089/10090/10168 = market-data subscription mancante (paper la eredita dal live).")
    print("    - per il BASIS/carry servono i MULTIPLI futures (front+next) -> poi reqContractDetails senza filtro expiry.")
    ib.disconnect()


if __name__ == "__main__":
    main()
