from io import BytesIO

from datetime import timedelta

from django.views.decorators.http import require_GET
from django.http import (
    HttpResponse,
    HttpResponseNotFound,
    HttpResponseServerError,
    HttpResponseBadRequest,
)
from django.utils.encoding import smart_str

from staff.person.models import Staff

from staff.lib.utils.qs_values import localize
from staff.lib.calendar import get_holidays
from staff.person_profile.errors import log_does_not_exist_staff_login

from staff.gap.base_forms import DistributeVacationsForm
from staff.gap.controllers.gap import GapCtl
from staff.gap.controllers.templates import TemplatesCtl
from staff.gap.controllers.utils import full_day_dates
from staff.gap.pdf import generate_vacations_pdf
from staff.gap.exceptions import GapError, GapViewError
from staff.gap.workflows.decorators import valid_gap_id

import logging
logger = logging.getLogger('staff.gap.views.vacation_views')


@require_GET
@valid_gap_id
def vacation_file(request, gap_id):
    try:
        gap = GapCtl().find_gap_by_id(int(gap_id))

        if not gap:
            return HttpResponseNotFound()

        workflow = gap['workflow']

        if workflow not in ['vacation', 'maternity']:
            return HttpResponseBadRequest()

        person = _get_person(gap['person_login'])
        if workflow == 'vacation':
            tag = 'vacation' + ('_selfpaid' if gap['is_selfpaid'] else '')
        else:
            tag = 'maternity'
        template = find_pdf_template(person, workflow, tag)

        if not template:
            return HttpResponseNotFound()

        if gap['full_day']:
            full_day_dates(gap, -1)

        holidays = next(iter(get_holidays(
            gap['date_from'],
            gap['date_to'],
            [person['office__city__geo_id']],
            out_mode='holidays'
        ).values()))

        _strip_gap_dates(gap, holidays)

        vacation = {
            'person_id': gap['person_id'],
            'date_from': gap['date_from'],
            'date_to': gap['date_to'],
        }

        return _return_pdf(person, template, [vacation])
    except Exception:
        logger.exception('Error generating vacation file for gap[%s]' % gap_id)
        return HttpResponseServerError()


@require_GET
def distribute_vacations(request, login):
    form = DistributeVacationsForm(request.GET)

    if not form.is_valid():
        logger.warning('Error distributing vacations, invalid form data: %s' % form.errors)
        return HttpResponseBadRequest()

    try:
        person = _get_person(login)
    except GapError:
        return HttpResponseNotFound()

    template = find_pdf_template(person, 'vacation', 'vacation')

    if not template:
        return HttpResponseNotFound()

    date_from = form.cleaned_data['date_from']
    days_to_spent = form.cleaned_data['days_to_spent']

    date_to = date_from + timedelta(weeks=(days_to_spent // 2 + 4))  # запас на всякий случай
    holidays = next(iter(get_holidays(
        date_from,
        date_to,
        [person['office__city__geo_id']],
        out_mode='holidays',
    ).values()))

    vacations = [v for v in _gaps_data(person['id'], holidays, days_to_spent)]

    return _return_pdf(person, template, vacations)


def _return_pdf(person, template, vacations):
    tmp_file = BytesIO()
    generate_vacations_pdf(tmp_file, template['template'], vacations)

    file_date = vacations[0]['date_from'].strftime('%Y.%m.%d')
    filename = ('filename=Отпуск (%s).%s.pdf' % (person['login'], file_date)).replace(' ', '_')

    response = HttpResponse(tmp_file, content_type='applicaton/pdf')
    response['Content-Disposition'] = smart_str('attachment;' + filename)
    return response


def _get_person(login):
    with log_does_not_exist_staff_login(logger=logger, message_params=[login], raise_e=GapViewError):
        return localize(Staff.objects.values(
            'id',
            'login',
            'first_name',
            'last_name',
            'first_name_en',
            'last_name_en',
            'office__city_id',
            'office__city__geo_id',
            'organization_id',
            'lang_ui',
        ).get(login=login))


def find_pdf_template(person, workflow, tag):
    return TemplatesCtl().find_one_not_strict(
        type='pdf',
        workflow=workflow,
        tag=tag,
        lang=person['lang_ui'] or 'ru',
        organization_id=person['organization_id'],
        city_id=person['office__city_id'],
    )


def _gaps_data(person_id, holidays, days_to_spent):
    buf = {'person_id': person_id, 'date_from': None, 'date_to': None}
    n = 1
    for day in holidays:
        if day['day-type'] != 'weekend':
            continue
        if n > days_to_spent:
            yield buf
            return
        date_from = day['date']
        if not buf['date_from']:
            buf = {'person_id': person_id, 'date_from': date_from, 'date_to': date_from}
        elif (date_from - buf['date_from']).days == 1:
            buf['date_to'] = buf['date_to'] + timedelta(days=1)
        else:
            yield buf
            buf = {'person_id': person_id, 'date_from': date_from, 'date_to': date_from}
        n += 1


def _strip_gap_dates(gap, calendar_days):
    holidays = [day['date'] for day in calendar_days if day['day-type'] == 'holiday']
    if not holidays:
        return
    date_from = gap['date_from'].date()
    date_to = gap['date_to'].date()

    for day in holidays:
        if day == date_from:
            date_from += timedelta(days=1)
        else:
            break

    for day in holidays[::-1]:
        if day == date_to:
            date_to -= timedelta(days=1)
        else:
            break

    if date_from > date_to:
        return

    gap['date_from'] = date_from
    gap['date_to'] = date_to
