"""Утилиты расписания работы бота (окно времени + дни недели)."""

from __future__ import annotations

import os
import re
from datetime import datetime, time
from typing import Iterable, Sequence
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError

# 0=Пн … 6=Вс (совпадает с datetime.weekday())
WEEKDAY_LABELS = {
    0: "Пн",
    1: "Вт",
    2: "Ср",
    3: "Чт",
    4: "Пт",
    5: "Сб",
    6: "Вс",
}

DEFAULT_WORK_DAYS = [0, 1, 2, 3, 4]
DEFAULT_WORK_FROM = "09:00"
DEFAULT_WORK_TO = "20:00"
RUN_MODE_MANUAL = "manual"
RUN_MODE_SCHEDULE = "schedule"

_TIME_RE = re.compile(r"^(\d{1,2}):(\d{2})$")


def get_bot_tz() -> ZoneInfo:
    name = (os.getenv("TZ") or "Europe/Kyiv").strip() or "Europe/Kyiv"
    try:
        return ZoneInfo(name)
    except ZoneInfoNotFoundError:
        # Старое имя зоны / образы без актуального tzdata
        try:
            return ZoneInfo("Europe/Kiev")
        except ZoneInfoNotFoundError:
            return ZoneInfo("UTC")


def now_in_bot_tz() -> datetime:
    return datetime.now(get_bot_tz())


def parse_hhmm(value: str | None) -> time | None:
    if not value:
        return None
    text = str(value).strip().replace(".", ":").replace("-", ":")
    m = _TIME_RE.match(text)
    if not m:
        return None
    hour, minute = int(m.group(1)), int(m.group(2))
    if hour > 23 or minute > 59:
        return None
    return time(hour=hour, minute=minute)


def format_hhmm(value: time | str | None, default: str = "09:00") -> str:
    if isinstance(value, time):
        return f"{value.hour:02d}:{value.minute:02d}"
    parsed = parse_hhmm(value) if value else None
    if parsed:
        return format_hhmm(parsed)
    return default


def normalize_work_days(days: Iterable[int] | None) -> list[int]:
    if days is None:
        return list(DEFAULT_WORK_DAYS)
    out: list[int] = []
    for d in days:
        try:
            di = int(d)
        except (TypeError, ValueError):
            continue
        if 0 <= di <= 6 and di not in out:
            out.append(di)
    out.sort()
    return out or list(DEFAULT_WORK_DAYS)


def format_work_days(days: Sequence[int] | None) -> str:
    norm = normalize_work_days(days)
    if norm == [0, 1, 2, 3, 4]:
        return "Пн–Пт"
    if norm == [0, 1, 2, 3, 4, 5, 6]:
        return "ежедневно"
    if norm == [5, 6]:
        return "Сб–Вс"
    return ", ".join(WEEKDAY_LABELS[d] for d in norm)


def validate_work_window(work_from: str, work_to: str) -> tuple[bool, str]:
    t_from = parse_hhmm(work_from)
    t_to = parse_hhmm(work_to)
    if not t_from or not t_to:
        return False, "Время нужно в формате ЧЧ:ММ, например 09:00"
    if t_from >= t_to:
        return (
            False,
            "Окно должно быть в пределах одного дня: время «с» меньше времени «до» "
            "(например 09:00–20:00). Через полночь пока нельзя.",
        )
    return True, ""


def is_within_window(
    work_from: str | None,
    work_to: str | None,
    work_days: Sequence[int] | None,
    when: datetime | None = None,
) -> bool:
    """True, если when попадает в выбранные дни и [work_from, work_to)."""
    dt = when or now_in_bot_tz()
    if dt.tzinfo is None:
        dt = dt.replace(tzinfo=get_bot_tz())
    else:
        dt = dt.astimezone(get_bot_tz())

    days = normalize_work_days(work_days)
    if dt.weekday() not in days:
        return False

    t_from = parse_hhmm(work_from) or parse_hhmm(DEFAULT_WORK_FROM)
    t_to = parse_hhmm(work_to) or parse_hhmm(DEFAULT_WORK_TO)
    if not t_from or not t_to or t_from >= t_to:
        return False

    current = dt.timetz().replace(tzinfo=None) if hasattr(dt, "timetz") else dt.time()
    # сравниваем naive time
    cur = time(hour=dt.hour, minute=dt.minute, second=dt.second)
    return t_from <= cur < t_to


def mode_label(run_mode: str | None) -> str:
    if (run_mode or RUN_MODE_MANUAL) == RUN_MODE_SCHEDULE:
        return "По расписанию"
    return "Вручную"


def schedule_summary(settings: dict) -> str:
    mode = mode_label(settings.get("run_mode"))
    days = format_work_days(settings.get("work_days"))
    wf = format_hhmm(settings.get("work_from"), DEFAULT_WORK_FROM)
    wt = format_hhmm(settings.get("work_to"), DEFAULT_WORK_TO)
    inside = is_within_window(wf, wt, settings.get("work_days"))
    now_s = now_in_bot_tz().strftime("%H:%M")
    window_state = "сейчас внутри окна" if inside else "сейчас вне окна"
    return (
        f"Режим: {mode}\n"
        f"Окно: {days}, {wf}–{wt}\n"
        f"Сейчас ({now_s}): {window_state}"
    )
