"""Probe for checking Oracle price data availability for alternative parking ETFs.

Checks whether BUFB, GPIX, JEPQ, SBIL, MERIX (and any custom symbols) have
sufficient price history in the Oracle API for use as cash parking assets.

Usage:
    python -m apps.tools.alternative_etf_parking_probe
    python -m apps.tools.alternative_etf_parking_probe --symbols JEPQ SBIL
    python -m apps.tools.alternative_etf_parking_probe --start 2020-01-01 --end 2026-04-01
"""
from __future__ import annotations

import argparse
import asyncio
import datetime as dt
from typing import Any

from libs.common.config import get_settings
from libs.oracle_client.client import OracleClient
from libs.oracle_client.exceptions import OracleNotFoundError
from libs.oracle_client.price import PriceService

_DEFAULT_SYMBOLS = ["BUFB", "GPIX", "JEPQ", "SBIL", "MERIX"]
_BACKTEST_START = dt.date(2022, 1, 1)
_BACKTEST_END = dt.date(2026, 4, 1)
_BASELINE_SYMBOLS = ["QQQM", "SGOV", "SPY"]


async def _check_symbol(
    svc: PriceService,
    ticker: str,
    start: dt.date,
    end: dt.date,
) -> dict[str, Any]:
    try:
        resp = await svc.get_daily_bars(
            ticker,
            start=start.isoformat(),
            end=end.isoformat(),
        )
        bars = resp.bars
        if not bars:
            return {"ticker": ticker, "available": False, "reason": "empty response"}
        dates = sorted(dt.date.fromisoformat(b.date) for b in bars)
        first = dates[0]
        last = dates[-1]
        # "Viable" = data starts within 6 months of backtest start AND ends within 30 days of backtest end
        covers_start = first <= start + dt.timedelta(days=180)
        covers_end = last >= end - dt.timedelta(days=30)
        coverage_pct = len(bars) / max(1, (end - start).days / 365.0 * 252)
        usable = covers_start and covers_end and coverage_pct >= 0.5
        first_close = float(bars[0].close)
        last_close = float(bars[-1].close)
        days_span = (last - first).days or 1
        ann_return = (last_close / first_close) ** (365.0 / days_span) - 1.0
        return {
            "ticker": ticker,
            "available": True,
            "bar_count": len(bars),
            "first_date": first.isoformat(),
            "last_date": last.isoformat(),
            "covers_backtest": first <= start,
            "usable": usable,
            "coverage_pct": round(coverage_pct * 100, 1),
            "first_close": first_close,
            "last_close": last_close,
            "ann_return_pct": round(ann_return * 100, 2),
        }
    except OracleNotFoundError:
        return {"ticker": ticker, "available": False, "reason": "not found in Oracle"}
    except Exception as exc:
        return {"ticker": ticker, "available": False, "reason": str(exc)}


async def _run(
    symbols: list[str],
    start: dt.date,
    end: dt.date,
) -> None:
    s = get_settings()
    async with OracleClient(base_url=s.stock_oracle_url) as client:
        svc = PriceService(client)
        all_symbols = _BASELINE_SYMBOLS + symbols
        results = await asyncio.gather(
            *[_check_symbol(svc, sym, start, end) for sym in all_symbols]
        )

    # Print results
    print()
    print("=" * 80)
    print(f"  Oracle Data Availability Check  ({start} → {end})")
    print("=" * 80)
    print(f"  {'Ticker':<8}  {'Bars':>5}  {'First':<12}  {'Last':<12}  {'Cover%':>6}  {'Ann%':>6}  {'Usable':^7}  Notes")
    print("  " + "-" * 76)

    viable: list[str] = []
    for r in results:
        ticker = r["ticker"]
        available = r.get("available", False)
        is_baseline = ticker in _BASELINE_SYMBOLS
        tag = " [baseline]" if is_baseline else ""
        if available:
            usable = r.get("usable", False)
            cov = f"{r.get('coverage_pct', 0):.0f}%"
            ann = f"{r['ann_return_pct']:+.1f}%"
            usable_str = "YES" if usable else "PARTIAL"
            print(
                f"  {ticker:<8}  {r['bar_count']:>5}  {r['first_date']:<12}  {r['last_date']:<12}  {cov:>6}  {ann:>6}  {usable_str:^7}{tag}"
            )
            if not is_baseline and usable:
                viable.append(ticker)
        else:
            reason = r.get("reason", "unknown")
            print(f"  {ticker:<8}  {'N/A':>5}  {'N/A':<12}  {'N/A':<12}  {'N/A':>6}  {'N/A':>6}  {'NO':^7}  {reason}{tag}")

    print("=" * 80)
    print()
    if viable:
        print(f"  ✓ Viable for parking backtest: {', '.join(viable)}")
        print()
        print("  Already added to snapshot_store (snapshot_store.py):")
        for sym in viable:
            lower = sym.lower()
            print(f"    - _MACRO_TRADE_BAR_PREFIXES: ('{sym}', '{lower}')")
            print(f"    - extra_symbol_responses fetch loop")
            print(f"  Experiment configs to create:")
            print(f"    - configs/experiments/parking_only_{lower}.json")
    else:
        print("  ✗ No alternative ETFs with sufficient Oracle data coverage")
    print()


def main() -> None:
    parser = argparse.ArgumentParser(description="Check Oracle data for alternative parking ETFs")
    parser.add_argument(
        "--symbols",
        nargs="+",
        default=_DEFAULT_SYMBOLS,
        help=f"ETF symbols to check (default: {' '.join(_DEFAULT_SYMBOLS)})",
    )
    parser.add_argument(
        "--start",
        default=_BACKTEST_START.isoformat(),
        help=f"Start date (default: {_BACKTEST_START})",
    )
    parser.add_argument(
        "--end",
        default=_BACKTEST_END.isoformat(),
        help=f"End date (default: {_BACKTEST_END})",
    )
    args = parser.parse_args()
    asyncio.run(
        _run(
            symbols=args.symbols,
            start=dt.date.fromisoformat(args.start),
            end=dt.date.fromisoformat(args.end),
        )
    )


if __name__ == "__main__":
    main()
