import json

from django.utils.decorators import method_decorator
from django.views.generic.base import View

from django.contrib.auth.decorators import login_required

from staff.lib.decorators import responding_json
from staff.lib.forms.staff_form_grid import StaffFormGrid
from staff.lib.forms.errors import invalid_json_error, form_data_error

from .forms import (
    OrderCardForm,
    OrderCardEnForm,
    OrderCardBackForm,
    OrderCardUploadForm,
)
from . import controller as card_ctl

import logging

logger = logging.getLogger('card_order')


def _parse_forms_data(request):
    first_level_forms = {
        'native': OrderCardForm,
        'english': OrderCardEnForm,
        'back': OrderCardBackForm,
    }

    request_body = request.body
    try:
        ordering_data = json.loads(request_body)
    except ValueError:
        logger.exception('Wrong JSON: %s', request_body)
        return invalid_json_error(request_body), 400

    grids = {
        form_name: StaffFormGrid(
            form_class, data=ordering_data.get(form_name, {}))
        for form_name, form_class in first_level_forms.items()
        if ordering_data.get(form_name) is not None
    }

    if not grids:
        return form_data_error('native'), 400

    errors = {
        grid_type: grid.errors()
        for grid_type, grid in grids.items()
        if not grid.is_valid()
    }
    return {
        'grids': {
            grid_name: grid.cleaned_data[0]
            for grid_name, grid in grids.items()
        },
        'errors': errors
    }, 200


class PrepareCardView(View):
    """
    Подготовка данных для визитки
    """
    @method_decorator(login_required)
    @method_decorator(responding_json)
    def dispatch(self, request, *args, **kwargs):
        return super(PrepareCardView, self).dispatch(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        grids_data, status_code = _parse_forms_data(request)
        if status_code != 200:
            return grids_data, status_code

        if grids_data['errors']:
            return {'errors': grids_data['errors']}

        grids = grids_data['grids']
        main_grid = grids['native'] if 'native' in grids else grids['english']
        response = {}

        for grid_name, grid in grids.items():
            card_builder = card_ctl.get_card_builder(grid_name)
            response[grid_name] = {
                'first_name': grid['first_name'],
                'last_name': grid['last_name'],
                'position': card_builder.position(grid),
                'address': card_builder.office(main_grid),
                'phone': card_builder.phone(main_grid),
                'fax': card_builder.fax(main_grid),
                'site': card_builder.site(main_grid),
                'email': card_builder.email(main_grid),
                'mobile': card_builder.mobile(main_grid),
                'contacts': card_builder.other(main_grid).strip(),
                'extra_phone': card_builder.extra_phone(main_grid),
            }

        return response


class OrderCardView(View):
    """
    Новый АПИ для создания заявок на визитки
    """
    @method_decorator(login_required)
    @method_decorator(responding_json)
    def dispatch(self, request, *args, **kwargs):
        return super(OrderCardView, self).dispatch(request, *args, **kwargs)

    def get(self, request, login, *args, **kwargs):
        try:
            return card_ctl.get_initial_card(login)
        except card_ctl.CardOrderError as e:
            return {'errors': [e.message]}, e.status_code

    def post(self, request, login, *args, **kwargs):
        form = OrderCardUploadForm(request.POST, request.FILES)
        if not form.is_valid():
            return {'errors': form.errors}

        try:
            order_result = card_ctl.order(
                login,
                form.cleaned_data.get('office'),
                form.cleaned_data.get('pdf'),
                form.cleaned_data.get('is_single'),
                request.user.get_profile()
            )
        except card_ctl.CardOrderError as e:
            return {'errors': [e.message]}, e.status_code
        return order_result
