import typing
import datetime as _datetime
import dateutil.relativedelta
from .common import *

def iso8601(dt: _datetime.datetime, timespec: TimeSpec=TimeSpec.NORMAL
        ) -> str:
    dt_format = dt.strftime(ISO8601_FORMAT_DT)
    tz_format = dt.strftime(ISO8601_FORMAT_TZ)

    ms_format = ""
    if timespec == TimeSpec.MILLISECOND:
        ms_format = ".%s" % str(int(dt.microsecond/1000)).zfill(3)

    return "%s%s%s" % (dt_format, ms_format, tz_format)
def iso8601_now(timespec: TimeSpec=TimeSpec.NORMAL) -> str:
    return iso8601(utcnow(), timespec)

def datetime_human(dt: _datetime.datetime, timespec: TimeSpec=TimeSpec.NORMAL):
    date = _datetime.datetime.strftime(dt, DATE_HUMAN)
    time = _datetime.datetime.strftime(dt, TIME_HUMAN)
    if timespec == TimeSpec.MILLISECOND:
        time += ".%s" % str(int(dt.microsecond/1000)).zfill(3)
    return "%s %s" % (date, time)
def date_human(dt: _datetime.datetime, timespec: TimeSpec=TimeSpec.NORMAL):
    return _datetime.datetime.strftime(dt, DATE_HUMAN)

def time_unit(seconds: int) -> typing.Tuple[int, str]:
    since = None
    unit = None
    if seconds >= TIME_WEEK:
        since = seconds/TIME_WEEK
        unit = "week"
    elif seconds >= TIME_DAY:
        since = seconds/TIME_DAY
        unit = "day"
    elif seconds >= TIME_HOUR:
        since = seconds/TIME_HOUR
        unit = "hour"
    elif seconds >= TIME_MINUTE:
        since = seconds/TIME_MINUTE
        unit = "minute"
    else:
        since = seconds
        unit = "second"
    since = int(since)
    if since > 1:
        unit = "%ss" % unit # pluralise the unit
    return (since, unit)

def to_pretty_time(total_seconds: int, max_units: int=UNIT_MINIMUM,
        direction: typing.Optional[RelativeDirection]=None) -> str:
    if total_seconds < 1:
        return "0s"

    if not direction == None:
        now = utcnow()
        later = now
        mod = _datetime.timedelta(seconds=total_seconds)
        if direction == RelativeDirection.FORWARD:
            later += mod
        else:
            later -= mod

        dts = [later, now]
        relative = dateutil.relativedelta.relativedelta(max(dts), min(dts))
        years = relative.years
        months = relative.months
        weeks, days = divmod(relative.days, 7)
        hours = relative.hours
        minutes = relative.minutes
        seconds = relative.seconds
    else:
        years, months    = 0, 0
        weeks, days      = divmod(total_seconds, SECONDS_WEEKS)
        days, hours      = divmod(days, SECONDS_DAYS)
        hours, minutes   = divmod(hours, SECONDS_HOURS)
        minutes, seconds = divmod(minutes, SECONDS_MINUTES)

    out: typing.List[str] = []
    if years and len(out) < max_units:
        out.append("%dy" % years)
    if months and len(out) < max_units:
        out.append("%dmo" % months)
    if weeks and len(out) < max_units:
        out.append("%dw" % weeks)
    if days and len(out) < max_units:
        out.append("%dd" % days)
    if hours and len(out) < max_units:
        out.append("%dh" % hours)
    if minutes and len(out) < max_units:
        out.append("%dmi" % minutes)
    if seconds and len(out) < max_units:
        out.append("%ds" % seconds)

    return " ".join(out)

def to_pretty_since(total_seconds: int, max_units: int=UNIT_MINIMUM
        ) -> str:
    return to_pretty_time(total_seconds, max_units, RelativeDirection.BACKWARD)
def to_pretty_until(total_seconds: int, max_units: int=UNIT_MINIMUM
        ) -> str:
    return to_pretty_time(total_seconds, max_units, RelativeDirection.FORWARD)