"""
Определение текущей даты, времени, дня недели и торговой сессии MOEX.

Функции:
    get_market_time — полная информация о текущем рыночном времени
    is_trading_session — проверка, идёт ли торговая сессия
    session_name — название текущей сессии
    trading_day_phase — фаза торгового дня
    is_holiday — проверка на праздничный/выходной день
"""

from dataclasses import dataclass
from datetime import datetime, time, timedelta, timezone
from typing import Optional

# Timezone
try:
    import pytz
    MSK = pytz.timezone("Europe/Moscow")
except ImportError:
    MSK = timezone(timedelta(hours=3))

# Торговые часы MOEX (МСК)
MORNING_START = time(6, 50)
MORNING_END = time(9, 50)
MAIN_AUCTION_START = time(9, 50)
MAIN_START = time(10, 0)
MAIN_END = time(18, 55)
CLOSING_AUCTION_START = time(18, 55)
CLOSING_AUCTION_END = time(18, 59, 30)
EVENING_START = time(19, 0)
EVENING_END = time(23, 50)

# Праздничные дни 2026 (основные нерабочие дни РФ + переносы)
HOLIDAYS_2026 = {
    "2026-01-01", "2026-01-02", "2026-01-03", "2026-01-04",
    "2026-01-05", "2026-01-06", "2026-01-07", "2026-01-08",
    "2026-02-23", "2026-02-24",
    "2026-03-07", "2026-03-08", "2026-03-09",
    "2026-05-01", "2026-05-02", "2026-05-03",
    "2026-05-09", "2026-05-10", "2026-05-11",
    "2026-06-12", "2026-06-13", "2026-06-14",
    "2026-11-03", "2026-11-04",
    "2026-12-31",
}

WEEKDAY_NAMES_RU = {
    0: "понедельник",
    1: "вторник",
    2: "среда",
    3: "четверг",
    4: "пятница",
    5: "суббота",
    6: "воскресенье",
}

WEEKDAY_SHORT_RU = {
    0: "пн",
    1: "вт",
    2: "ср",
    3: "чт",
    4: "пт",
    5: "сб",
    6: "вс",
}


@dataclass
class MarketTime:
    """Текущее рыночное время MOEX."""

    # Дата/время
    now_msk: datetime
    date_str: str           # "2026-06-20"
    time_str: str           # "15:30:45"
    weekday: int            # 0-6 (0=пн)
    weekday_ru: str         # "пятница"
    weekday_short: str      # "пт"

    # Сессия
    is_trading_day: bool    # биржа работает
    is_holiday: bool        # праздник/выходной
    session: str            # "основная" / "утренняя" / "вечерняя" / "закрыто"
    session_phase: str      # "аукцион открытия" / "торги" / "аукцион закрытия" / "закрыто"

    # Период
    is_month_end: bool      # последний торговый день месяца
    is_week_end: bool       # пятница
    days_to_month_end: int  # сколько торговых дней до конца месяца

    # Рынок
    volatility_bias: str    # "normal" / "elevated" / "reduced"
    confidence_adjust: int  # корректировка confidence (±%)
    comment: str            # текстовый комментарий


def is_weekend(dt: datetime) -> bool:
    """Суббота или воскресенье."""
    return dt.weekday() >= 5


def is_holiday_date(dt: datetime) -> bool:
    """Проверка на праздничный день."""
    return dt.strftime("%Y-%m-%d") in HOLIDAYS_2026


def is_trading_day(dt: datetime) -> bool:
    """Является ли день торговым."""
    if is_weekend(dt):
        return False
    if is_holiday_date(dt):
        return False
    return True


def get_session(now: datetime) -> tuple[str, str, bool]:
    """
    Определить текущую сессию.

    Returns:
        (session, phase, is_trading) — названия сессии, фазы и флаг активности торгов.
    """
    t = now.time()

    if MORNING_START <= t < MORNING_END:
        return "утренняя", "торги", True
    elif MAIN_AUCTION_START <= t < MAIN_START:
        return "основная", "аукцион открытия", True
    elif MAIN_START <= t < MAIN_END:
        return "основная", "торги", True
    elif CLOSING_AUCTION_START <= t <= CLOSING_AUCTION_END:
        return "основная", "аукцион закрытия", True
    elif EVENING_START <= t <= EVENING_END:
        return "вечерняя", "торги", True
    else:
        return "закрыто", "закрыто", False


def get_volatility_bias(session: str, weekday: int, is_month_end: bool) -> str:
    """Определить смещение волатильности."""
    if session == "утренняя":
        return "reduced"
    if session == "вечерняя":
        return "reduced"
    if session == "основная" and weekday == 4:  # пятница
        return "elevated"
    if is_month_end:
        return "elevated"
    if weekday == 0:  # понедельник
        return "elevated"
    return "normal"


def get_confidence_adjust(session: str, session_phase: str, weekday: int) -> int:
    """Корректировка confidence сигнала в зависимости от времени."""
    adj = 0

    if session == "утренняя":
        adj -= 10
    elif session == "вечерняя":
        adj -= 10

    if session_phase == "аукцион открытия":
        adj -= 15
    elif session_phase == "аукцион закрытия":
        adj -= 10

    if weekday == 4:  # пятница — осторожность перед выходными
        adj -= 5

    return adj


def get_days_to_month_end(now: datetime) -> int:
    """Торговых дней до конца месяца."""
    days = 0
    current = now.date()
    # Найти последний день месяца
    if current.month == 12:
        next_month = current.replace(year=current.year + 1, month=1, day=1)
    else:
        next_month = current.replace(month=current.month + 1, day=1)
    last_day = next_month - timedelta(days=1)

    d = current + timedelta(days=1)
    while d <= last_day:
        if d.weekday() < 5 and d.strftime("%Y-%m-%d") not in HOLIDAYS_2026:
            days += 1
        d += timedelta(days=1)
    return days


def get_market_time(dt: Optional[datetime] = None) -> MarketTime:
    """
    Получить полную информацию о текущем рыночном времени MOEX.

    Args:
        dt: момент времени (по умолчанию — сейчас в MSK).

    Returns:
        MarketTime со всеми параметрами.
    """
    if dt is None:
        dt = datetime.now(MSK)
    elif dt.tzinfo is None:
        dt = dt.replace(tzinfo=MSK)

    date_str = dt.strftime("%Y-%m-%d")
    time_str = dt.strftime("%H:%M:%S")
    weekday = dt.weekday()
    trading = is_trading_day(dt)
    holiday = is_holiday_date(dt) or is_weekend(dt)
    session, phase, is_trading = get_session(dt)

    # Корректировка: если день не торговый — сессия закрыта
    if not trading:
        session = "закрыто"
        phase = "закрыто"

    is_month_end = dt.day >= 25 and is_trading_day(dt)
    days_to_end = get_days_to_month_end(dt)

    vol_bias = get_volatility_bias(session, weekday, is_month_end) if trading else "none"
    conf_adj = get_confidence_adjust(session, phase, weekday) if trading else 0

    # Комментарий
    comments = []
    if not trading:
        comments.append("биржа закрыта")
    elif session == "утренняя":
        comments.append("утренняя сессия — пониженная ликвидность")
    elif session == "вечерняя":
        comments.append("вечерняя сессия — пониженная ликвидность")
    elif phase == "аукцион открытия":
        comments.append("аукцион открытия — возможна волатильность, избегать входов")
    elif phase == "аукцион закрытия":
        comments.append("аукцион закрытия — формирование цены закрытия")
    if weekday == 4:
        comments.append("пятница — осторожность перед выходными")
    if weekday == 0:
        comments.append("понедельник — отыгрыш новостей выходных")
    if is_month_end:
        comments.append(f"конец месяца ({days_to_end} дн. до конца) — налоговый период")
    if holiday:
        comments.append("праздничный/выходной день")

    return MarketTime(
        now_msk=dt,
        date_str=date_str,
        time_str=time_str,
        weekday=weekday,
        weekday_ru=WEEKDAY_NAMES_RU.get(weekday, "?"),
        weekday_short=WEEKDAY_SHORT_RU.get(weekday, "?"),
        is_trading_day=trading,
        is_holiday=holiday,
        session=session,
        session_phase=phase,
        is_month_end=is_month_end,
        is_week_end=(weekday == 4),
        days_to_month_end=days_to_end,
        volatility_bias=vol_bias,
        confidence_adjust=conf_adj,
        comment="; ".join(comments) if comments else "торговый день",
    )


# ── CLI ───────────────────────────────────────────────────

if __name__ == "__main__":
    mt = get_market_time()
    print(f"Дата:     {mt.date_str} ({mt.weekday_ru}, {mt.weekday_short})")
    print(f"Время:    {mt.time_str} (MSK)")
    print(f"Торговый: {'да' if mt.is_trading_day else 'нет'}")
    print(f"Сессия:   {mt.session} / {mt.session_phase}")
    print(f"Волатильность: {mt.volatility_bias}")
    print(f"Confidence корректировка: {mt.confidence_adjust:+d}%")
    print(f"Конец месяца: {'да' if mt.is_month_end else 'нет'} ({mt.days_to_month_end} дн.)")
    print(f"Комментарий: {mt.comment}")
