import logging
from datetime import datetime
from typing import Any, Dict

import os

logger = logging.getLogger(__name__)


def refresh_movers_service(db) -> Dict[str, Any]:
    """Populate `db['movers']` with latest gainers/losers.

    This is used by background jobs. Implementation is intentionally
    defensive; on failure it stores empty lists.
    """

    try:
        from app.v1.services.zerolive.list import TopMoversFetcher  # type: ignore

        fetcher = TopMoversFetcher(db)
        gainers, losers = fetcher.fetch_top_movers()

        def _norm(arr):
            if not arr:
                return []
            out = []
            for item in arr:
                if isinstance(item, dict):
                    for k in ("symbol", "nse_symbol", "tradingsymbol", "ticker", "name"):
                        if item.get(k):
                            out.append(item.get(k))
                            break
                elif isinstance(item, str):
                    out.append(item)
            return out

        gainers = _norm(gainers)
        losers = _norm(losers)
    except Exception:
        logger.info("TopMoversFetcher not available or failed; using empty movers")
        gainers = []
        losers = []

    record = {
        "type": "latest",
        "fetched_at": datetime.utcnow().isoformat(),
        "gainers": gainers,
        "losers": losers,
    }

    db["movers"].update_one({"type": "latest"}, {"$set": record}, upsert=True)

    # Optional: lightweight history for audits/backfills
    try:
        hist = dict(record)
        hist["type"] = "snapshot"
        db["movers_history"].insert_one(hist)
    except Exception:
        pass

    return record


def refresh_live_movers_from_latest_movers(db) -> Dict[str, Any]:
    """Rebuild `live_movers` from the latest ET movers snapshot (DB-only).

    IMPORTANT:
    - Must NOT call Zerodha, ET scraping, or GPT.
    - Only resolves symbols against the existing `stocks` master list.
    - If a symbol is not found in `stocks`, it is skipped.
    """

    doc = db["movers"].find_one({"type": "latest"}) or {}
    gainers_raw = doc.get("gainers") if isinstance(doc, dict) else []
    losers_raw = doc.get("losers") if isinstance(doc, dict) else []

    def _norm_list(v: Any) -> list[str]:
        if not isinstance(v, list):
            return []
        out: list[str] = []
        for it in v:
            if not isinstance(it, str):
                continue
            sym = it.strip().upper()
            if sym and sym not in out:
                out.append(sym)
        return out

    gainers = _norm_list(gainers_raw)
    losers = _norm_list(losers_raw)

    # Optional cap to avoid huge writes if ET returns a long list.
    limit_per_side = int(os.getenv("LIVE_MOVERS_LIMIT_PER_SIDE", "200") or "200")
    if limit_per_side > 0:
        gainers = gainers[:limit_per_side]
        losers = losers[:limit_per_side]

    symbols = list(dict.fromkeys([*gainers, *losers]))
    if not symbols:
        # Nothing to build; clear live_movers to avoid showing stale data.
        try:
            db["live_movers"].delete_many({})
        except Exception:
            pass
        return {"ok": True, "built": 0, "skipped": 0, "timestamp": datetime.utcnow().isoformat()}

    # Resolve to stock_ids using stocks master.
    stocks = list(
        db["stocks"].find(
            {"symbol": {"$in": symbols}, "exchange": "NSE"},
            {"_id": 0, "symbol": 1, "stock_id": 1},
        )
    )
    by_symbol: Dict[str, str] = {}
    for s in stocks:
        sym = (s.get("symbol") or "").strip().upper()
        sid = s.get("stock_id")
        if sym and isinstance(sid, str) and sid.strip():
            by_symbol[sym] = sid.strip()

    now = datetime.utcnow()
    live_docs: list[Dict[str, Any]] = []
    skipped = 0

    rank = 1
    for sym in gainers:
        sid = by_symbol.get(sym)
        if not sid:
            skipped += 1
            continue
        live_docs.append({"stock_id": sid, "mover_type": "GAINER", "rank": rank, "last_updated": now})
        rank += 1

    rank = 1
    for sym in losers:
        sid = by_symbol.get(sym)
        if not sid:
            skipped += 1
            continue
        live_docs.append({"stock_id": sid, "mover_type": "LOSER", "rank": rank, "last_updated": now})
        rank += 1

    # Replace live_movers atomically-ish (delete then insert).
    try:
        db["live_movers"].delete_many({})
        if live_docs:
            db["live_movers"].insert_many(live_docs)
    except Exception:
        logger.exception("Failed to refresh live_movers from movers")

    return {"ok": True, "built": len(live_docs), "skipped": skipped, "timestamp": now.isoformat()}
