import json
import threading as th

from flask import request, jsonify, Blueprint, current_app
from flask_swagger import swagger
from werkzeug.exceptions import BadRequest, ServiceUnavailable
from redis.exceptions import RedisError

try:
    from config import logger
    from utils import process_task
    from tasks import reply_on_status, reply_on_start, IdNotFound, Task
    from statuses import ExternalStatus
except ModuleNotFoundError:
    from .config import logger
    from .utils import process_task
    from .tasks import reply_on_status, reply_on_start, IdNotFound, Task
    from .statuses import ExternalStatus


bp = Blueprint('tasks', __name__, url_prefix='/firestarter')


@bp.app_errorhandler(BadRequest)
def bad_request(error):
    return jsonify({'errors': 'Bad request'}), 400


@bp.app_errorhandler(IdNotFound)
def invalid_request(error):
    return jsonify({'errors': 'Id not found'}), 404


@bp.app_errorhandler(ServiceUnavailable)
def redis_problems(error):
    return jsonify({'errors': 'Some problems with Redis, please check backend logs'}), 503


@bp.route('/swagger')
def spec():

    swag = swagger(current_app)
    swag['info']['version'] = "0.9"
    swag['info']['title'] = "Firestarter"
    swag['host'] = 'back.luna.yandex-team.ru'
    swag['basePath'] = '/firestarter'
    swag['schemes'] = ['https']
    swag['consumes'] = ['application/json']
    swag['produces'] = ['application/json']

    return jsonify(swag), 200, {
        'Access-Control-Allow-Credentials': True,
        'Access-Control-Allow-Headers': 'Content-Type',
        'Access-Control-Allow-Methods': 'GET, POST, PATCH, DELETE, OPTIONS',
        'Access-Control-Allow-Origin': 'https://swagger-ui.yandex-team.ru'
    }


@bp.route('/start', methods=['POST'])
def init_task():
    """
    Init new firestarter task.
    Creates new firestarter task and initiates its processing.
    ---
    parameters:
        - in: body
          name: config
          schema:
            required:
                - config
            properties:
                config:
                    type: object
                    example: {"phantom": {"address": "ya.ru", "ssl": true, ...}, "neuploader": {"api_address": "http://localhost", ...}, ...}

    responses:
        201:
          description: Task created
        400:
          description: Bad request
        405:
          description: Method not allowed
    """

    if request.content_type != 'application/json' or not request.data:
        raise BadRequest()

    try:
        logger.debug('Start request with body %s', request.json)
        config = json.dumps(request.json.get('config'))
        task = Task(config=config)
    except (ValueError, KeyError):
        logger.error('The body content is not a valid JSON', exc_info=True)
        raise BadRequest
    except RedisError:
        logger.error('Redis problems', exc_info=True)
        raise ServiceUnavailable()

    if not config:
        logger.error('The body content does not contain valid config')
        raise BadRequest

    processing_th = th.Thread(target=process_task, args=(task,))
    processing_th.start()
    result = reply_on_start(task.id)

    return jsonify(result), 201


@bp.route('/status', methods=['GET'])
def get_status():
    """
    Returns task status by ID
    ---
    parameters:
        - in: query
          name: id
          type: string
          required: true
          description: Task id

    responses:
        200:
          description: OK
    """
    firestarter_id = request.args.get('id')
    logger.debug('Get status for task %s', firestarter_id)
    try:
        assert firestarter_id, 'No id in request'
        assert firestarter_id.isalnum(), 'Id contains invalid symbols'
    except AssertionError:
        logger.error('Bad request', exc_info=True)
        raise BadRequest

    result, task_status = reply_on_status(firestarter_id)

    if task_status in (ExternalStatus.SUCCESS, ExternalStatus.IN_PROGRESS):
        return jsonify(result), 202

    return jsonify(result)
