import logging

from fastapi import APIRouter, HTTPException, Query
from pydantic import BaseModel
from typing import List, Optional

router = APIRouter(prefix="/api/watchlist", tags=["watchlist"])
logger = logging.getLogger(__name__)

# Intentionally duplicated from market_data.py to keep this system fully decoupled
# from the other instrument-list mechanisms (market_watchlist, INSTRUMENT_MODELS,
# instruments.json, options_vol watchlist-tickers).
_QUOTE_TYPE_TO_ASSET_CLASS = {
    "CURRENCY": "forex",
    "FUTURE": "energy",
    "INDEX": "indices",
    "ETF": "etfs",
    "EQUITY": "equities",
    "MUTUALFUND": "etfs",
}


@router.get("/")
def list_watchlist():
    from services.database import get_instruments_watchlist
    return get_instruments_watchlist()


def _saxo_quote(saxo_symbol: str) -> Optional[dict]:
    """Try Saxo's own chart history for price/change/volatility — avoids the unadjusted-
    roll artifact continuous futures tickers (BZ=F, CL=F, GC=F...) have on yfinance.
    Untested against a live account; any failure here is routine, not an error — the
    caller falls back to yfinance."""
    from services.database import get_saxo_catalog_by_symbol
    from services.saxo_client import get_saxo_quote_with_volatility
    entry = get_saxo_catalog_by_symbol(saxo_symbol)
    asset_type = entry["asset_type"] if entry else "FxSpot"
    try:
        return get_saxo_quote_with_volatility(saxo_symbol, asset_type)
    except Exception as e:
        # WARNING, not INFO — the System Logs page only captures WARNING+ (confirmed
        # 2026-07-23: an earlier INFO-level version of this message never showed up there).
        logger.warning(f"[watchlist/quotes] Saxo quote failed for '{saxo_symbol}' ({asset_type}), falling back to yfinance: {e}")
        return None


@router.get("/quotes")
def watchlist_quotes():
    from services.database import get_instruments_watchlist
    from services.data_fetcher import get_quote_with_volatility
    items = []
    for row in get_instruments_watchlist():
        q = None
        if row.get("saxo_quote_symbol"):
            q = _saxo_quote(row["saxo_quote_symbol"])
        if q is None:
            q = get_quote_with_volatility(row["ticker"]) or {}
        items.append({
            **row,
            "price": q.get("price"),
            "change_pct": q.get("change_pct"),
            "volatility_pct": q.get("volatility_pct"),
            "volatility_change_pct": q.get("volatility_change_pct"),
            "quote_source": q.get("source", "yfinance"),
        })
    return {"items": items}


@router.get("/curve-regimes")
def watchlist_curve_regimes():
    """Cached Curve Regime classification (services.curve_regime) per Watchlist instrument —
    refreshed on the same cadence as the wavelet cache (services.wavelet_scheduler), not a
    live call. Every Watchlist ticker is represented, with a null-regime placeholder for any
    never successfully classified yet."""
    from services.database import get_all_curve_regime_cache
    return {"items": get_all_curve_regime_cache()}


_HISTORY_PERIODS = {
    "1w":  {"yf": "5d",  "days": 7},
    "1m":  {"yf": "1mo", "days": 30},
    "3m":  {"yf": "3mo", "days": 90},
    "6m":  {"yf": "6mo", "days": 180},
    "1y":  {"yf": "1y",  "days": 365},
    "5y":  {"yf": "5y",  "days": 1825},
    "max": {"yf": "max", "days": 3650},
}


@router.get("/history/{ticker}")
def watchlist_history(ticker: str, period: str = Query("3m")):
    """Daily close series for the Watchlist card's chart — Saxo-sourced if this
    instrument has a saxo_quote_symbol link (see saxo-quote-link below), yfinance
    otherwise. Same source-of-truth split as /quotes above, just returning a series
    instead of a single latest point."""
    from services.database import get_instruments_watchlist, get_saxo_catalog_by_symbol
    from services.saxo_client import get_price_history
    import yfinance as yf

    ticker = ticker.strip().upper()
    spec = _HISTORY_PERIODS.get(period.lower(), _HISTORY_PERIODS["3m"])

    row = next((r for r in get_instruments_watchlist() if r["ticker"] == ticker), None)
    saxo_quote_symbol = row.get("saxo_quote_symbol") if row else None

    if saxo_quote_symbol:
        try:
            entry = get_saxo_catalog_by_symbol(saxo_quote_symbol)
            asset_type = entry["asset_type"] if entry else "FxSpot"
            bars = get_price_history(saxo_quote_symbol, asset_type, days=spec["days"])
            return {"ticker": ticker, "source": "saxo", "bars": [{"date": b["date"], "close": b["close"]} for b in bars]}
        except Exception as e:
            logger.info(f"[watchlist/history] Saxo history failed for '{saxo_quote_symbol}', falling back to yfinance: {e}")

    try:
        hist = yf.Ticker(ticker).history(period=spec["yf"], interval="1d", auto_adjust=True)
        hist = hist.dropna(subset=["Close"])
        bars = [{"date": idx.strftime("%Y-%m-%d"), "close": round(float(c), 6)} for idx, c in hist["Close"].items()]
        return {"ticker": ticker, "source": "yfinance", "bars": bars}
    except Exception as e:
        return {"ticker": ticker, "source": "none", "bars": [], "error": str(e)}


@router.post("/{ticker}")
def add_ticker(ticker: str):
    """Adds a tracked instrument. yfinance validation is best-effort, not a gate — an
    instrument can be entirely Saxo-sourced (both saxo_option_symbol and
    saxo_quote_symbol linked, see the two PUT .../saxo-*-link endpoints below) with no
    yfinance equivalent at all, so a ticker yfinance doesn't recognize is still added,
    just without a name/asset_class lookup or an initial price to report back."""
    from services.data_fetcher import get_quote
    from services.database import get_instruments_watchlist, add_instrument_watchlist
    import yfinance as yf
    ticker = ticker.strip().upper()
    if any(row["ticker"] == ticker for row in get_instruments_watchlist()):
        raise HTTPException(409, f"'{ticker}' is already in the watchlist")

    q = get_quote(ticker)
    name = ticker
    asset_class = "unknown"
    if q and q.get("price"):
        try:
            info = yf.Ticker(ticker).fast_info
            name = getattr(info, "long_name", None) or getattr(info, "short_name", None) or ticker
            quote_type = getattr(info, "quote_type", "") or ""
            asset_class = _QUOTE_TYPE_TO_ASSET_CLASS.get(quote_type.upper(), "unknown")
        except Exception:
            pass

    add_instrument_watchlist(ticker, name, asset_class)
    return {
        "ticker": ticker, "name": name, "asset_class": asset_class,
        "price": q.get("price") if q else None,
        "yfinance_recognized": bool(q and q.get("price")),
    }


@router.delete("/{ticker}")
def remove_ticker(ticker: str):
    from services.database import remove_instrument_watchlist
    remove_instrument_watchlist(ticker.strip().upper())
    return {"removed": ticker.strip().upper()}


class RenameBody(BaseModel):
    name: str


@router.put("/{ticker}/name")
def rename_ticker(ticker: str, body: RenameBody):
    """Free-form display name — no longer tied to yfinance's long_name lookup, since an
    instrument can now be entirely Saxo-sourced."""
    from services.database import rename_instrument_watchlist
    if not body.name.strip():
        raise HTTPException(400, "Name cannot be empty")
    ok = rename_instrument_watchlist(ticker, body.name)
    if not ok:
        raise HTTPException(404, f"'{ticker}' is not in the instruments watchlist")
    return {"ticker": ticker.strip().upper(), "name": body.name.strip()}


class ReorderBody(BaseModel):
    tickers: List[str]


@router.put("/reorder")
def reorder(body: ReorderBody):
    from services.database import reorder_instruments_watchlist
    reorder_instruments_watchlist(body.tickers)
    return {"ok": True}


class SaxoLinkBody(BaseModel):
    saxo_symbol: str | None = None


@router.put("/{ticker}/saxo-option-link")
def set_saxo_option_link(ticker: str, body: SaxoLinkBody):
    """Link this tracked instrument to the Saxo symbol whose OPTIONS CHAIN Options Lab
    should analyze for it (or pass null to unlink) — e.g. CL=F -> MCL:XCME. Adds the
    symbol to services.saxo_scheduler's watchlist automatically if it wasn't already
    there, so that chain starts getting snapshotted."""
    from services.database import set_instrument_watchlist_saxo_option_symbol
    ok = set_instrument_watchlist_saxo_option_symbol(ticker, body.saxo_symbol)
    if not ok:
        raise HTTPException(404, f"'{ticker}' is not in the instruments watchlist")
    return {"ticker": ticker.strip().upper(), "saxo_option_symbol": (body.saxo_symbol or "").strip().upper() or None}


@router.put("/{ticker}/saxo-quote-link")
def set_saxo_quote_link(ticker: str, body: SaxoLinkBody):
    """Link this tracked instrument to the Saxo symbol used to price it in the Cockpit
    (or pass null to unlink) — an accurate broker spot/futures feed instead of
    yfinance's unadjusted continuous-futures tickers. Independent of the options link
    above; often a different Saxo instrument."""
    from services.database import set_instrument_watchlist_saxo_quote_symbol
    ok = set_instrument_watchlist_saxo_quote_symbol(ticker, body.saxo_symbol)
    if not ok:
        raise HTTPException(404, f"'{ticker}' is not in the instruments watchlist")
    return {"ticker": ticker.strip().upper(), "saxo_quote_symbol": (body.saxo_symbol or "").strip().upper() or None}
