from __future__ import annotations

from datetime import date, datetime, time, timedelta, timezone
from typing import Any
from zoneinfo import ZoneInfo

SHANGHAI = ZoneInfo("Asia/Shanghai")


def now_shanghai(clock: datetime | None = None) -> datetime:
    if clock is not None:
        if clock.tzinfo is None:
            return clock.replace(tzinfo=SHANGHAI)
        return clock.astimezone(SHANGHAI)
    return datetime.now(SHANGHAI)


def isoformat(value: datetime | None = None) -> str:
    current = now_shanghai(value)
    return current.isoformat(timespec="seconds")


def yyyymmdd(value: date | datetime | str | None = None) -> str:
    if value is None:
        return now_shanghai().strftime("%Y%m%d")
    if isinstance(value, str):
        digits = value.replace("-", "")[:8]
        if len(digits) != 8 or not digits.isdigit():
            raise ValueError(f"invalid trade_date: {value}")
        return digits
    if isinstance(value, datetime):
        return value.astimezone(SHANGHAI).strftime("%Y%m%d")
    return value.strftime("%Y%m%d")


def parse_trade_date(value: str) -> date:
    text = yyyymmdd(value)
    return date(int(text[:4]), int(text[4:6]), int(text[6:8]))


def session_phase(clock: datetime | None, is_open_day: bool) -> str:
    """pre | intradaily | lunch | eod | closed"""
    if not is_open_day:
        return "closed"
    current = now_shanghai(clock).time()
    if current < time(9, 15):
        return "pre"
    if current < time(11, 30) or (time(13, 0) <= current <= time(15, 5)):
        return "intraday"
    if current < time(13, 0):
        return "lunch"
    if current <= time(23, 40):
        return "eod"
    return "closed"


def add_days(trade_date: str, days: int) -> str:
    return (parse_trade_date(trade_date) + timedelta(days=days)).strftime("%Y%m%d")


def iter_yyyymmdd(start: str, end: str):
    cursor = parse_trade_date(start)
    last = parse_trade_date(end)
    if cursor > last:
        return
    while cursor <= last:
        yield cursor.strftime("%Y%m%d")
        cursor += timedelta(days=1)


def utc_timestamp(value: Any) -> str:
    if isinstance(value, datetime):
        return isoformat(value)
    return isoformat()
