import logging
import jinja2.exceptions
from flask import jsonify, render_template
from smif.exception import (
    SmifDataExistsError,
    SmifDataMismatchError,
    SmifDataNotFoundError,
)
from smif.http_api.crud import (
    DimensionAPI,
    ModelRunAPI,
    ScenarioAPI,
    SectorModelAPI,
    SmifAPI,
    SosModelAPI,
)


def register_routes(app):
    """Register plain routing"""

    @app.route("/")
    @app.route("/jobs/")
    @app.route("/jobs/<path:path>")
    @app.route("/configure")
    @app.route("/configure/<path:path>")
    def home(path=None):
        """Render single page"""
        try:
            return render_template("index.html")
        except jinja2.exceptions.TemplateNotFound as ex:
            logging.error(ex, exc_info=True)
            return """<!doctype html>
            <style>html, body { font-family: sans-serif; }</style>
            <h1>Error: smif app template not found</h1>
            <p>If you are running from a development build of smif, you may need to build the
            app:</p>
            <code><pre>
cd ./src/smif/app
npm install
npm run build
            </pre>
            </code>
            <p>Otherwise, please report the issue, including as much detail as possible, on
            <a href="https://github.com/nismod/smif/issues">GitHub</a>.</p>
            """


def register_api_endpoints(app):
    """Register API calls (using pluggable views)"""
    register_api(
        app, SmifAPI, "smif_api", "/api/v1/smif/", key="key", key_type="string"
    )
    register_api(
        app,
        ModelRunAPI,
        "model_run_api",
        "/api/v1/model_runs/",
        key="model_run_name",
        key_type="string",
        action="action",
        action_type="string",
    )
    register_api(
        app,
        SosModelAPI,
        "sos_model_api",
        "/api/v1/sos_models/",
        key="sos_model_name",
        key_type="string",
    )
    register_api(
        app,
        SectorModelAPI,
        "sector_model_api",
        "/api/v1/sector_models/",
        key="sector_model_name",
        key_type="string",
    )
    register_api(
        app,
        ScenarioAPI,
        "scenario_api",
        "/api/v1/scenarios/",
        key="scenario_name",
        key_type="string",
    )
    register_api(
        app,
        DimensionAPI,
        "dimension_api",
        "/api/v1/dimensions/",
        key="dimension_name",
        key_type="string",
    )


def register_error_handlers(app):
    """Handle expected errors"""

    @app.errorhandler(SmifDataExistsError)
    def handle_exists(error):
        """Return 400 Bad Request if data to be created already exists"""
        response = jsonify({"message": str(error)})
        response.status_code = 400
        return response

    @app.errorhandler(SmifDataMismatchError)
    def handle_mismatch(error):
        """Return 400 Bad Request if data and id/name are mismatched"""
        response = jsonify({"message": str(error)})
        response.status_code = 400
        return response

    @app.errorhandler(SmifDataNotFoundError)
    def handle_not_found(error):
        """Return 404 if data is not found"""
        response = jsonify({"message": str(error)})
        response.status_code = 404
        return response


def register_api(
    app, view, endpoint, url, key="id", key_type="int", action=None, action_type=None
):
    """Register a MethodView as an endpoint with CRUD operations at a URL"""
    view_func = view.as_view(endpoint)
    app.add_url_rule(url, defaults={key: None}, view_func=view_func, methods=["GET"])
    app.add_url_rule(url, view_func=view_func, methods=["POST"])
    if action:
        app.add_url_rule(
            "%s<%s:%s>/<%s:%s>" % (url, key_type, key, action_type, action),
            view_func=view_func,
            methods=["GET"],
        )
        app.add_url_rule(
            "%s<%s:%s>/<%s:%s>" % (url, key_type, key, action_type, action),
            view_func=view_func,
            methods=["POST"],
        )
    app.add_url_rule(
        "%s<%s:%s>" % (url, key_type, key),
        view_func=view_func,
        methods=["GET", "PUT", "DELETE"],
    )
