import logging
from typing import AnyStr, Dict

import sform
from django.views.decorators.http import (
    require_http_methods,
)

from staff.lib.db import atomic
from staff.lib.decorators import (
    available_for_external,
    consuming_json,
    responding_json,
)
from staff.person.models import (
    Visa,
)
from staff.person_profile.permissions.utils import observer_has_perm


logger = logging.getLogger(__name__)


class VisaForm(sform.SForm):
    id = sform.IntegerField()
    description = sform.CharField(max_length=256)
    country = sform.CharField(max_length=128, state=sform.REQUIRED)
    number = sform.CharField(max_length=64, state=sform.REQUIRED)
    is_multiple = sform.BooleanField(default=False)
    issue_date = sform.DateField(state=sform.REQUIRED)
    expire_date = sform.DateField()


MODEL_TO_FORM = {
    Visa: VisaForm,
}

FORM_FIELD_TO_MODEL = {
    'visas': Visa,
}


def _get_documents_form_dict(login):
    # type: (AnyStr) -> Dict
    """
    :return: structure like DocumentsForm.cleaned_data
    """
    def get_values(model):
        return list(
            model.objects
            .values(*MODEL_TO_FORM[model].base_fields.keys())
            .filter(person__login=login)
            .order_by('id')
        )
    return {
        field: get_values(model)
        for field, model in FORM_FIELD_TO_MODEL.items()
    }


@responding_json
@available_for_external
@require_http_methods(['GET', 'POST'])
@observer_has_perm('documents')
def get(request, login):
    return {'target': {'documents': _get_documents_form_dict(login)}}


class DocumentsForm(sform.SForm):
    visas = sform.GridField(sform.FieldsetField(VisaForm))


@atomic
def _update_documents(to_update, person_id):
    # type: (Dict, int) -> None
    """
    :param to_update: structure like DocumentsForm.cleaned_data
    """
    for field, model in FORM_FIELD_TO_MODEL.items():
        all_ids = []
        for model_kwargs in to_update.get(field, []):
            cur_id = model_kwargs.pop('id', None)
            if cur_id:
                model.objects.filter(id=cur_id, person_id=person_id).update(**model_kwargs)
            else:
                cur_id = model.objects.create(person_id=person_id, **model_kwargs).id
            all_ids.append(cur_id)
        model.objects.filter(person_id=person_id).exclude(id__in=all_ids).delete()


@responding_json
@consuming_json
@available_for_external
@require_http_methods(['GET', 'POST'])
@observer_has_perm('edit_documents')
def edit(request, login, json_data):
    if request.method == 'GET':
        return DocumentsForm(initial=_get_documents_form_dict(login)).as_dict()

    cur_form = DocumentsForm(data=json_data)
    if not cur_form.is_valid():
        return cur_form.errors

    person_id = request.permissions_ctl.properties.get_target_data(login)['id']
    _update_documents(to_update=cur_form.cleaned_data, person_id=person_id)
    return get(request, login)
