import json
import logging

from flask import Flask, request, jsonify

from .model import LabelStudioMLBase
from .exceptions import exception_handler


logger = logging.getLogger(__name__)

_server = Flask(__name__)
MODEL_CLASS = LabelStudioMLBase


def init_app(model_class):
    global MODEL_CLASS

    if not issubclass(model_class, LabelStudioMLBase):
        raise ValueError('Inference class should be the subclass of ' + LabelStudioMLBase.__class__.__name__)

    MODEL_CLASS = model_class
    return _server


@_server.route('/predict', methods=['POST'])
@exception_handler
def _predict():
    """
    Predict tasks

    Example request:
    request = {
            'tasks': tasks,
            'model_version': model_version,
            'project': '{project.id}.{int(project.created_at.timestamp())}',
            'label_config': project.label_config,
            'params': {
                'login': project.task_data_login,
                'password': project.task_data_password,
                'context': context,
            },
        }

    @return:
    Predictions in LS format
    """
    data = request.json
    print(f'Request: {json.dumps(data,ensure_ascii=False,indent=4)}')
    tasks = data.get('tasks')
    params = data.get('params') or {}
    project = data.get('projectUuid')
    if project:
        project_id = data.get('projectUuid')
    else:
        project_id = None
    label_config = data.get('label_config')
    context = params.pop('context', {})

    model = MODEL_CLASS(project_id=project_id)
    model.use_label_config(label_config)

    predictions = model.predict(tasks, context=context, **params)
    print(f'Predictions: {json.dumps(predictions,ensure_ascii=False,indent=4)}')
    return jsonify({'results': predictions})


@_server.route('/setup', methods=['POST'])
@exception_handler
def _setup():
    data = request.json
    print(f'data: {json.dumps(data, indent=4)}')
    project_id = data.get('projectUuid')
    print(f'project_id: {project_id}')
    label_config = data.get('schema')
    model = MODEL_CLASS(project_id=project_id, label_config=label_config)
    model.use_label_config(label_config)
    model_version = model.get('model_version')
    return jsonify({'model_version': model_version})


TRAIN_EVENTS = (
    'ANNOTATION_CREATED',
    'ANNOTATION_UPDATED',
    'ANNOTATION_DELETED',
    'PROJECT_UPDATED'
)


@_server.route('/webhook', methods=['POST'])
def webhook():
    data = request.json
    event = data.pop('action')
    if event not in TRAIN_EVENTS:
        return jsonify({'status': 'Unknown event'}), 200
    project_id = str(data['project']['id'])
    label_config = data['project']['label_config']
    model = MODEL_CLASS(project_id)
    model.use_label_config(label_config)
    model.fit(event, data)
    return jsonify({}), 201


@_server.route('/health', methods=['GET'])
@_server.route('/', methods=['GET'])
@exception_handler
def health():
    return jsonify({
        'status': 'UP',
        'model_class': MODEL_CLASS.__name__
    })


@_server.route('/metrics', methods=['GET'])
@exception_handler
def metrics():
    return jsonify({})


@_server.errorhandler(FileNotFoundError)
def file_not_found_error_handler(error):
    logger.warning('Got error: ' + str(error))
    return str(error), 404


@_server.errorhandler(AssertionError)
def assertion_error(error):
    logger.error(str(error), exc_info=True)
    return str(error), 500


@_server.errorhandler(IndexError)
def index_error(error):
    logger.error(str(error), exc_info=True)
    return str(error), 500


@_server.before_request
def log_request_info():
    logger.debug('Request headers: %s', request.headers)
    logger.debug('Request body: %s', request.get_data())


@_server.after_request
def log_response_info(response):
    logger.debug('Response status: %s', response.status)
    logger.debug('Response headers: %s', response.headers)
    logger.debug('Response body: %s', response.get_data())
    return response
