import dateutil.parser
from datetime import (
    datetime,
    timedelta,
    time,
)
from pytz import timezone
import pymongo
import logging

from django.conf import settings
from django.views.decorators.http import require_GET
from django.http import (
    HttpResponseBadRequest,
    HttpResponseServerError,
    HttpResponseForbidden,
)

from staff.lib.decorators import responding_json, available_for_external

from staff.gap.controllers.gap import (
    GapCtl,
    GapQueryBuilder,
)
from staff.gap.workflows import ALL_WORKFLOWS_ORDER
from staff.gap.permissions import can_see_issues, get_accessible_logins
from staff.gap.workflows.utils import workflows_for_ui
from staff.gap.controllers.utils import (
    get_gap_days,
    full_day_dates,
    gap_dates_from_utc,
    get_unique_issues,
    collect_confirmed_by,
    is_chief_of,
)
from staff.gap.controllers.staff_utils import collect_forms_person_ids
from staff.gap.controllers.unconfirmed import unconfirmed_for_ui
from staff.gap.permissions import can_see_all_gaps
from staff.gap.workflows.choices import LIMITED_STATES_HISTORY

logger = logging.getLogger('staff.gap.views.gaps_history_view')


@responding_json
@require_GET
@available_for_external
def gaps_history(request):
    observer = request.user

    observer_person = observer.get_profile()
    observer_login = observer_person.login
    observer_id = observer_person.id
    observer_tz = timezone(observer_person.tz)

    login = request.GET.get('login', observer_login)

    if not get_accessible_logins(observer_person, [login]):
        logger.info('%s has no access to %s gap', observer_login, login)
        return HttpResponseForbidden()

    try:
        limit = int(request.GET.get('limit', 10))
        page = int(request.GET.get('page', 1))

        if limit < 1 or limit > 100:
            limit = 10
        if page < 1:
            page = 1
    except (TypeError, ValueError):
        return HttpResponseBadRequest()

    if 'workflow' in request.GET:
        workflows = request.GET.getlist('workflow')
    else:
        workflows = ALL_WORKFLOWS_ORDER

    date_from = _get_date(request, 'date_from', datetime.utcnow() - timedelta(days=365))
    date_from = datetime.combine(date_from.date(), time.min)

    date_to = _get_date(request, 'date_to', datetime.utcnow() + timedelta(days=31))
    date_to = datetime.combine(date_to.date(), time.min)

    is_chief = is_chief_of(observer_login, login)

    gaps = []

    res = {
        'workflows': workflows_for_ui(),
        'gaps': [],
        'limit': limit,
        'page': page,
        'total': 0,
        'filter': {
            'login': login,
            'date_from': date_from,
            'date_to': date_to,
            'workflows': workflows,
        },
        'unconfirmed': unconfirmed_for_ui(
            observer.get_profile().id,
            observer_login=observer_login,
            target_login=login,
        ),
    }

    # STAFF-17109
    if login == settings.LOGIN_TIGRAN and observer_login != settings.LOGIN_TIGRAN:
        return res

    try:
        can_see_all = can_see_all_gaps(observer, [login])
        _gaps = list(_gaps_paged(can_see_all, login, date_from, date_to, workflows, limit, page - 1))
        confirmed_by = collect_confirmed_by(_gaps)
        forms_person_ids = collect_forms_person_ids(_gaps)
        for gap in _gaps:
            gap['issues'] = get_unique_issues(gap) if can_see_issues(gap, observer, login, is_chief=is_chief) else []

            if 'master_issue' in gap:
                del gap['master_issue']
            if 'slave_issues' in gap:
                del gap['slave_issues']

            if 'form_key' in gap:
                form_key = gap.get('form_key')
                if form_key and (observer_id in forms_person_ids.get(form_key, []) or observer.is_superuser):
                    gap['form_url'] = '/trip/?trip_uuid=%s' % form_key
                    event_type = 'trip'
                    if gap['workflow'] == 'conference_trip':
                        event_type = 'trip_conf'
                    elif gap['workflow'] == 'conference':
                        event_type = 'conf'
                    gap['trip_repeat_url'] = '/trip/?event_type=%s&uuid=%s' % (event_type, form_key)
                del gap['form_key']

            if gap.get('confirmed_by_id'):
                gap['confirmed_by'] = confirmed_by.get(gap['confirmed_by_id'])
                del gap['confirmed_by_id']

            # TODO: какое-то костыльное место, хорошо бы переделать (c) another
            gap_date_from = gap['date_from']
            gap_date_to = gap['date_to']
            gap['days'] = get_gap_days(gap_date_from, gap_date_to, gap['full_day'])
            if gap['full_day']:
                full_day_dates(gap, -1)
            else:
                gap_dates_from_utc(gap, observer_tz)

            if gap['workflow'] == 'duty':
                gap['work_in_absence'] = False
            if gap['workflow'] == 'illness':
                gap['is_covid'] = gap.get('is_covid', False)

            gaps.append(gap)
        res['gaps'] = gaps
        res['total'] = _gaps_total_count(can_see_all, login, date_from, date_to, workflows)
        return res
    except Exception:
        logger.exception('Error getting gaps history')
        return HttpResponseServerError()


def _gaps_query(can_see_all, login, date_from, date_to, workflows):
    gqb = (
        GapQueryBuilder()
        .person_login(login)
        .dates_not_strict(
            date_from + timedelta(seconds=1),
            date_to,
        )
    )

    if workflows is not None:
        gqb = gqb.workflows(workflows)

    if not can_see_all:
        gqb.workflows_states(LIMITED_STATES_HISTORY)
    return gqb.query()


def _gaps_paged(can_see_all, login, date_from, date_to, workflows, limit, page):
    fields = [
        'id',
        'workflow',
        'date_from',
        'date_to',
        'comment',
        'state',
        'master_issue',
        'slave_issues',
        'full_day',
        'form_key',
        'work_in_absence',
        'is_covid',
        'confirmed_by_id',
        'created_at',
        'service_slug',
        'service_name',
        'shift_id',
        'role_on_duty',
        'mandatory',
        'vacation_updated',
        'deadline',
    ]

    sorting = [('date_from', pymongo.DESCENDING)]

    gaps = GapCtl().find_gaps(
        query=_gaps_query(can_see_all, login, date_from, date_to, workflows),
        fields=fields,
        sorting=sorting,
    )

    return gaps.skip(page * limit).limit(limit)


def _gaps_total_count(can_see_all, login, date_from, date_to, workflows):
    return GapCtl().find_gaps(
        query=_gaps_query(can_see_all, login, date_from, date_to, workflows),
    ).count()


def _get_date(request, date_name, default):
    _date = request.GET.get(date_name)
    if not _date:
        return default
    try:
        return dateutil.parser.parse(_date)
    except ValueError:
        return None
