"""Yahoo Finance Chart API 직접 호출 클라이언트 (yfinance 우회).

IP rotation (yfinance_proxy.ip_rotating_session) 과 함께 사용하면
yfinance 1.2 의 curl_cffi 요구사항 없이 Yahoo 데이터 수집 가능.

지원 기능:
    - 배치 OHLC 다운로드 (fetch_ohlcv_batch)
    - 시가총액 등 기본 정보 (fetch_quote_summary)

사용 예:
    from backend.app.services.yahoo_chart_client import fetch_ohlcv_batch
    from backend.app.services.yfinance_proxy import ip_rotating_session

    with ip_rotating_session() as session:
        df = fetch_ohlcv_batch(["AAPL", "MSFT"], session=session, range_str="60d")
"""
from __future__ import annotations

import logging
from concurrent.futures import ThreadPoolExecutor, as_completed
from typing import Any

import pandas as pd
import requests

logger = logging.getLogger(__name__)

_BASE = "https://query1.finance.yahoo.com"
_HEADERS = {
    # Yahoo가 2026년부터 흔한 봇 UA(Chrome/120 등)를 429로 차단 — 짧은 AppleWebKit UA는 통과.
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
    "Accept": "application/json",
}
_TIMEOUT = 20
_MAX_WORKERS = 8  # connection pool (default 10) 안에 맞춤


def _fetch_single_chart(
    ticker: str,
    range_str: str,
    interval: str,
    session: requests.Session,
    use_adjclose: bool = False,
) -> tuple[str, pd.DataFrame | None]:
    """단일 티커의 OHLCV 조회. 실패 시 (ticker, None) 반환.

    Args:
        use_adjclose: True면 indicators.adjclose.adjclose를 Close로 사용 (배당·분할 조정됨).
            yfinance의 auto_adjust=True와 동일한 효과.
    """
    url = f"{_BASE}/v8/finance/chart/{ticker}"
    params = {
        "interval": interval,
        "range": range_str,
        "includePrePost": "false",
        "events": "div,splits",  # adjclose 활성화
    }
    try:
        resp = session.get(url, params=params, headers=_HEADERS, timeout=_TIMEOUT)
        resp.raise_for_status()
        data = resp.json()
    except (requests.RequestException, ValueError) as exc:
        logger.debug("Chart API failed for %s: %s", ticker, exc)
        return ticker, None

    chart = data.get("chart") or {}
    result = (chart.get("result") or [None])[0]
    if not result:
        return ticker, None

    timestamps = result.get("timestamp") or []
    if not timestamps:
        return ticker, None
    indicators = result.get("indicators") or {}
    quote = (indicators.get("quote") or [{}])[0]

    close_values = quote.get("close") or []
    if use_adjclose:
        adj_list = indicators.get("adjclose") or [{}]
        adj_values = adj_list[0].get("adjclose") or []
        if adj_values:
            close_values = adj_values

    df = pd.DataFrame(
        {
            "Open": quote.get("open") or [],
            "High": quote.get("high") or [],
            "Low": quote.get("low") or [],
            "Close": close_values,
            "Volume": quote.get("volume") or [],
        },
        index=pd.to_datetime(timestamps, unit="s", utc=True).tz_convert("UTC").normalize(),
    )
    df.index.name = "Date"
    return ticker, df


def fetch_ohlcv_batch(
    tickers: list[str],
    session: requests.Session,
    range_str: str = "60d",
    interval: str = "1d",
    max_workers: int = _MAX_WORKERS,
    use_adjclose: bool = False,
) -> pd.DataFrame:
    """배치 OHLCV 조회. yfinance download() 와 비슷한 MultiIndex DataFrame 반환.

    Args:
        use_adjclose: True면 adjusted close (배당·분할 조정) 사용. yfinance auto_adjust=True와 동일.

    Returns:
        pd.DataFrame with MultiIndex columns (ticker, OHLCV field)
        빈 DataFrame 이면 모든 티커 실패.
    """
    results: dict[str, pd.DataFrame] = {}
    failed: list[str] = []

    with ThreadPoolExecutor(max_workers=max_workers) as executor:
        futures = {
            executor.submit(_fetch_single_chart, t, range_str, interval, session, use_adjclose): t
            for t in tickers
        }
        for fut in as_completed(futures):
            ticker, df = fut.result()
            if df is not None and not df.empty:
                results[ticker] = df
            else:
                failed.append(ticker)

    if failed:
        logger.warning("Failed to fetch %d/%d tickers: %s", len(failed), len(tickers), failed[:5])

    if not results:
        return pd.DataFrame()

    # yfinance 와 동일한 MultiIndex 포맷으로 결합
    combined = pd.concat(results, axis=1, names=["Ticker", "OHLCV"])
    # reorder: swap levels so it becomes (Ticker, OHLCV) with ticker on top
    return combined


# NOTE: /v7/finance/quote 와 /v10/finance/quoteSummary 는 2023년부터 crumb 인증 필요.
# 메타데이터(sector/industry/name/marketCap) 조회는 yfinance 1.2+ 의
# Ticker.info 를 그대로 사용하세요 (신고가 20건 정도면 레이트리밋 걱정 없음).
