"""Market-regime guard.

Computes a simple, robust trend regime for a benchmark (SPY by default) from its
own moving averages, so the UI can warn against fighting the broad market — e.g.
going long while SPY is below its 200-day average. Result is cached in a
SystemSetting (JSON) and refreshed by a daily job; reads are cheap.

Regime is informational: it warns, it doesn't suppress setups.
"""

from __future__ import annotations

import json
import logging
from datetime import date, datetime, timedelta, timezone

from sqlalchemy.ext.asyncio import AsyncSession

from app.config import settings
from app.providers.alpaca import AlpacaOHLCVProvider
from app.services import settings_store
from app.services.admin_service import update_setting

logger = logging.getLogger(__name__)

KEY_REGIME = "market_regime"
BENCHMARK = "SPY"
_SMA_SHORT = 50
_SMA_LONG = 200


def _sma(values: list[float], window: int) -> float | None:
    if len(values) < window:
        return None
    return sum(values[-window:]) / window


def compute_regime(closes: list[float]) -> dict:
    """Derive a regime label from a benchmark's close series.

    bullish  = price above the 200-day and the 50-day confirms (50 ≥ 200)
    bearish  = price below the 200-day and the 50-day confirms (50 ≤ 200)
    neutral  = mixed / chop
    unknown  = not enough history
    """
    if not closes:
        return {"label": "unknown", "reason": "no benchmark data"}

    price = closes[-1]
    sma50 = _sma(closes, _SMA_SHORT)
    sma200 = _sma(closes, _SMA_LONG)

    if sma200 is None:
        # Fall back to the 50-day alone when we lack a full year of history.
        if sma50 is None:
            return {"label": "unknown", "reason": "insufficient history"}
        label = "bullish" if price > sma50 else "bearish"
        return {
            "label": label,
            "price": round(price, 2),
            "sma50": round(sma50, 2),
            "sma200": None,
            "pct_above_200": None,
            "reason": "based on 50-day only (under 200 bars)",
        }

    above_200 = price > sma200
    if above_200 and sma50 >= sma200:
        label = "bullish"
    elif not above_200 and sma50 <= sma200:
        label = "bearish"
    else:
        label = "neutral"

    return {
        "label": label,
        "price": round(price, 2),
        "sma50": round(sma50, 2),
        "sma200": round(sma200, 2),
        "pct_above_200": round((price / sma200 - 1.0) * 100, 2),
        "reason": None,
    }


async def update_market_regime(db: AsyncSession) -> dict:
    """Fetch the benchmark, compute the regime, and cache it. Job entrypoint."""
    if not settings.alpaca_api_key or not settings.alpaca_api_secret:
        return {"label": "unknown", "reason": "Alpaca keys not configured"}

    provider = AlpacaOHLCVProvider(settings.alpaca_api_key, settings.alpaca_api_secret)
    end = date.today()
    start = end - timedelta(days=400)  # ~280 trading days → covers the 200-day SMA
    bars = await provider.fetch_ohlcv(BENCHMARK, start, end)
    closes = [b.close for b in sorted(bars, key=lambda b: b.date)]

    regime = compute_regime(closes)
    regime["benchmark"] = BENCHMARK
    regime["computed_at"] = datetime.now(timezone.utc).isoformat()

    await update_setting(db, KEY_REGIME, json.dumps(regime))
    logger.info(json.dumps({"event": "market_regime_updated", "regime": regime["label"]}))
    return regime


async def get_market_regime(db: AsyncSession) -> dict:
    """Return the cached regime (computed by the daily job)."""
    setting = await settings_store.get_setting(db, KEY_REGIME)
    if setting is None:
        return {"label": "unknown", "benchmark": BENCHMARK, "reason": "not computed yet"}
    try:
        return json.loads(setting.value)
    except (TypeError, ValueError):
        return {"label": "unknown", "benchmark": BENCHMARK, "reason": "corrupt cache"}
