import json
from django.db import transaction
from django.http import JsonResponse

from common.decorators import ajax_required
from connections.models import Entry, Status, ExampleConnection, SchemaHook, ArgumentConnection, RealisationDescription
from meanings.models import LexicalUnit
from semantics.models import Frame, FrameOpinion, Argument, ArgumentRole, SemanticRole, \
    RoleAttribute, RoleSubAttribute, RoleType
from syntax.models import Position


@ajax_required
@transaction.atomic
def change_status(request):
    """
    Changing status of slowal frame in building process.
    The request has to contain 'entry_id' and 'status'.
    :param request: http request
    :return: Empty json response
    """
    if request.method == 'POST':
        entry_id = request.POST['entry_id']
        status = request.POST['status']

        entry = Entry.objects.get(pk=entry_id)
        entry.status = Status.objects.get(key=status)
        entry.save()
        return JsonResponse({})

    return JsonResponse({})


@ajax_required
@transaction.atomic
def create_new_slowal_frame(request):
    """
    Create new empty slowal frame for lus without sementic layer.
    The request has to contain 'entry_id' and 'lu_ids' list for witch slowal frame has to be created.
    :param request: http request
    :return: Empty json response
    """
    if request.method == 'POST':
        entry_id = request.POST['entry_id']
        lu_ids = json.loads(request.POST['lu_ids'])
        frame = Frame(opinion=FrameOpinion.objects.get(key='unk'), in_building=True)
        frame.save()

        entry = Entry.objects.get(pk=entry_id)
        for lu_id in lu_ids:
            lu = LexicalUnit.objects.get(pk=lu_id)
            lu.entry = entry
            lu.save()
            frame.lexical_units.add(lu)
            entry.lexical_units_count = entry.lexical_units_count + 1

        frame.save()
        entry.save()
        return JsonResponse({})

    return JsonResponse({})


@ajax_required
@transaction.atomic
def add_argument_to_frame(request):
    """
    Creating a new argument for the specified slowal frame in building process.
    The request has to contain 'frame_id' that represents slowal frame in building process.
    :param request: http request
    :return: Empty json response
    """
    if request.method == 'POST':
        frame_id = request.POST['frame_id']
        frame = Frame.objects.get(id=frame_id)

        argument = Argument(frame=frame)
        change_role_base(argument, request)
        argument.save()

        return JsonResponse({})

    return JsonResponse({})


@ajax_required
@transaction.atomic
def change_role(request):
    """
    Changing role of a specified slowal frame argument.
    The request has to contain 'frame_id' and 'argument_id' that represents slowal frame in building process.
    As well as other params required for role change ('role_id', 'role_type', 'attribute_id', 'sub_attribute_id').
    :param request: http request
    :return: Empty json response
    """
    if request.method == 'POST':
        frame_id = request.POST['frame_id']
        argument_id = request.POST['argument_id']

        frame_argument = Argument.objects.get(frame_id=frame_id, id=argument_id)
        change_role_base(frame_argument, request)
        frame_argument.save()
    return JsonResponse({})


def change_role_base(frame_argument, request):
    role_id = request.POST['role_id']
    attribute_id = request.POST.get('attribute_id', None)
    sub_attribute_id = request.POST.get('sub_attribute_id', None)

    argument_role = ArgumentRole.objects.filter(role_id=role_id, attribute_id=attribute_id,
                                                sub_attribute_id=sub_attribute_id).first()
    if argument_role is None:
        argument_role = ArgumentRole(role=SemanticRole.objects.get(pk=role_id),
                                     attribute=None if attribute_id is None else RoleAttribute.objects.get(
                                         pk=attribute_id),
                                     sub_attribute=RoleSubAttribute.objects.get(pk=sub_attribute_id))
        argument_role.save()

    frame_argument.role = argument_role


@ajax_required
@transaction.atomic
def attach_examples_to_frame(request):
    """
    Attaching selected examples to the slowal frame in building process.
    The request has to contain 'frame_id' and 'argument_id' that represents slowal frame in building process.
    List of example ids ('example_ids') is also required.
    :param request: http request
    :return: Empty json response
    """
    if request.method == 'POST':
        frame_id = request.POST['frame_id']
        argument_id = request.POST['argument_id']
        example_ids = json.loads(request.POST['example_ids'])

        argument = Argument.objects.get(id=argument_id)
        frame = Frame.objects.get(id=frame_id)

        lexical_units = frame.lexical_units.all()

        for example_id in example_ids:
            for lexical_unit in lexical_units:
                example_conn = ExampleConnection(example_id=example_id, lexical_unit=lexical_unit)
                example_conn.save()
                example_conn.arguments.add(argument)
                example_conn.save()

    return JsonResponse({})


@ajax_required
@transaction.atomic
def attach_schema_to_argument(request):
    """
    Attaching selected schema to the specifier argument of slowal frame in building process.
    The request has to contain 'frame_id', 'argument_id', schema_id and 'schema_position_id'.
    :param request: http request
    :return: Empty json response
    """
    if request.method == 'POST':
        frame_id = int(request.POST['frame_id'])
        subentry_id = int(request.POST['subentry_id'])
        argument_id = int(request.POST['argument_id'])
        schema_id = int(request.POST['schema_id'])
        schema_position_id = int(request.POST['schema_position_id'])
        schema_alternation_id = int(request.POST['schema_alternation_id'])

        argument = Argument.objects.get(id=argument_id)

        if not ArgumentConnection.objects.filter(argument=argument, schema_connections__schema_id=schema_id).exists():
            position = Position.objects.get(id=schema_position_id)
            for phrase_type in position.phrase_types.all():
                schema_hooks = SchemaHook.objects.filter(subentry_id=subentry_id, schema_id=schema_id,
                                                             position_id=schema_position_id, phrase_type=phrase_type,
                                                             alternation=schema_alternation_id)
                if schema_hooks.count() > 0:
                    schema_hook = schema_hooks[0]
                else:
                    schema_hook = SchemaHook.objects.create(subentry_id=subentry_id, schema_id=schema_id,
                                                             position_id=schema_position_id, phrase_type=phrase_type,
                                                             alternation=schema_alternation_id)

                argument_connection, _ = ArgumentConnection.objects.get_or_create(argument=argument,
                                                                                    defaults={'argument': argument})
                argument_connection.save()
                argument_connection.schema_connections.add(schema_hook)
                argument_connection.save()

                RealisationDescription.objects.get_or_create(frame_id=frame_id,
                                                             schema_id=schema_id,
                                                             defaults={'alternation': schema_alternation_id,
                                                                       'description': ''})
            return JsonResponse({'succ': True})
        else:
            return JsonResponse({'succ': False, 'error': 'Odrzucono próbę połączenia. Argument jest już podłączony do innej pozycji w wybranym schemacie.'})
    else:
        return JsonResponse({'succ': False, 'error': 'Not a Post request.'})


@ajax_required
@transaction.atomic
def delete_schema_to_argument_connection(request):
    """
    Deleting a schema and frame argument connection.
    The request has to contain 'argument_id', schema_id and 'schema_position_id'.
    :param request: http request
    :return: Empty json response
    """
    if request.method == 'POST':
        argument_id = request.POST['argument_id']
        schema_id = request.POST['schema_id']
        schema_position_id = request.POST['schema_position_id']

        argument = Argument.objects.get(id=argument_id)

        schema_hooks = SchemaHook.objects.filter(schema_id=schema_id, position=schema_position_id)

        argument_connection = ArgumentConnection.objects.get(argument=argument)
        argument_connection.schema_connections.remove(schema_hooks[0])
        argument_connection.save()

    return JsonResponse({})


@ajax_required
@transaction.atomic
def finish_frame_processing(request):
    """
    Changing frame status to created.
    The request has to contain 'frame_id'.
    :param request: http request
    :return: Empty json response
    """
    if request.method == 'POST':
        frame_id = request.POST['frame_id']

        frame = Frame.objects.get(id=frame_id)
        frame.in_building = False
        frame.save()

    return JsonResponse({})