"""
rcextensions module.

"""


import os
import imp
import string
import functools

here = os.path.dirname(os.path.abspath(__file__))
registered_extensions = dict()

class DotDict(dict):

    def __contains__(self, k):
        try:
            return dict.__contains__(self, k) or hasattr(self, k)
        except:
            return False

    # only called if k not found in normal places
    def __getattr__(self, k):
        try:
            return object.__getattribute__(self, k)
        except AttributeError:
            try:
                return self[k]
            except KeyError:
                raise AttributeError(k)

    def __setattr__(self, k, v):
        try:
            object.__getattribute__(self, k)
        except AttributeError:
            try:
                self[k] = v
            except:
                raise AttributeError(k)
        else:
            object.__setattr__(self, k, v)

    def __delattr__(self, k):
        try:
            object.__getattribute__(self, k)
        except AttributeError:
            try:
                del self[k]
            except KeyError:
                raise AttributeError(k)
        else:
            object.__delattr__(self, k)

    def toDict(self):
        return unserialize(self)

    def __repr__(self):
        keys = list(self.iterkeys())
        keys.sort()
        args = ', '.join(['%s=%r' % (key, self[key]) for key in keys])
        return '%s(%s)' % (self.__class__.__name__, args)

    @staticmethod
    def fromDict(d):
        return serialize(d)


def serialize(x):
    if isinstance(x, dict):
        return DotDict((k, serialize(v)) for k, v in x.iteritems())
    elif isinstance(x, (list, tuple)):
        return type(x)(serialize(v) for v in x)
    else:
        return x


def unserialize(x):
    if isinstance(x, dict):
        return dict((k, unserialize(v)) for k, v in x.iteritems())
    elif isinstance(x, (list, tuple)):
        return type(x)(unserialize(v) for v in x)
    else:
        return x


def load_extension(filename, async=False):
    """
    use to load extensions inside rcextension folder.
    for example::

        callback = load_extension('email.py', async=False)
        if callback:
            callback('foobar')

    put file named email.py inside rcextensions folder to load it. Changing
    async=True will make the call of the plugin async, it's useful for
    blocking calls like sending an email or notification with APIs.
    """
    mod = ''.join(filename.split('.')[:-1])
    loaded = imp.load_source(mod, os.path.join(here, filename))

    callback = getattr(loaded, 'run', None)
    if not callback:
        raise Exception('Plugin missing `run` method')
    if async:
        # modify callback so it's actually an async call
        def _async_callback(*args, **kwargs):
            import threading
            thr = threading.Thread(target=callback, args=args, kwargs=kwargs)
            thr.start()
            if kwargs.get('_async_block'):
                del kwargs['_async_block']
                thr.join()

        return _async_callback
    return callback


def _verify_kwargs(expected_parameters, kwargs):
    """
    Verify that exactly `expected_parameters` are passed in as `kwargs`.
    """
    expected_parameters = set(expected_parameters)
    kwargs_keys = set(kwargs.keys())
    if kwargs_keys != expected_parameters:
        missing_kwargs = expected_parameters - kwargs_keys
        unexpected_kwargs = kwargs_keys - expected_parameters
        raise AssertionError(
            "Missing parameters: %r, unexpected parameters: %s" %
            (missing_kwargs, unexpected_kwargs))


def verify_kwargs(required_args):
    """
    decorator to verify extension calls arguments.

    :param required_args:
    """
    def wrap(func):
        def wrapper(*args, **kwargs):
            _verify_kwargs(required_args, kwargs)
            return func(*args, **kwargs)
        return wrapper
    return wrap


def register(name=None):
    def wrap(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            # register load_extensions in kwargs, so we can chain plugins
            kwargs['_load_extension'] = load_extension
            # append this path for us to use added plugins or modules
            import sys
            _cur_path = os.path.dirname(os.path.abspath(__file__))
            if _cur_path not in sys.path:
                sys.path.append(_cur_path)

            registered_extensions[func.__name__] = func
            return func(*args, **kwargs)
        return wrapper
    return wrap

# =============================================================================
#  END OF UTILITY FUNCTIONS HERE
# =============================================================================

# Additional mappings that are not present in the pygments lexers
# used for building stats
# format is {'ext':['Names']} eg. {'py':['Python']} note: there can be
# more than one name for extension
# NOTE: that this will override any mappings in LANGUAGES_EXTENSIONS_MAP
# build by pygments
EXTRA_MAPPINGS = {}

# additional lexer definitions for custom files it's overrides pygments lexers,
# and uses defined name of lexer to colorize the files. Format is {'ext':
# 'lexer_name'} List of lexers can be printed running:
#   >> python -c "import pprint;from pygments import lexers;
#           pprint.pprint([(x[0], x[1]) for x in lexers.get_all_lexers()]);"

EXTRA_LEXERS = {}


CONFIG = DotDict(
    slack=DotDict(
        api_key='api-key',
        api_url='slack-incoming-hook-url',
        default_room='#slack-channel',
        default_plugin_config={},
    ),
    redmine=DotDict(
        api_key='api-key',
        default_tracker_url='https://redmine.tracker.url',
        default_project_id=None,
        default_status_resolved_id=3
    ),
)

# slack conf
CONFIG.slack.default_plugin_config = {
    'INCOMING_WEBHOOK_URL': CONFIG.slack.api_url,
    'SLACK_TOKEN': CONFIG.slack.api_key,
    'SLACK_ROOM': CONFIG.slack.default_room,
    'SLACK_FROM': 'RhodeCode',
    'SLACK_FROM_ICON_EMOJI': ':rhodecode:',
}

# redmine smart_pr configuration
def configure_redmine_smart_pr(issues, kwargs):
    kwargs['REDMINE_ISSUES'] = issues
    kwargs['redmine_tracker_url'] = kwargs.pop(
        'redmine_tracker_url', '') or CONFIG.redmine.default_tracker_url
    kwargs['redmine_api_key'] = kwargs.pop(
        'redmine_api_key', '') or CONFIG.redmine.api_key
    kwargs['redmine_project_id'] = kwargs.pop(
        'redmine_project_id', '') or CONFIG.redmine.default_project_id


@register('CREATE_REPO_HOOK')
@verify_kwargs(
    ['_load_extension', 'repo_name', 'repo_type', 'description', 'private',
     'created_on', 'enable_downloads', 'repo_id', 'user_id', 'enable_statistics',
     'clone_uri', 'fork_id', 'group_id', 'created_by'])
def _create_repo_hook(*args, **kwargs):
    """
    POST CREATE REPOSITORY HOOK. This function will be executed after
    each repository is created. kwargs available:

    :param repo_name:
    :param repo_type:
    :param description:
    :param private:
    :param created_on:
    :param enable_downloads:
    :param repo_id:
    :param user_id:
    :param enable_statistics:
    :param clone_uri:
    :param fork_id:
    :param group_id:
    :param created_by:
    """
    return 0
CREATE_REPO_HOOK = _create_repo_hook


@register('CREATE_REPO_GROUP_HOOK')
@verify_kwargs(
    ['_load_extension', 'group_name', 'group_parent_id', 'group_description',
     'group_id', 'user_id', 'created_by', 'created_on', 'enable_locking'])
def _create_repo_group_hook(*args, **kwargs):
    """
    POST CREATE REPOSITORY GROUP HOOK, this function will be
    executed after each repository group is created. kwargs available:

    :param group_name:
    :param group_parent_id:
    :param group_description:
    :param group_id:
    :param user_id:
    :param created_by:
    :param created_on:
    :param enable_locking:
    """
    return 0
CREATE_REPO_GROUP_HOOK = _create_repo_group_hook


@register('PRE_CREATE_USER_HOOK')
@verify_kwargs(
    ['_load_extension', 'username', 'password', 'email', 'firstname',
     'lastname', 'active', 'admin', 'created_by'])
def _pre_create_user_hook(*args, **kwargs):
    """
    PRE CREATE USER HOOK, this function will be executed before each
    user is created, it returns a tuple of bool, reason.
    If bool is False the user creation will be stopped and reason
    will be displayed to the user. kwargs available:

    :param username:
    :param password:
    :param email:
    :param firstname:
    :param lastname:
    :param active:
    :param admin:
    :param created_by:
    """

    reason = 'allowed'
    return True, reason
PRE_CREATE_USER_HOOK = _pre_create_user_hook


@register('CREATE_USER_HOOK')
@verify_kwargs(
    ['_load_extension', 'username', 'full_name_or_username', 'full_contact',
     'user_id', 'name', 'firstname', 'short_contact', 'admin', 'lastname',
     'ip_addresses', 'extern_type', 'extern_name', 'email', 'api_key',
     'api_keys', 'last_login', 'full_name', 'active', 'password', 'emails',
     'inherit_default_permissions', 'created_by', 'created_on'])
def _create_user_hook(*args, **kwargs):
    """
    POST CREATE USER HOOK, this function will be executed after each user is created
    kwargs available:

    :param username:
    :param full_name_or_username:
    :param full_contact:
    :param user_id:
    :param name:
    :param firstname:
    :param short_contact:
    :param admin:
    :param lastname:
    :param ip_addresses:
    :param extern_type:
    :param extern_name:
    :param email:
    :param api_key:
    :param api_keys:
    :param last_login:
    :param full_name:
    :param active:
    :param password:
    :param emails:
    :param inherit_default_permissions:
    :param created_by:
    :param created_on:
    """
    return 0
CREATE_USER_HOOK = _create_user_hook


@register('DELETE_REPO_HOOK')
@verify_kwargs(
    ['_load_extension', 'repo_name', 'repo_type', 'description', 'private',
     'created_on', 'enable_downloads', 'repo_id', 'user_id', 'enable_statistics',
     'clone_uri', 'fork_id', 'group_id', 'deleted_by', 'deleted_on'])
def _delete_repo_hook(*args, **kwargs):
    """
    POST DELETE REPOSITORY HOOK, this function will be executed after
    each repository deletion kwargs available:

    :param repo_name:
    :param repo_type:
    :param description:
    :param private:
    :param created_on:
    :param enable_downloads:
    :param repo_id:
    :param user_id:
    :param enable_statistics:
    :param clone_uri:
    :param fork_id:
    :param group_id:
    :param deleted_by:
    :param deleted_on:
    """
    return 0
DELETE_REPO_HOOK = _delete_repo_hook


@register('DELETE_USER_HOOK')
@verify_kwargs(
    ['_load_extension', 'username', 'full_name_or_username', 'full_contact',
     'user_id', 'name', 'firstname', 'short_contact', 'admin', 'lastname',
     'ip_addresses', 'email', 'api_key', 'last_login', 'full_name', 'active',
     'password', 'emails', 'inherit_default_permissions', 'deleted_by'
     ])
def _delete_user_hook(*args, **kwargs):
    """
    POST DELETE USER HOOK, this function will be executed after each
    user is deleted kwargs available:

    :param username:
    :param full_name_or_username:
    :param full_contact:
    :param user_id:
    :param name:
    :param firstname:
    :param short_contact:
    :param admin:
    :param lastname:
    :param ip_addresses:
    :param ldap_dn:
    :param email:
    :param api_key:
    :param last_login:
    :param full_name:
    :param active:
    :param password:
    :param emails:
    :param inherit_default_permissions:
    :param deleted_by:
    """
    return 0
DELETE_USER_HOOK = _delete_user_hook


@register('PRE_PUSH_HOOK')
@verify_kwargs(
    ['_load_extension', 'server_url', 'config', 'scm', 'username',
     'ip', 'action', 'repository', 'repo_store_path'])
def _pre_push_hook(*args, **kwargs):
    """
    Post push hook
    kwargs available:

      :param server_url: url of instance that triggered this hook
      :param config: path to .ini config used
      :param scm: type of VS 'git' or 'hg'
      :param username: name of user who pushed
      :param ip: ip of who pushed
      :param action: push
      :param repository: repository name
      :param repo_store_path: full path to where repositories are stored
    """
    return 0
PRE_PUSH_HOOK = _pre_push_hook


@register('PUSH_HOOK')
@verify_kwargs(
    ['_load_extension', 'server_url', 'config', 'scm', 'username',
     'ip', 'action', 'repository', 'repo_store_path', 'pushed_revs'])
def _push_hook(*args, **kwargs):
    """
    POST PUSH HOOK, this function will be executed after each push it's
    executed after the build-in hook that RhodeCode uses for logging pushes
    kwargs available:

    :param server_url: url of instance that triggered this hook
    :param config: path to .ini config used
    :param scm: type of VS 'git' or 'hg'
    :param username: name of user who pushed
    :param ip: ip of who pushed
    :param action: push
    :param repository: repository name
    :param repo_store_path: full path to where repositories are stored
    :param pushed_revs: list of pushed commit ids
    """
    # backward compat
    kwargs['commit_ids'] = kwargs['pushed_revs']

    # fetch extra fields from repository
    call = load_extension('extra_fields.py')
    _extra_fields = {}
    if call:
        repo_extra_fields = call(**kwargs)
        # now update if we have extra fields, they have precedence
        # this way users can store any configuration inside the database per
        # repo
        for key, data in repo_extra_fields.items():
            kwargs[key] = data['field_value']
            _extra_fields[key] = data['field_value']

    # fetch pushed commits, from commit_ids list
    call = load_extension('extract_commits.py')
    extracted_commits = {}
    if call:
        extracted_commits = call(**kwargs)
        # store the commits for the next call chain
    kwargs['COMMITS'] = extracted_commits

    # slack !
    call = load_extension('slack_push_notify.py')
    if call:
        kwargs.update(CONFIG.slack.default_plugin_config)
        call(**kwargs)

    # fetch redmine issues from given commits
    call = load_extension('extract_redmine_issues.py')
    issues = {}
    if call:
        issues = call(**kwargs)

    # redmine smart commits
    call = load_extension('redmine_smart_commits.py')
    if call:
        kwargs['REDMINE_ISSUES'] = issues

        kwargs['redmine_tracker_url'] = kwargs.pop(
            'redmine_tracker_url', '') or CONFIG.redmine.default_tracker_url
        kwargs['redmine_api_key'] = kwargs.pop(
            'redmine_api_key', '') or CONFIG.redmine.api_key
        kwargs['redmine_status_resolved_id'] = kwargs.pop(
            'redmine_status_resolved_id', '') or CONFIG.redmine.default_status_resolved_id
        kwargs['redmine_project_id'] = kwargs.pop(
            'redmine_project_id', '') or CONFIG.redmine.default_project_id
        call(**kwargs)

    return 0
PUSH_HOOK = _push_hook


@register('PRE_PULL_HOOK')
@verify_kwargs(
    ['_load_extension', 'server_url', 'config', 'scm', 'username', 'ip',
     'action', 'repository'])
def _pre_pull_hook(*args, **kwargs):
    """
    Post pull hook
    kwargs available::

      :param server_url: url of instance that triggered this hook
      :param config: path to .ini config used
      :param scm: type of VS 'git' or 'hg'
      :param username: name of user who pulled
      :param ip: ip of who pulled
      :param action: pull
      :param repository: repository name
    """
    return 0
PRE_PULL_HOOK = _pre_pull_hook


@register('PULL_HOOK')
@verify_kwargs(
    ['_load_extension', 'server_url', 'config', 'scm', 'username', 'ip',
     'action', 'repository'])
def _pull_hook(*args, **kwargs):
    """
    POST PULL HOOK, this function will be executed after each push it's
    executed after the build-in hook that RhodeCode uses for logging pulls

    kwargs available:

    :param server_url: url of instance that triggered this hook
    :param config: path to .ini config used
    :param scm: type of VS 'git' or 'hg'
    :param username: name of user who pulled
    :param ip: ip of who pulled
    :param action: pull
    :param repository: repository name
    """
    return 0
PULL_HOOK = _pull_hook


# =============================================================================
#  PULL REQUEST RELATED HOOKS
# =============================================================================
@register('CREATE_PULL_REQUEST')
@verify_kwargs(
    ['_load_extension', 'server_url', 'config', 'scm', 'username', 'ip',
     'action', 'repository', 'pull_request_id', 'url', 'title', 'description',
     'status', 'created_on', 'updated_on', 'commit_ids', 'review_status',
     'mergeable', 'source', 'target', 'author', 'reviewers'])
def _create_pull_request_hook(*args, **kwargs):
    """

    """
    # extract extra fields and default reviewers from target
    kwargs['REPOSITORY'] = kwargs['target']['repository']

    call = load_extension('extra_fields.py')
    if call:
        repo_extra_fields = call(**kwargs)
        # now update if we have extra fields, they have precedence
        # this way users can store any configuration inside the database per
        # repo
        for key, data in repo_extra_fields.items():
            kwargs[key] = data['field_value']

    call = load_extension('default_reviewers.py')
    if call:
        # read default_reviewers key propagated from extra fields
        kwargs['default_reviewers'] = map(string.strip, kwargs.pop(
            'default_reviewers', '').split(','))
        call(**kwargs)

    # extract below from source repo as commits are there
    kwargs['REPOSITORY'] = kwargs['source']['repository']

    # # fetch pushed commits, from commit_ids list
    # call = load_extension('extract_commits.py')
    # extracted_commits = {}
    # if call:
    #     extracted_commits = call(**kwargs)
    #     # store the commits for the next call chain
    # kwargs['COMMITS'] = extracted_commits
    #
    # # fetch issues from given commits
    # call = load_extension('extract_redmine_issues.py')
    # issues = {}
    # if call:
    #     issues = call(**kwargs)
    #
    # # redmine smart pr update
    # call = load_extension('redmine_pr_flow.py')
    # if call:
    #     # updates kwargs on the fly
    #     configure_redmine_smart_pr(issues=issues, kwargs=kwargs)
    #     call(**kwargs)
    #
    # # slack notification on merging PR
    # call = load_extension('slack_message.py')
    # if call:
    #     kwargs.update(CONFIG.slack.default_plugin_config)
    #     kwargs['SLACK_ROOM'] = '#develop'
    #     kwargs['SLACK_MESSAGE'] = 'Pull request <%s|#%s> (%s) was created.' % (
    #         kwargs.get('url'), kwargs.get('pull_request_id'), kwargs.get('title'))
    #
    #     call(**kwargs)

    return 0
CREATE_PULL_REQUEST = _create_pull_request_hook


@register('REVIEW_PULL_REQUEST')
@verify_kwargs(
    ['_load_extension', 'server_url', 'config', 'scm', 'username', 'ip',
     'action', 'repository', 'pull_request_id', 'url', 'title', 'description',
     'status', 'created_on', 'updated_on', 'commit_ids', 'review_status',
     'mergeable', 'source', 'target', 'author', 'reviewers'])
def _review_pull_request_hook(*args, **kwargs):
    """

    """
    # extract extra fields and default reviewers from target
    kwargs['REPOSITORY'] = kwargs['target']['repository']

    # fetch extra fields
    call = load_extension('extra_fields.py')
    if call:
        repo_extra_fields = call(**kwargs)
        # now update if we have extra fields, they have precedence
        # this way users can store any configuration inside the database per
        # repo
        for key, data in repo_extra_fields.items():
            kwargs[key] = data['field_value']

    # extract below from source repo as commits are there
    kwargs['REPOSITORY'] = kwargs['source']['repository']

    # fetch pushed commits, from commit_ids list
    call = load_extension('extract_commits.py')
    extracted_commits = {}
    if call:
        extracted_commits = call(**kwargs)
        # store the commits for the next call chain
    kwargs['COMMITS'] = extracted_commits

    # fetch issues from given commits
    call = load_extension('extract_redmine_issues.py')
    issues = {}
    if call:
        issues = call(**kwargs)

    # redmine smart pr update
    call = load_extension('redmine_pr_flow.py')
    if call:
        # updates kwargs on the fly
        configure_redmine_smart_pr(issues=issues, kwargs=kwargs)
        call(**kwargs)

    return 0
REVIEW_PULL_REQUEST = _review_pull_request_hook


@register('UPDATE_PULL_REQUEST')
@verify_kwargs(
    ['_load_extension', 'server_url', 'config', 'scm', 'username', 'ip',
     'action', 'repository', 'pull_request_id', 'url', 'title', 'description',
     'status', 'created_on', 'updated_on', 'commit_ids', 'review_status',
     'mergeable', 'source', 'target', 'author', 'reviewers'])
def _update_pull_request_hook(*args, **kwargs):
    """

    """
    # extract extra fields and default reviewers from target
    kwargs['REPOSITORY'] = kwargs['target']['repository']

    # fetch extra fields
    call = load_extension('extra_fields.py')
    if call:
        repo_extra_fields = call(**kwargs)
        # now update if we have extra fields, they have precedence
        # this way users can store any configuration inside the database per
        # repo
        for key, data in repo_extra_fields.items():
            kwargs[key] = data['field_value']

    # extract below from source repo as commits are there
    kwargs['REPOSITORY'] = kwargs['source']['repository']

    # fetch pushed commits, from commit_ids list
    call = load_extension('extract_commits.py')
    extracted_commits = {}
    if call:
        extracted_commits = call(**kwargs)
        # store the commits for the next call chain
    kwargs['COMMITS'] = extracted_commits

    # fetch issues from given commits
    call = load_extension('extract_redmine_issues.py')
    issues = {}
    if call:
        issues = call(**kwargs)

    # redmine smart pr updated
    call = load_extension('redmine_pr_flow.py')
    if call:
        # updates kwargs on the fly
        configure_redmine_smart_pr(issues=issues, kwargs=kwargs)
        call(**kwargs)

    return 0
UPDATE_PULL_REQUEST = _update_pull_request_hook


@register('MERGE_PULL_REQUEST')
@verify_kwargs(
    ['_load_extension', 'server_url', 'config', 'scm', 'username', 'ip',
     'action', 'repository', 'pull_request_id', 'url', 'title', 'description',
     'status', 'created_on', 'updated_on', 'commit_ids', 'review_status',
     'mergeable', 'source', 'target', 'author', 'reviewers'])
def _merge_pull_request_hook(*args, **kwargs):
    """

    """
    # extract extra fields and default reviewers from target
    kwargs['REPOSITORY'] = kwargs['target']['repository']

    # fetch extra fields
    call = load_extension('extra_fields.py')
    if call:
        repo_extra_fields = call(**kwargs)
        # now update if we have extra fields, they have precedence
        # this way users can store any configuration inside the database per
        # repo
        for key, data in repo_extra_fields.items():
            kwargs[key] = data['field_value']

    # extract below from source repo as commits are there
    kwargs['REPOSITORY'] = kwargs['source']['repository']

    # fetch pushed commits, from commit_ids list
    call = load_extension('extract_commits.py')
    extracted_commits = {}
    if call:
        extracted_commits = call(**kwargs)
        # store the commits for the next call chain
    kwargs['COMMITS'] = extracted_commits

    # fetch issues from given commits
    call = load_extension('extract_redmine_issues.py')
    issues = {}
    if call:
        issues = call(**kwargs)

    # redmine smart pr update
    call = load_extension('redmine_pr_flow.py')
    if call:
        # updates kwargs on the fly
        configure_redmine_smart_pr(issues=issues, kwargs=kwargs)
        call(**kwargs)

    # slack notification on merging PR
    call = load_extension('slack_message.py')
    if call:
        kwargs.update(CONFIG.slack.default_plugin_config)
        kwargs['SLACK_ROOM'] = '#develop'
        kwargs['SLACK_MESSAGE'] = 'Pull request <%s|#%s> (%s) was merged.' % (
            kwargs.get('url'), kwargs.get('pull_request_id'), kwargs.get('title'))
        call(**kwargs)

    return 0
MERGE_PULL_REQUEST = _merge_pull_request_hook


@register('CLOSE_PULL_REQUEST')
@verify_kwargs(
    ['_load_extension', 'server_url', 'config', 'scm', 'username', 'ip',
     'action', 'repository', 'pull_request_id', 'url', 'title', 'description',
     'status', 'created_on', 'updated_on', 'commit_ids', 'review_status',
     'mergeable', 'source', 'target', 'author', 'reviewers'])
def _close_pull_request_hook(*args, **kwargs):
    """

    """
    # extract extra fields and default reviewers from target
    kwargs['REPOSITORY'] = kwargs['target']['repository']

    # fetch extra fields
    call = load_extension('extra_fields.py')
    if call:
        repo_extra_fields = call(**kwargs)
        # now update if we have extra fields, they have precedence
        # this way users can store any configuration inside the database per
        # repo
        for key, data in repo_extra_fields.items():
            kwargs[key] = data['field_value']

    # extract below from source repo as commits are there
    kwargs['REPOSITORY'] = kwargs['source']['repository']

    # fetch pushed commits, from commit_ids list
    call = load_extension('extract_commits.py')
    extracted_commits = {}
    if call:
        extracted_commits = call(**kwargs)
        # store the commits for the next call chain
    kwargs['COMMITS'] = extracted_commits

    # fetch issues from given commits
    call = load_extension('extract_redmine_issues.py')
    issues = {}
    if call:
        issues = call(**kwargs)

    # redmine smart pr update
    call = load_extension('redmine_pr_flow.py')
    if call:
        # updates kwargs on the fly
        configure_redmine_smart_pr(issues=issues, kwargs=kwargs)
        call(**kwargs)

    return 0
CLOSE_PULL_REQUEST = _close_pull_request_hook