# -*- coding: utf-8 -*-

"""
 (c) 2019 - Copyright Red Hat Inc

 Authors:
   Michal Konecny <mkonecny@redhat.com>

"""

from __future__ import print_function, unicode_literals, absolute_import

import flask
import logging

from sqlalchemy.exc import SQLAlchemyError

import pagure.exceptions
import pagure.lib.query

from pagure.lib.plugins import get_enabled_plugins
from pagure.api import (
    API,
    api_method,
    api_login_required,
    api_login_optional,
    APIERROR,
)
from pagure.api.utils import _get_repo, _check_token, _check_plugin

_log = logging.getLogger(__name__)


@API.route("/<repo>/settings/<plugin>/install", methods=["POST"])
@API.route("/<namespace>/<repo>/settings/<plugin>/install", methods=["POST"])
@API.route(
    "/fork/<username>/<repo>/settings/<plugin>/install", methods=["POST"]
)
@API.route(
    "/fork/<username>/<namespace>/<repo>/settings/<plugin>/install",
    methods=["POST"],
)
@api_login_required(acls=["modify_project"])
@api_method
def api_install_plugin(repo, username=None, namespace=None, plugin=None):
    """
    Install plugin
    --------------
    Install a plugin to a repository.

    ::

        POST /api/0/<repo>/settings/<plugin>/install
        POST /api/0/<namespace>/<repo>/settings/<plugin>/install

    ::

        POST /api/0/fork/<username>/<repo>/settings/<plugin>/install
        POST /api/0/fork/<username>/<namespace>/<repo>/settings/<plugin>
             /install

    Sample response
    ^^^^^^^^^^^^^^^

    ::

        {
          "plugin": {
            "assignee": null,
            "blocks": [],
            "close_status": null,
            "closed_at": null,
            "closed_by": null,
            "comments": [],
            "content": "This issue needs attention",
            "custom_fields": [],
            "date_created": "1479458613",
            "depends": [],
            "id": 1,
            "milestone": null,
            "priority": null,
            "private": false,
            "status": "Open",
            "tags": [],
            "title": "test issue",
            "user": {
              "fullname": "PY C",
              "name": "pingou"
            }
          },
          "message": "Issue created"
        }

    """
    output = {}
    repo = _get_repo(repo, username, namespace)
    _check_token(repo, project_token=False)
    plugin = _check_plugin(plugin)

    # Private repos are not allowed to leak information outside so disabling CI
    # enables us to keep the repos totally discreate and prevents from leaking
    # information outside
    if repo.private and plugin.name == "Pagure CI":
        raise pagure.exceptions.APIError(
            404, error_code=APIERROR.EPLUGINDISABLED
        )

    if plugin.name in pagure.config.config.get("DISABLED_PLUGINS", []):
        raise pagure.exceptions.APIError(
            404, error_code=APIERROR.EPLUGINDISABLED
        )

    if plugin.name == "default":
        raise pagure.exceptions.APIError(
            403, error_code=APIERROR.EPLUGINCHANGENOTALLOWED
        )

    fields = []
    new = True
    dbobj = plugin.db_object()

    if hasattr(repo, plugin.backref):
        dbobj = getattr(repo, plugin.backref)

        # There should always be only one, but let's double check
        if dbobj:
            new = False
        else:
            dbobj = plugin.db_object()

    form = plugin.form(obj=dbobj, csrf_enabled=False)
    for field in plugin.form_fields:
        fields.append(getattr(form, field))

    form.active.data = True

    if form.validate_on_submit():
        form.populate_obj(obj=dbobj)

        if new:
            dbobj.project_id = repo.id
            flask.g.session.add(dbobj)
        try:
            flask.g.session.flush()
        except SQLAlchemyError:  # pragma: no cover
            flask.g.session.rollback()
            _log.exception("Could not add plugin %s", plugin.name)
            raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR)

        try:
            # Set up the main script if necessary
            plugin.set_up(repo)
            # Install the plugin itself
            plugin.install(repo, dbobj)
        except pagure.exceptions.FileNotFoundException as err:
            flask.g.session.rollback()
            _log.exception(err)
            raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR)

        try:
            flask.g.session.commit()
            output["message"] = "Hook %s activated" % plugin.name
            output["plugin"] = plugin.to_json()
        except SQLAlchemyError as err:  # pragma: no cover
            flask.g.session.rollback()
            _log.exception(err)
            raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR)

    else:
        raise pagure.exceptions.APIError(
            400, error_code=APIERROR.EINVALIDREQ, errors=form.errors
        )

    jsonout = flask.jsonify(output)
    return jsonout


@API.route("/<repo>/settings/<plugin>/remove", methods=["POST"])
@API.route("/<namespace>/<repo>/settings/<plugin>/remove", methods=["POST"])
@API.route(
    "/fork/<username>/<repo>/settings/<plugin>/remove", methods=["POST"]
)
@API.route(
    "/fork/<username>/<namespace>/<repo>/settings/<plugin>/remove",
    methods=["POST"],
)
@api_login_required(acls=["modify_project"])
@api_method
def api_remove_plugin(repo, username=None, namespace=None, plugin=None):
    """
    Remove plugin
    --------------
    Remove a plugin from repository.

    ::

        POST /api/0/<repo>/settings/<plugin>/remove
        POST /api/0/<namespace>/<repo>/settings/<plugin>/remove

    ::

        POST /api/0/fork/<username>/<repo>/settings/<plugin>/remove
        POST /api/0/fork/<username>/<namespace>/<repo>/settings/<plugin>
             /remove

    Sample response
    ^^^^^^^^^^^^^^^

    ::

        {
          "plugin": {
            "assignee": null,
            "blocks": [],
            "close_status": null,
            "closed_at": null,
            "closed_by": null,
            "comments": [],
            "content": "This issue needs attention",
            "custom_fields": [],
            "date_created": "1479458613",
            "depends": [],
            "id": 1,
            "milestone": null,
            "priority": null,
            "private": false,
            "status": "Open",
            "tags": [],
            "title": "test issue",
            "user": {
              "fullname": "PY C",
              "name": "pingou"
            }
          },
          "message": "Issue created"
        }

    """
    output = {}
    repo = _get_repo(repo, username, namespace)
    _check_token(repo, project_token=False)
    plugin = _check_plugin(plugin)

    # Private repos are not allowed to leak information outside so disabling CI
    # enables us to keep the repos totally discreate and prevents from leaking
    # information outside
    if repo.private and plugin.name == "Pagure CI":
        raise pagure.exceptions.APIError(
            404, error_code=APIERROR.EPLUGINDISABLED
        )

    if plugin.name in pagure.config.config.get("DISABLED_PLUGINS", []):
        raise pagure.exceptions.APIError(
            404, error_code=APIERROR.EPLUGINDISABLED
        )

    if plugin.name == "default":
        raise pagure.exceptions.APIError(
            403, error_code=APIERROR.EPLUGINCHANGENOTALLOWED
        )

    fields = []
    new = True
    dbobj = plugin.db_object()

    if hasattr(repo, plugin.backref):
        dbobj = getattr(repo, plugin.backref)

        # There should always be only one, but let's double check
        if dbobj:
            new = False
        else:
            dbobj = plugin.db_object()

    form = plugin.form(obj=dbobj)
    for field in plugin.form_fields:
        fields.append(getattr(form, field))

    form.active.data = False

    if form.validate_on_submit():
        form.populate_obj(obj=dbobj)

        if new:
            dbobj.project_id = repo.id
            flask.g.session.add(dbobj)
        try:
            flask.g.session.flush()
        except SQLAlchemyError:  # pragma: no cover
            flask.g.session.rollback()
            _log.exception("Could not add plugin %s", plugin.name)
            raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR)

        try:
            plugin.remove(repo)
        except pagure.exceptions.FileNotFoundException as err:
            flask.g.session.rollback()
            _log.exception(err)
            raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR)

        try:
            flask.g.session.commit()
            output["message"] = "Hook %s deactivated" % plugin.name
            output["plugin"] = plugin.to_json(public=True)
        except SQLAlchemyError as err:  # pragma: no cover
            flask.g.session.rollback()
            _log.exception(err)
            raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR)

    else:
        raise pagure.exceptions.APIError(
            400, error_code=APIERROR.EINVALIDREQ, errors=form.errors
        )

    jsonout = flask.jsonify(output)
    return jsonout


@API.route("/<namespace>/<repo>/plugins")
@API.route("/fork/<username>/<repo>/plugins")
@API.route("/<repo>/plugins")
@API.route("/fork/<username>/<namespace>/<repo>/plugins")
@api_login_optional()
@api_method
def api_view_plugins(repo, username=None, namespace=None):
    """
    List project's plugins
    ----------------------
    List installed plugins on a project.

    ::

        GET /api/0/<repo>/plugins
        GET /api/0/<namespace>/<repo>/plugins

    ::

        GET /api/0/fork/<username>/<repo>/plugins
        GET /api/0/fork/<username>/<namespace>/<repo>/plugins

    Sample response
    ^^^^^^^^^^^^^^^

    ::

        {
          "total_plugins": 1,
          "plugins": [
            {
              "assignee": null,
              "blocks": ["1"],
              "close_status": null,
              "closed_at": null,
              "closed_by": null,
              "comments": [],
              "content": "asd",
              "custom_fields": [],
              "date_created": "1427442217",
              "depends": [],
              "id": 4,
              "last_updated": "1533815358",
              "milestone": null,
              "priority": null,
              "private": false,
              "status": "Fixed",
              "tags": [
                "0.1"
              ],
              "title": "bug",
              "user": {
                "fullname": "PY.C",
                "name": "pingou"
              }
            }
          ]
        }

    """
    repo = _get_repo(repo, username, namespace)

    plugins = get_enabled_plugins(repo)

    jsonout = flask.jsonify(
        {
            "total_issues": len(plugins),
            "issues": [plugin.to_json(public=True) for plugin in plugins],
        }
    )
    return jsonout
