import json

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

from staff.lib.decorators import available_for_external
from staff.gap.permissions import can_see_gap
from staff.gap.workflows import TRIP_WORKFLOWS
from staff.gap.controllers.gap import GapCtl, PeriodicGapCtl
from staff.gap.exceptions import GapError, GapNotFoundError, MandatoryVacationError
from staff.gap.workflows.utils import find_workflow
from staff.gap.controllers.utils import get_short_person_with_department
from staff.gap.workflows.decorators import valid_gap_id

from staff.gap.api.exceptions import NoGapWithThisIssueKeyError
from staff.gap.api.views.utils import get_gap_id_by_issue_key


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


def gap_action(observer, gap_id, action_name, req_data=None):
    observer_id = observer.get_profile().id

    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()

    gap_id = int(gap_id)
    try:
        gap = GapCtl().find_gap_by_id(gap_id)
    except GapError:
        return HttpResponseNotFound()

    if gap['workflow'] in TRIP_WORKFLOWS:
        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_id,
            gap_from=gap,
            periodic_gap=periodic_gap,
        )
    else:
        workflow = workflow.init_to_modify(observer_id, gap=gap)

    if hasattr(workflow, action_name):
        try:
            getattr(workflow, action_name)()
        except MandatoryVacationError as err:
            return JsonResponse(err.error_dict, status=400)
        except Exception:
            logger.exception('Error changing gap[%s] state to %s' % (gap_id, action_name))
            return HttpResponseServerError()
    else:
        return HttpResponseBadRequest()

    return HttpResponse()


@csrf_exempt
@require_POST
@valid_gap_id
@available_for_external('gap.robot_with_gap_api_access')
def gap_cancel(request, gap_id):
    req_data = None
    if request.body:
        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)


@csrf_exempt
@require_POST
@valid_gap_id
@available_for_external('gap.robot_with_gap_api_access')
def gap_confirm(request, gap_id):
    return gap_action(request.user, gap_id, 'confirm_gap')


@csrf_exempt
@require_POST
@available_for_external('gap.robot_with_gap_api_access')
def gap_confirm_by_issue(request, issue_key):
    try:
        gap_id = get_gap_id_by_issue_key(issue_key)
    except NoGapWithThisIssueKeyError:
        return HttpResponseNotFound(f'There is no gap for issue {issue_key}')
    return gap_action(request.user, gap_id, 'confirm_gap')


@csrf_exempt
@require_POST
@valid_gap_id
@available_for_external('gap.robot_with_gap_api_access')
def gap_sign(request, gap_id):
    return gap_action(request.user, gap_id, 'sign_gap')
