import datetime
from nile.api.v1 import (datetime as nd)

MODERATION_MAX_DELAY = 7

YEAR_DAYS = 364
MONTH_DAYS = 28
WEEK_DAYS = 7
ONE_DAY = 1


def _get_dates_to_update(run_isodate, period):
    if period <= 0 or not float(period).is_integer():
        raise ValueError("period {} is not a positive integer".format(period))

    end_date = nd.Datetime.from_iso(run_isodate).date()
    start_date = end_date - datetime.timedelta(days=period)
    today = datetime.date.today()
    if end_date > today:
        raise ValueError("future run_isodate={}, according to today={}".format(end_date, today))

    isodates = [start_date.isoformat(), ]
    if end_date == today:
        for numday in range(1, period):
            date = start_date + datetime.timedelta(days=numday)
            isodates.append(date.isoformat())
    return isodates


def dates_to_update(run_isodates, period=MODERATION_MAX_DELAY):
    dates = set()
    for run_isodate in run_isodates:
        dates.update(_get_dates_to_update(run_isodate, period))
    return sorted(dates)


def aggregation_period(scale):
    if scale == 'd':
        return ONE_DAY
    if scale == 'w':
        return WEEK_DAYS
    if scale == 'm':
        return MONTH_DAYS
    if scale == 'y':
        return YEAR_DAYS
    raise ValueError('got scale {} (expected d, w, m or y)'.format(scale))


def aggregation_dates(last_isodate, scale):
    period = aggregation_period(scale)
    assert period > 0

    aggregation_delta = datetime.timedelta(days=period)
    date_last = nd.Datetime.from_iso(last_isodate).date()
    date_end = date_last + datetime.timedelta(days=1)
    date_start = date_end - aggregation_delta
    return map(lambda b: b.decode('utf8'), nd.date_range(date_start, date_last, stringify=True))


def aggregation_dates_str(last_isodate, scale):
    period = aggregation_period(scale)
    assert period > 0

    aggregation_delta = datetime.timedelta(days=period)
    date_last = nd.Datetime.from_iso(last_isodate).date()
    date_end = date_last + datetime.timedelta(days=1)
    date_start = date_end - aggregation_delta
    if period == ONE_DAY:
        return date_start.isoformat()
    return '{' + date_start.isoformat() + '..' + date_last.isoformat() + '}'
