# engine/data.py
from datetime import datetime, timezone
from pathlib import Path
import os
import threading

import pandas as pd
import yfinance as yf

ENGINE_ROOT = Path(__file__).resolve().parents[1]
HISTORY_DIR = ENGINE_ROOT / "storage" / "history"
ALLOW_PRICE_CACHE = os.getenv("ALLOW_PRICE_CACHE", "0").strip().lower() in {"1", "true", "yes"}

_LAST_PRICE: dict[str, dict[str, object]] = {}
_LAST_PRICE_LOCK = threading.Lock()


def _set_last_price(ticker: str, price: float, source: str):
    now = datetime.now(timezone.utc)
    with _LAST_PRICE_LOCK:
        _LAST_PRICE[ticker] = {"price": float(price), "source": source, "ts": now}


def get_price_snapshot(ticker: str) -> dict[str, object] | None:
    with _LAST_PRICE_LOCK:
        data = _LAST_PRICE.get(ticker)
        if not data:
            return None
        return dict(data)


def _get_last_live_price(ticker: str) -> float | None:
    with _LAST_PRICE_LOCK:
        data = _LAST_PRICE.get(ticker)
        if not data:
            return None
        if data.get("source") == "live":
            return float(data.get("price", 0))
        return None


def _cached_last_close(ticker: str) -> float | None:
    file = HISTORY_DIR / f"{ticker}.csv"
    if not file.exists():
        return None
    df = pd.read_csv(file)
    if df.empty or "Close" not in df.columns:
        return None
    return float(df["Close"].iloc[-1])


def fetch_live_price(ticker, allow_cache: bool | None = None):
    if allow_cache is None:
        allow_cache = ALLOW_PRICE_CACHE
    try:
        df = yf.download(
            ticker,
            period="1d",
            interval="1m",
            auto_adjust=True,
            progress=False,
            timeout=5,
        )
        if df is not None and not df.empty:
            price = float(df["Close"].iloc[-1])
            _set_last_price(ticker, price, "live")
            return price
    except Exception:
        pass

    if allow_cache:
        last_live = _get_last_live_price(ticker)
        if last_live is not None:
            return last_live

        cached = _cached_last_close(ticker)
        if cached is not None:
            _set_last_price(ticker, cached, "cache")
            return cached

    raise RuntimeError(f"No live data for {ticker}")
