"""
market_time.py

All schedule/time comparisons go through here so the rest of the code
never does naive string or local-time comparisons (a common source of
"incorrect market-time handling" bugs, especially around DST). Uses
zoneinfo (stdlib, Python 3.9+) with America/New_York from config.
"""

from datetime import datetime, time as dtime
from zoneinfo import ZoneInfo

from config_loader import get_config


def _tz():
    return ZoneInfo(get_config()["schedule"]["timezone"])


def now_et() -> datetime:
    return datetime.now(_tz())


def et_date(dt: datetime):
    """Trading-day date for an arbitrary (tz-aware) timestamp, e.g. a
    UTC exit_time/updated_at pulled from persisted state, converted to
    the exchange's own calendar day rather than UTC's."""
    return dt.astimezone(_tz()).date()


def _parse_time(hhmmss: str) -> dtime:
    h, m, s = [int(x) for x in hhmmss.split(":")]
    return dtime(h, m, s)


def _is_after(current: datetime, hhmmss: str) -> bool:
    return current.timetz().replace(tzinfo=None) >= _parse_time(hhmmss)


def is_premarket_scan_time() -> bool:
    cfg = get_config()["schedule"]
    return _is_after(now_et(), cfg["premarket_scan_time"])


def is_market_open() -> bool:
    cfg = get_config()["schedule"]
    current = now_et()
    return _is_after(current, cfg["market_open_time"]) and not _is_after(current, cfg["market_close_time"])


def is_past_market_open() -> bool:
    cfg = get_config()["schedule"]
    return _is_after(now_et(), cfg["market_open_time"])


def is_new_entries_cutoff() -> bool:
    cfg = get_config()["schedule"]
    return _is_after(now_et(), cfg["no_new_entries_after"])


def is_force_liquidate_time() -> bool:
    cfg = get_config()["schedule"]
    return _is_after(now_et(), cfg["force_liquidate_time"])


def is_market_closed_for_day() -> bool:
    cfg = get_config()["schedule"]
    return _is_after(now_et(), cfg["market_close_time"])


def is_weekday() -> bool:
    return now_et().weekday() < 5


def minutes_since_open() -> float:
    """[FEATURE 2026-09-05] Minutes since market_open_time today, for
    regime_engine.classify_regime()'s session_elapsed_minutes param (its
    OPENING_VOLATILITY window check). Negative before the open -- callers
    that only care about "are we in the opening window" should treat a
    negative value the same as regime_engine already treats None (can't
    be in the opening window if the session hasn't started)."""
    cfg = get_config()["schedule"]
    open_time = _parse_time(cfg["market_open_time"])
    current = now_et()
    open_dt = current.replace(hour=open_time.hour, minute=open_time.minute,
                               second=open_time.second, microsecond=0)
    return (current - open_dt).total_seconds() / 60.0
