"""User-visible timestamps — always Asia/Shanghai (北京时间)."""

from __future__ import annotations

import re
from datetime import datetime, timezone
from zoneinfo import ZoneInfo

_CALENDAR_DATE_RE = re.compile(r"^\d{4}-\d{2}-\d{2}$")

BEIJING_TZ = ZoneInfo("Asia/Shanghai")
OPERATOR_TZ = BEIJING_TZ


def utc_now() -> datetime:
    return datetime.now(timezone.utc)


def ensure_utc(dt: datetime) -> datetime:
    if dt.tzinfo is None:
        return dt.replace(tzinfo=timezone.utc)
    return dt.astimezone(timezone.utc)


def to_naive_utc(dt: datetime) -> datetime:
    return ensure_utc(dt).replace(tzinfo=None)


def to_beijing(dt: datetime) -> datetime:
    """Convert aware or naive-UTC datetime to Beijing wall clock."""
    if dt.tzinfo is None:
        dt = dt.replace(tzinfo=timezone.utc)
    return dt.astimezone(BEIJING_TZ)


def format_beijing(dt: datetime | None = None, *, with_label: bool = True) -> str:
    """Format datetime for operators (default: now in 北京时间)."""
    when = to_beijing(dt or datetime.now(timezone.utc))
    text = when.strftime("%Y-%m-%d %H:%M:%S")
    return f"{text} 北京时间" if with_label else text


def format_beijing_now() -> str:
    return format_beijing(with_label=True)


def operator_wall(dt: datetime) -> tuple[int, int, int, int, int]:
    """Cron wall-clock components in operator TZ: minute, hour, day, month, dow (0=Sunday)."""
    bj = to_beijing(ensure_utc(dt))
    return (
        bj.minute,
        bj.hour,
        bj.day,
        bj.month,
        (bj.weekday() + 1) % 7,
    )


def parse_operator_calendar_date(raw: str) -> str:
    """Validate YYYY-MM-DD for operator (Beijing) calendar day filters."""
    text = (raw or "").strip()[:10]
    if not _CALENDAR_DATE_RE.match(text):
        raise ValueError(f"日期格式须为 YYYY-MM-DD，收到: {raw!r}")
    try:
        datetime.fromisoformat(text)
    except ValueError as e:
        raise ValueError(f"日期格式须为 YYYY-MM-DD，收到: {raw!r}") from e
    return text


def beijing_day_range_utc(date_yyyy_mm_dd: str) -> tuple[datetime, datetime]:
    """Inclusive Beijing calendar day → naive UTC (start, end) for MySQL filters."""
    base = datetime.fromisoformat(parse_operator_calendar_date(date_yyyy_mm_dd))
    start_bj = base.replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=BEIJING_TZ)
    end_bj = base.replace(hour=23, minute=59, second=59, microsecond=999999, tzinfo=BEIJING_TZ)
    return (
        start_bj.astimezone(timezone.utc).replace(tzinfo=None),
        end_bj.astimezone(timezone.utc).replace(tzinfo=None),
    )
