import json

from django.http import (
    HttpResponse,
    HttpResponseNotFound,
    HttpResponseBadRequest,
    HttpResponseForbidden,
    HttpResponseServerError,
    JsonResponse,
)
from django.views.decorators.http import require_POST

from staff.lib.decorators import responding_json, available_for_external

from staff.gap.workflows.utils import find_workflow
from staff.gap.controllers.gap import GapCtl, PeriodicGapCtl
from staff.gap.exceptions import GapError, GapNotFoundError, MandatoryVacationError
from staff.gap.edit_views.utils import check_user_can_perform_gap_action
from staff.gap.controllers.utils import get_short_person_with_department
from staff.gap.permissions import can_see_gap, get_accessible_logins
from staff.gap.workflows.decorators import valid_gap_id

import logging
logger = logging.getLogger('staff.gap.edit_views.gap_state_views')


def gap_action(observer, gap_id, action_name, req_data=None):
    gap_id = int(gap_id)
    try:
        gap = GapCtl().find_gap_by_id(gap_id)
    except GapError:
        return HttpResponseNotFound()

    periodic_gap_id = None
    if req_data:
        periodic_gap_id = req_data.get('periodic_gap_id')

    periodic_gap = None
    if periodic_gap_id:
        try:
            periodic_gap = PeriodicGapCtl().find_gap_by_id(periodic_gap_id)
        except GapNotFoundError:
            return HttpResponseNotFound()

    if not get_accessible_logins(observer.get_profile(), [gap['person_login']]):
        logger.info('%s has no access to %s gap', observer, gap['person_login'])
        return HttpResponseForbidden()

    person = get_short_person_with_department(gap['person_login'])

    if not can_see_gap(gap, observer, person):
        return HttpResponseForbidden()

    try:
        workflow = find_workflow(gap['workflow'])
    except KeyError:
        return HttpResponseBadRequest()

    if periodic_gap:
        workflow = workflow.init_to_modify_periodic_gap(
            modifier_id=observer.get_profile().id,
            gap_from=gap,
            periodic_gap=periodic_gap,
        )
    else:
        workflow = workflow.init_to_modify(observer.get_profile().id, gap=gap)

    try:
        check_user_can_perform_gap_action(gap, observer, action_name)
        getattr(workflow, action_name)()
    except MandatoryVacationError as err:
        return JsonResponse(err.error_dict, status=400)
    except Exception:
        logger.exception('Error changing gap state')
        return HttpResponseServerError()

    return HttpResponse()


@responding_json
@require_POST
@available_for_external
@valid_gap_id
def cancel_gap(request, gap_id):
    try:
        req_data = json.loads(request.body)
    except ValueError:
        logger.exception('Wrong JSON: %s', request.body)
        return HttpResponseBadRequest()

    if req_data and 'periodic_gap_id' in req_data:
        return gap_action(request.user, gap_id, 'cancel_gap_with_period', req_data)

    return gap_action(request.user, gap_id, 'cancel_gap', req_data)


@responding_json
@require_POST
@available_for_external
@valid_gap_id
def confirm_gap(request, gap_id):
    return gap_action(request.user, gap_id, 'confirm_gap')


@responding_json
@require_POST
@available_for_external
@valid_gap_id
def sign_gap(request, gap_id):
    return gap_action(request.user, gap_id, 'sign_gap')
