"""
V18 smoke: validate broker.recent_trades against live TopstepX API.

Confirms POST /api/Trade/search still works on the ineligible account
and that the response shape matches what topstepx_v16.py expects:
  - filterable by accountId + time window (server-side)
  - filterable by symbol/contractId tag (client-side via _symbol_matches)
  - profitAndLoss != 0 isolates closing legs
  - sorted by closed_at desc, capped to limit

Usage:
    cd ~/apex_v16
    venv/bin/python -m tools.smoke_recent_trades                     # last 24h
    venv/bin/python -m tools.smoke_recent_trades --hours 168         # last 7 days
    venv/bin/python -m tools.smoke_recent_trades --symbol 6E         # filter
    venv/bin/python -m tools.smoke_recent_trades --raw               # dump raw POST body too

Env required (same as bot):
    PROJECT_X_API_KEY
    PROJECT_X_USERNAME
"""

from __future__ import annotations

import argparse
import asyncio
import logging
import os
import sys
from datetime import datetime, timedelta, timezone
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent.parent))

from broker.topstepx_v16 import TopstepXBroker


# Universe of symbols the bot tracks — keep aligned with main.py asset_list.
DEFAULT_INSTRUMENTS = ["MES", "MNQ", "MGC", "MCL", "6E", "6B", "6A", "6J", "6C"]


def _setup_logging() -> None:
    fmt = "%(asctime)s %(levelname)s [%(name)s] %(message)s"
    logging.basicConfig(level=logging.INFO, format=fmt)
    for name in ("topstepx_v16", "topstepx_adapter", "project_x_py"):
        logging.getLogger(name).setLevel(logging.INFO)


async def _raw_trade_search(broker: TopstepXBroker, since: datetime) -> dict | None:
    """
    Hit /Trade/search directly to capture the un-parsed JSON body.
    Mirrors topstepx_v16.recent_trades but without the ClosedTrade casting,
    so we see the actual server fields and field names.
    """
    token, account_id = await broker._rest_auth()
    if not token or not account_id:
        print("auth failed (token/account_id missing)")
        return None
    print(f"  account_id resolved: {account_id}")
    end = datetime.now(timezone.utc)
    return await broker._rest_post(
        "/Trade/search",
        {
            "accountId": account_id,
            "startTimestamp": since.isoformat(),
            "endTimestamp": end.isoformat(),
        },
        token,
        timeout=10,
    )


async def _run(args) -> int:
    api_key = os.getenv("PROJECT_X_API_KEY")
    username = os.getenv("PROJECT_X_USERNAME")
    if not api_key or not username:
        print("ERROR: PROJECT_X_API_KEY / PROJECT_X_USERNAME not set")
        return 2

    broker = TopstepXBroker(instruments=DEFAULT_INSTRUMENTS)
    print(f"connecting to TopstepX (instruments={DEFAULT_INSTRUMENTS})...")
    ok = await broker.connect()
    if not ok:
        print("ERROR: broker.connect() failed")
        return 3

    try:
        since = datetime.now(timezone.utc) - timedelta(hours=args.hours)
        print(
            f"\nquery window: since={since.isoformat()}  "
            f"hours={args.hours}  symbol_filter={args.symbol or '<any>'}"
        )

        # ── 1. Raw POST: confirm endpoint + body shape ──
        if args.raw:
            print("\n[1] raw POST /Trade/search")
            raw = await _raw_trade_search(broker, since)
            if raw is None:
                print("  raw call returned None (HTTP non-200 or transport error)")
            else:
                trades_raw = raw.get("trades") or []
                print(f"  keys: {list(raw.keys())}")
                print(f"  trades_count: {len(trades_raw)}")
                if trades_raw:
                    sample = trades_raw[0]
                    print(f"  first trade keys: {list(sample.keys())}")
                    print(f"  first trade: {sample!r}")

        # ── 2. Typed call: confirm ClosedTrade casting works ──
        print("\n[2] broker.recent_trades(...) typed call")
        trades = await broker.recent_trades(
            symbol=args.symbol, since=since, limit=args.limit,
        )
        print(f"  returned {len(trades)} ClosedTrade(s)")
        for i, t in enumerate(trades[:args.show]):
            print(
                f"  [{i:2d}] {t.closed_at}  {t.symbol}  side={t.side}  "
                f"size={t.contracts}  exit={t.exit_price}  pnl={t.pnl_usd:+.2f}  "
                f"id={t.trade_id}"
            )
        if len(trades) > args.show:
            print(f"  ... ({len(trades) - args.show} more)")

        # ── 3. Sanity: closing-leg disambiguation ──
        # Replicate orchestrator._check_external_close logic for any open
        # symbols we have today (none in smoke, so just demonstrate side
        # filter against the most recent trade if present).
        if trades:
            recent = trades[0]
            opp = "BUY" if recent.side == "SELL" else "SELL"
            print(
                f"\n[3] disambiguation sanity: most recent has side={recent.side}, "
                f"would match an active position with direction={opp}"
            )

        return 0
    finally:
        try:
            await broker.disconnect()
        except Exception as e:
            print(f"disconnect warning: {e}")


def _parse_args() -> argparse.Namespace:
    p = argparse.ArgumentParser(description="V18 smoke for broker.recent_trades")
    p.add_argument("--hours", type=int, default=24,
                   help="lookback window in hours (default 24)")
    p.add_argument("--symbol", type=str, default=None,
                   help="optional symbol filter (e.g. 6E, MES)")
    p.add_argument("--limit", type=int, default=50,
                   help="max ClosedTrades to fetch (default 50)")
    p.add_argument("--show", type=int, default=20,
                   help="how many trades to print (default 20)")
    p.add_argument("--raw", action="store_true",
                   help="also dump raw /Trade/search response shape")
    return p.parse_args()


def main() -> int:
    _setup_logging()
    args = _parse_args()
    return asyncio.run(_run(args))


if __name__ == "__main__":
    sys.exit(main())
