# coding: utf-8


import constance
import logging
from django.db import transaction
from tastypie import http
from tastypie.resources import Resource

from idm.api.base import RequiredAuthentication, NotFoundView
from idm.api.exceptions import BadRequest
from idm.api.frontend.batch.request import ApiRequest
from idm.api.frontend.batch.processor import RequestProcessor
from idm.api.frontend.forms import BatchResourceForm


log = logging.getLogger(__name__)


class BatchResource(Resource):
    batch_resource_form = BatchResourceForm

    class Meta:
        authentication = RequiredAuthentication()
        allowed_methods = ['post']
        resource_name = 'batch'
        detail_allowed_methods = None

    def __init__(self, api_name=None, api=None):
        super(BatchResource, self).__init__(api_name=api_name)
        self.api = api

    def get_schema(self, request, **kwargs):
        return NotFoundView().dispatch(request)

    def post_list(self, request, **kwargs):
        try:
            deserialized = self.deserialize(
                request, request.body,
                format=request.META.get('CONTENT_TYPE', 'application/json')
            )
        except BadRequest:
            log.warning(f'Content could be deserialized from json: {request.body}')
            raise

        data = self.alter_deserialized_detail_data(request, deserialized)

        subrequests = data
        options = {}
        if not isinstance(data, list):
            subrequests = data.get('requests', [])
            options = data.get('options', {})

        if not isinstance(data, (list, dict)) or len(subrequests) == 0:
            return self.create_response(
                request=request,
                response_class=http.HttpBadRequest,
                data={
                    'message': 'Non-empty list of request objects must be supplied',
                    'error_code': 'BAD_REQUEST',
                }
            )

        rollback_subrequests = [subrequest.pop('rollback_on_4xx', True) for subrequest in subrequests]

        tolerate_4xx = request.user.username not in constance.config.IDM_OLD_BATCH_USERS.split(',')

        forms = list(map(self.batch_resource_form, subrequests))
        api_requests = list(map(ApiRequest, forms))

        processor = RequestProcessor(original_request=request, resource=self, options=options)

        api_responses = []
        response_class = http.HttpResponse
        connection = transaction.get_connection()

        for api_request, should_rollback in zip(api_requests, rollback_subrequests):
            api_response = processor.process_api_request(api_request)
            api_responses.append(api_response)

            # Если произошла прямо ошибка, 500 или нужно откатывать транзакцию
            if connection.get_rollback() or api_response.is_error(tolerate_4xx=True):
                response_class = http.HttpApplicationError
                log.error('Unexpected subrequest failure')
                connection.set_rollback(True)
                break

            # Ошибки не случилось, транзакция в порядке, код не 5xx
            if not tolerate_4xx:
                # Робот в списке исключений, отвечаем 5xx, если подзапрос 4xx
                if api_response.is_error(tolerate_4xx=False):
                    log.info('Got expected batch error for user %s', request.user.username)
                    response_class = http.HttpApplicationError
                    connection.set_rollback(True)
                    break
            elif api_response.is_error(tolerate_4xx=not should_rollback):
                # Случилась 4xx, которую мы не ожидали (should_rollback не проставлен в False)
                # Возвращаем общую 400, откатываем транзакцию и прекращаем дальнейший процессинг
                log.info('Got unexpected subrequest 4xx client error')
                response_class = http.HttpBadRequest
                connection.set_rollback(True)
                break
            elif api_response.is_warning(tolerate_4xx=True):
                # Случилась 4xx, которую мы ожидали
                # Тем не менее, ставим общий код в 400
                response_class = http.HttpBadRequest

        return self.create_response(
            request=request,
            response_class=response_class,
            data={
                'responses': [response.serialize() for response in api_responses],
            }
        )
