import json
import logging

from flask import Response, request
from pydantic import ValidationError

from backends.utils import return_ko
from solutions.graph.models import (
    FeatureUnavailableError,
    GraphAlreadyExistsError,
    GraphDBDoesNotExistError,
    GraphDBWriteInProgressError,
    GraphDoesNotExistError,
    ModelValidationError,
    PresetNotSetException,
    SnapshotDoesNotExistError,
)
from stores.graph_metadata_store import ConcurrentUpdateCollisionError

logger = logging.getLogger(__name__)


def handle_validation_error(ex: ValidationError) -> Response:
    errors = ex.errors(include_url=False)
    logger.warning(f"Invalid request body for {request.method} {request.path}: {errors}.", exc_info=ex)

    response = Response(json.dumps(errors), content_type="application/json")
    response.status_code = 400

    return response


def handle_graph_does_not_exist_error(ex: GraphDoesNotExistError) -> Response:
    logger.warning(f"Graph '{ex.graph_id}' does not exist.")

    response = return_ko({"errorCode": "GRAPH_DOES_NOT_EXIST"})
    response.status_code = 404

    return response


def handle_graph_already_exists_error(ex: GraphAlreadyExistsError) -> Response:
    logger.warning(f"Graph '{ex.graph_id}' already exists.")

    response = Response()
    response.status_code = 409

    return response


def handle_graph_db_being_built_error(ex: GraphDBWriteInProgressError) -> Response:
    logger.warning(f"Graph database is being built.")

    return return_ko({"errorCode": "GRAPH_DB_BUILD_IN_PROGRESS"})


def handle_graph_db_not_exist_error(ex: GraphDBDoesNotExistError) -> Response:
    logger.warning(f"Graph database does not exist.")

    response = return_ko({"errorCode": "GRAPH_DB_DOES_NOT_EXIST"})
    response.status_code = 404

    return response


def handle_concurrent_update_error(ex: ConcurrentUpdateCollisionError) -> Response:
    logger.warning(f"Attempting to update a graph '{ex.graph_id}' configuration over an out-of-date configuration.")

    response = Response()
    response.status_code = 409

    return response


def handle_snapshot_does_not_exist_error(ex: SnapshotDoesNotExistError) -> Response:
    logger.warning(f"Saved configuration '{ex.snapshot_id}' does not exist.")

    response = Response()
    response.status_code = 404

    return response


def handle_model_validation_error(ex: ModelValidationError) -> Response:
    logger.warning(f"Model is not valid.", exc_info=ex)

    response = return_ko({"errorCode": "MODEL_VALIDATION_ERROR", "message": ex.message})
    response.status_code = 400

    return response


def handle_feature_unavailable_error(ex: FeatureUnavailableError) -> Response:
    logger.warning(f"Attempted to used an unavailable feature.", exc_info=ex)

    response = Response()
    response.status_code = 503
    response.set_data("Feature unavailable.")

    return response


def handle_unexpected_error(ex: Exception) -> Response:
    logger.error(f"An unexpected error occured.", exc_info=ex)

    response = Response()
    response.status_code = 500

    return response


def handle_preset_not_set_error(ex: PresetNotSetException) -> Response:
    logger.warning(f"A preset is mandatory for this operation.", exc_info=ex)

    response = return_ko({"errorCode": "PRESET_NOT_SET"})
    response.status_code = 400

    return response