"""
Price History — fetch + cache des cours historiques via yfinance.
Cache SQLite dans la table price_history_cache.
"""
import json
import sqlite3
from datetime import datetime, timedelta, date as date_type
from typing import Optional


YF_TICKERS: dict[str, str] = {
    "EURUSD": "EURUSD=X",
    "USDJPY": "USDJPY=X",
    "XAUUSD": "GC=F",
    "SP500":  "^GSPC",
    "TLT":    "TLT",
    "GBPUSD": "GBPUSD=X",
    "EEM":    "EEM",
    "QQQ":    "QQQ",
}

PRICE_LABELS: dict[str, str] = {
    "EURUSD": "EUR/USD",
    "USDJPY": "USD/JPY",
    "XAUUSD": "Or ($/oz)",
    "SP500":  "S&P 500",
    "TLT":    "TLT ETF",
    "GBPUSD": "GBP/USD",
    "EEM":    "EEM ETF",
    "QQQ":    "QQQ ETF",
}

PERIOD_DAYS: dict[str, int] = {
    "5d": 7, "1mo": 35, "3mo": 95, "6mo": 190, "1y": 370, "2y": 740,
}


def _ensure_cache_table(conn):
    conn.execute("""
        CREATE TABLE IF NOT EXISTS price_history_cache (
            id          INTEGER PRIMARY KEY,
            instrument  TEXT NOT NULL,
            date        TEXT NOT NULL,
            close       REAL NOT NULL,
            fetched_at  TEXT DEFAULT (datetime('now')),
            UNIQUE(instrument, date)
        )
    """)
    conn.execute("""
        CREATE INDEX IF NOT EXISTS idx_price_cache_inst_date
        ON price_history_cache(instrument, date)
    """)
    conn.commit()


def _fetch_yf(instrument: str, period_days: int) -> list[dict]:
    """Fetch from yfinance. Returns [{date, close}] sorted ascending."""
    try:
        import yfinance as yf
        ticker = YF_TICKERS.get(instrument.upper())
        if not ticker:
            return []
        # yfinance period string
        if period_days <= 7:   p = "5d"
        elif period_days <= 35:  p = "1mo"
        elif period_days <= 95:  p = "3mo"
        elif period_days <= 190: p = "6mo"
        elif period_days <= 370: p = "1y"
        else:                    p = "2y"

        df = yf.download(ticker, period=p, interval="1d", progress=False, auto_adjust=True)
        if df is None or df.empty:
            return []

        # Handle MultiIndex columns (yfinance 0.2+)
        if hasattr(df.columns, 'levels'):
            df.columns = df.columns.get_level_values(0)

        close_col = next((c for c in ["Close", "Adj Close", "close"] if c in df.columns), None)
        if not close_col:
            return []

        result = []
        for idx, row in df.iterrows():
            dt = str(idx)[:10]
            v  = float(row[close_col])
            if v and v == v:  # not NaN
                result.append({"date": dt, "close": round(v, 6)})
        return result

    except Exception:
        return []


def get_price_history(
    conn,
    instrument: str,
    period: str = "1y",
    force_refresh: bool = False,
) -> list[dict]:
    """
    Retourne [{date, close}] pour l'instrument sur la période.
    Cache SQLite — rafraîchit si les données datent de plus de 6h.
    """
    _ensure_cache_table(conn)
    inst = instrument.upper()
    days = PERIOD_DAYS.get(period, 370)
    date_from = (datetime.utcnow().date() - timedelta(days=days)).isoformat()

    # Check cache freshness
    cache_ok = False
    if not force_refresh:
        newest = conn.execute(
            "SELECT fetched_at FROM price_history_cache WHERE instrument=? ORDER BY fetched_at DESC LIMIT 1",
            (inst,)
        ).fetchone()
        if newest:
            try:
                age_h = (datetime.utcnow() - datetime.fromisoformat(str(newest[0])[:19])).total_seconds() / 3600
                cache_ok = age_h < 6.0
            except Exception:
                pass

    if not cache_ok:
        rows = _fetch_yf(inst, days + 30)
        if rows:
            conn.executemany(
                "INSERT OR REPLACE INTO price_history_cache (instrument, date, close) VALUES (?,?,?)",
                [(inst, r["date"], r["close"]) for r in rows]
            )
            conn.commit()

    # Read from cache
    rows_db = conn.execute(
        "SELECT date, close FROM price_history_cache WHERE instrument=? AND date>=? ORDER BY date ASC",
        (inst, date_from)
    ).fetchall()
    return [{"date": r[0], "close": r[1]} for r in rows_db]


def calibrate_intercept(
    conn,
    instrument: str,
    model_pips_at_ref: float,
    ref_date: Optional[str] = None,
) -> Optional[float]:
    """
    Calcule l'intercept = real_price - model_pips × pip_to_price au point de référence.
    Si ref_date non fourni, utilise il y a 30 jours.
    """
    from services.instrument_models import INSTRUMENT_MODELS
    meta = INSTRUMENT_MODELS.get(instrument.upper(), {})
    pip_to_price = meta.get("pip_to_price", 0.0001)

    if ref_date is None:
        ref_date = (datetime.utcnow().date() - timedelta(days=30)).isoformat()

    # Find nearest price to ref_date
    row = conn.execute(
        """SELECT date, close FROM price_history_cache
           WHERE instrument=? AND date<=? ORDER BY date DESC LIMIT 1""",
        (instrument.upper(), ref_date)
    ).fetchone()
    if not row:
        # Try fetching
        history = get_price_history(conn, instrument, "3mo", force_refresh=True)
        row = conn.execute(
            "SELECT date, close FROM price_history_cache WHERE instrument=? AND date<=? ORDER BY date DESC LIMIT 1",
            (instrument.upper(), ref_date)
        ).fetchone()
    if not row:
        return None

    real_price = float(row[1])
    intercept  = real_price - model_pips_at_ref * pip_to_price
    return round(intercept, 6)
