# coding: utf-8
from __future__ import absolute_import

import uuid
from base64 import b64decode
from base64 import b64encode
from zipfile import ZIP_DEFLATED
from zipfile import ZipFile
import datetime

from six import BytesIO
from spyne import Fault


def create_attachment(filename, content):
    u"""Возвращает base64-кодированный zip-архив."""
    in_memory_file = BytesIO()
    with ZipFile(in_memory_file, 'w', ZIP_DEFLATED) as zip_file:
        zip_file.writestr(filename, content)
    return b64encode(in_memory_file.getvalue())


def unpack_attachment(content, filename):
    u"""Распаковывает base64-кодированный zip-архив."""
    with ZipFile(BytesIO(b64decode(content)), 'r') as zip_file:
        with zip_file.open(filename, 'r') as zip_content:
            return zip_content.read()


def as_date(string):
    if not string:
        return
    date_format = '%d.%m.%Y'
    strptime = datetime.datetime.strptime
    return strptime(string, date_format).date()


PERIOD_TODAY = 'today'
PERIOD_YESTERDAY = 'yesterday'
PERIOD_DATE = 'date'
PERIOD_PERIOD = 'period'
PERIOD_TYPES = (PERIOD_TODAY, PERIOD_YESTERDAY, PERIOD_DATE, PERIOD_PERIOD)


def validate_request(ctx, report_code, period):
    u"""Проверяет входные данные."""
    error = None
    period_type = period.IndexPeriodType

    if period_type == PERIOD_DATE and not period.IndexPeriodDate:
        error = (
            u'Для периода `date` должен быть указан параметр `IndexPeriodDate`'
        )

    elif period_type == PERIOD_PERIOD:
        if not all((
            period.IndexPeriodStartDate, period.IndexPeriodEndDate
        )):
            error = (
                u'Для периода `period` должны быть указаны параметры '
                u'`IndexPeriodStartDate` и `IndexPeriodEndDate`'
            )
        elif (
            as_date(period.IndexPeriodStartDate) >
            as_date(period.IndexPeriodEndDate)
        ):
            error = (
                u'Дата начала периода позднее, чем дата окончания.'
            )

    if error is not None:
        raise Fault(faultstring=error)


def get_report_period(period_type, **kwargs):
    u"""Возвращает период дат отчета в зависимости от типа запроса."""
    assert period_type in PERIOD_TYPES, period_type

    result = {}
    if period_type == PERIOD_TODAY:
        today = datetime.date.today()
        result['date_from'] = result['date_to'] = today

    elif period_type == PERIOD_YESTERDAY:
        today = datetime.date.today()
        yesterday = today - datetime.timedelta(days=1)
        result['date_from'] = result['date_to'] = yesterday

    elif period_type == PERIOD_DATE:
        assert kwargs.get('period_date')
        result['date_from'] = result['date_to'] = kwargs['period_date']

    elif period_type == PERIOD_PERIOD:
        result['date_from'] = kwargs['date_from']
        result['date_to'] = kwargs['date_to']

    return result


def create_report_uid():
    u"""Возвращает идентификатор формирующегося отчета."""
    return str(uuid.uuid4())


def create_content_params(ctx, report_code, param, period,
                          report_uid=None, file_name=None):
    u"""Возвращает параметры для формирования отчета."""

    res = dict(
        ctx=ctx,
        report_code=report_code,
        period_type=period.IndexPeriodType,
        param=param,
        report_uid=report_uid,
        file_name=file_name,
    )
    res.update(
        get_report_period(
            period.IndexPeriodType,
            period_date=as_date(period.IndexPeriodDate),
            date_from=as_date(period.IndexPeriodStartDate),
            date_to=as_date(period.IndexPeriodEndDate),
        ),
    )
    return res


def validate_get_builded_index_request(ctx, report_uid):
    u"""Проверка параметров запроса сформированных данных."""

    error = None

    if not report_uid:
        error = (
            u'Должен быть указан идентификатор запроса формирования данных '
            u'в параметре `IndexIdent`.'
        )
    elif len(report_uid) != 36:
        error = (
            u'Ожидается идентификатор из 36-ти знаков.'
        )

    if error is not None:
        raise Fault(faultstring=error)
