import datetime

DATETIME_UNITS = {
    'second': datetime.timedelta(seconds=1),
    'seconds': datetime.timedelta(seconds=1),
    'minute': datetime.timedelta(minutes=1),
    'minutes': datetime.timedelta(minutes=1),
    'hour': datetime.timedelta(hours=1),
    'hours': datetime.timedelta(hours=1),
    'day': datetime.timedelta(days=1),
    'days': datetime.timedelta(days=1),
    'week': datetime.timedelta(days=7),
    'weeks': datetime.timedelta(days=7),
    'month': datetime.timedelta(days=30),
    'months': datetime.timedelta(days=30),
    'year': datetime.timedelta(days=365),
    'years': datetime.timedelta(days=365),
}

FORMAT_DATE_HEADER = '%a, %d %b %Y %H:%M:%S GMT'


def parse_duration(text: str) -> datetime.timedelta:
    (num_str, unit_str) = text.split(' ')
    num = float(num_str)
    unit = DATETIME_UNITS[unit_str]
    return unit * num


def parse_response_datetime(response) -> datetime.datetime:
    return datetime.datetime.strptime(
        response.headers['Date'],
        FORMAT_DATE_HEADER,
    ).replace(tzinfo=datetime.UTC)


NOW = datetime.datetime.now(datetime.UTC)
LOCAL_TIMEZONE = NOW.astimezone().tzinfo


def try_parse(text: str, fmt: str) -> datetime.datetime | None:
    try:
        time = datetime.datetime.strptime(text, fmt)  # noqa: DTZ007
    except ValueError:
        time = None
    return time


def parse_time(text: str, timezone=LOCAL_TIMEZONE) -> datetime.datetime:
    text = text.replace('\n', ' ')
    text = text.strip()

    time = try_parse(text, '%d %b %Y %I:%M:%S %p')
    time = time or try_parse(text, '%d %b, %Y @ %I:%M%p')
    if time is None and (m := try_parse(text, '%d %b @ %I:%M%p')):
        time = m.replace(year=NOW.year)

    if time is None:
        msg = 'Unknown format: ' + text
        raise RuntimeError(msg)

    if time.tzinfo is None:
        time = time.replace(tzinfo=timezone)

    if time.tzinfo is None:
        msg = 'Could not parse timezone: ' + text
        raise RuntimeError(msg)
    return time.astimezone(datetime.UTC)


def parse_date(text: str) -> datetime.date:
    text = text.strip()
    if dt := try_parse(text, '%d %B %Y'):
        return dt.date()
    if dt := try_parse(text, '%b %d, %Y'):
        return dt.date()
    if dt := try_parse(text, '%B %d, %Y'):
        return dt.date()
    if dt := try_parse(text, '%b %d'):
        return dt.date()
    msg = 'Unknown format: ' + text
    raise RuntimeError(msg)