"""Yahoo Finance day_gainers fetcher for TGTC strategy.

Uses yfinance.screen("day_gainers") instead of the raw httpx endpoint
(query1.finance.yahoo.com/v1/finance/screener/predefined/saved was IP-blocked
after the zombie-daemon burst; yfinance routes through a different path).

Usage:
    # Async (in engine)
    quotes = await fetch_day_gainers()

    # Sync one-shot (CLI / testing)
    quotes = fetch_day_gainers_sync()

CLI:
    python -m apps.tgtc_trader.yahoo_gainers --once
"""
from __future__ import annotations

import asyncio
import logging
from dataclasses import dataclass
from typing import Any

import yfinance as yf

log = logging.getLogger(__name__)

# Deprecated / blocked endpoint constants kept as comments for reference:
# _YAHOO_URL = "https://query1.finance.yahoo.com/v1/finance/screener/predefined/saved?..."
# _HEADERS = {"User-Agent": "Mozilla/5.0 ..."}

_TIMEOUT_SECS = 12          # kept for documentation; yfinance manages its own timeout
_MAX_RETRIES = 3
_RETRY_DELAY_SECS = 3.0


@dataclass
class GainerQuote:
    symbol: str
    rank: int
    price: float
    pct_change: float        # e.g. 0.12 = +12%
    volume: float
    market_cap: float | None
    exchange: str | None


def _parse_yf_quotes(result: dict[str, Any]) -> list[GainerQuote]:
    """Extract GainerQuote list from yfinance.screen() result dict."""
    try:
        quotes_raw = result.get("quotes", [])
    except (AttributeError, TypeError):
        log.warning("TGTC Yahoo: unexpected yfinance screen result structure")
        return []

    out: list[GainerQuote] = []
    for rank, q in enumerate(quotes_raw, start=1):
        try:
            sym = q.get("symbol", "").upper()
            if not sym:
                continue
            price = float(q.get("regularMarketPrice") or 0)
            # yfinance already returns pct as raw value (e.g. 31.16834 = +31.17%)
            pct = float(q.get("regularMarketChangePercent") or 0) / 100.0
            vol = float(q.get("regularMarketVolume") or 0)
            mktcap_raw = q.get("marketCap")
            mktcap = float(mktcap_raw) if mktcap_raw is not None else None
            exchange = q.get("exchange") or q.get("fullExchangeName")
            out.append(GainerQuote(
                symbol=sym,
                rank=rank,
                price=price,
                pct_change=pct,
                volume=vol,
                market_cap=mktcap,
                exchange=exchange,
            ))
        except Exception as exc:
            log.debug("TGTC Yahoo: skip quote parse error: %s", exc)
    return out


async def fetch_day_gainers(count: int = 100) -> list[GainerQuote]:
    """Async fetch of Yahoo day_gainers screener via yfinance. Retries up to _MAX_RETRIES times."""
    last_exc: Exception | None = None
    for attempt in range(_MAX_RETRIES):
        try:
            result = await asyncio.to_thread(yf.screen, "day_gainers", count=count)
            return _parse_yf_quotes(result)
        except Exception as exc:
            last_exc = exc
            log.warning("TGTC Yahoo: fetch attempt %d/%d failed: %s",
                        attempt + 1, _MAX_RETRIES, exc)
            if attempt < _MAX_RETRIES - 1:
                await asyncio.sleep(_RETRY_DELAY_SECS * (attempt + 1))

    log.error("TGTC Yahoo: all %d attempts failed: %s", _MAX_RETRIES, last_exc)
    return []


def fetch_day_gainers_sync(count: int = 100) -> list[GainerQuote]:
    """Synchronous wrapper for testing or CLI usage."""
    return asyncio.run(fetch_day_gainers(count))


# ── CLI one-shot ──────────────────────────────────────────────────────────────

if __name__ == "__main__":
    import argparse

    logging.basicConfig(level=logging.INFO, format="%(levelname)s %(message)s")
    parser = argparse.ArgumentParser(description="TGTC Yahoo day_gainers one-shot fetch")
    parser.add_argument("--once", action="store_true", help="Fetch once and print")
    parser.add_argument("--count", type=int, default=100)
    args = parser.parse_args()

    if args.once:
        quotes = fetch_day_gainers_sync(args.count)
        print(f"Fetched {len(quotes)} gainers:")
        for q in quotes[:20]:
            print(f"  {q.rank:3d}. {q.symbol:<8s}  {q.pct_change*100:+.1f}%  "
                  f"${q.price:.2f}  vol={q.volume:,.0f}")
