"""
Equity universes for buy-the-dip and cross-sectional sleeves.

Russell 3000: cached CSV from a public GitHub mirror of index membership
(see ``RUSSELL3000_TICKERS_URL``). Not point-in-time; refresh periodically.
"""

from __future__ import annotations

import urllib.request
from io import StringIO
from pathlib import Path

import pandas as pd

_REPO = Path(__file__).resolve().parents[2]
UNIVERSE_DIR = _REPO / "RenTech" / "data" / "universe"
RUSSELL3000_TICKERS_URL = (
    "https://raw.githubusercontent.com/shashankvemuri/Finance/master/russell3000_tickers.csv"
)
RUSSELL3000_CACHE = UNIVERSE_DIR / "russell3000_tickers.csv"
_USER_AGENT = (
    "Mozilla/5.0 (compatible; trading_bot/1.0) "
    "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
)


def _to_yahoo_symbol(symbol: str) -> str:
    return symbol.strip().upper().replace(".", "-")


def fetch_russell3000_tickers(*, refresh: bool = False) -> list[str]:
    """
    Return Yahoo-style tickers for Russell 3000 (snapshot list, ~2.5k names).
    """
    if RUSSELL3000_CACHE.is_file() and not refresh:
        df = pd.read_csv(RUSSELL3000_CACHE)
    else:
        req = urllib.request.Request(RUSSELL3000_TICKERS_URL, headers={"User-Agent": _USER_AGENT})
        with urllib.request.urlopen(req, timeout=120) as resp:
            df = pd.read_csv(StringIO(resp.read().decode("utf-8", errors="replace")))
        UNIVERSE_DIR.mkdir(parents=True, exist_ok=True)
        df.to_csv(RUSSELL3000_CACHE, index=False)

    sym_col = "Ticker" if "Ticker" in df.columns else df.columns[0]
    raw = df[sym_col].dropna().astype(str).str.strip().str.upper()
    raw = raw.str.replace(".", "-", regex=False)
    # Drop blanks / obvious bad rows
    ok = raw.str.match(r"^[A-Z][A-Z0-9\-]{0,9}$", na=False)
    out = sorted(set(raw[ok].tolist()))
    if len(out) < 500:
        raise RuntimeError(f"Russell 3000 list too short ({len(out)} tickers); check {RUSSELL3000_CACHE}")
    return out


def load_equity_panel_dict(
    universe: str,
    daily_period: str,
    *,
    max_tickers: int = 0,
    refresh_cache: bool = False,
) -> dict[str, pd.DataFrame]:
    """
    Daily panels with ``close``, ``ret``, ``sma_200``, ``aqr_mom`` for dip / L/S engines.
    """
    from RenTech.strategy_stack.main import _compute_daily_backtest_features, _load_aqr_equity_dict

    u = universe.lower().strip()
    if u in ("sp500", "sp100"):
        return _load_aqr_equity_dict(
            daily_period,
            universe=u,
            max_tickers=max_tickers,
            refresh_cache=refresh_cache,
        )
    if u in ("russell3000", "r3k", "russell_3000"):
        import universe_scanner as us  # type: ignore[import-not-found]

        from RenTech.strategy_stack.main import _parse_daily_period_years

        tickers = fetch_russell3000_tickers()
        if max_tickers and max_tickers > 0:
            tickers = tickers[: int(max_tickers)]
        years = _parse_daily_period_years(daily_period)
        print(f"🔍 Downloading Russell 3000 equity universe ({len(tickers)} names) …", flush=True)
        equity_raw = us.download_and_cache_data(
            tickers,
            timeframe="1d",
            lookback_years=years,
            max_age_hours=0.0 if refresh_cache else 24.0,
        )
        equity_dict: dict[str, pd.DataFrame] = {}
        for t, df in equity_raw.items():
            if df is None or df.empty:
                continue
            df2 = df.copy()
            df2.index = pd.to_datetime(df2.index).tz_localize(None)
            df2 = df2.sort_index()
            equity_dict[t] = _compute_daily_backtest_features(df2)
        return equity_dict
    raise ValueError(f"universe must be sp500, sp100, or russell3000; got {universe!r}")
