# Copyright 2020 The StackStorm Authors.
# Copyright 2019 Extreme Networks, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import absolute_import

import os
import re

import six

from collections.abc import Iterable
from st2common.util import schema as util_schema
from st2common.constants.pack import MANIFEST_FILE_NAME
from st2common.constants.pack import PACK_REF_WHITELIST_REGEX
from st2common.content.loader import MetaLoader
from st2common.persistence.pack import Pack
from st2common.exceptions.apivalidation import ValueValidationException
from st2common.util import jinja as jinja_utils

__all__ = [
    "get_pack_ref_from_metadata",
    "get_pack_metadata",
    "get_pack_warnings",
    "get_pack_common_libs_path_for_pack_ref",
    "get_pack_common_libs_path_for_pack_db",
    "validate_config_against_schema",
    "normalize_pack_version",
]

# Common format for python 2.7 warning
if six.PY2:
    PACK_PYTHON2_WARNING = (
        "DEPRECATION WARNING: Pack %s only supports Python 2.x. "
        "Python 2 support will be dropped in future releases. "
        "Please consider updating your packs to work with Python 3.x"
    )
else:
    PACK_PYTHON2_WARNING = (
        "DEPRECATION WARNING: Pack %s only supports Python 2.x. "
        "Python 2 support has been removed since st2 v3.4.0. "
        "Please update your packs to work with Python 3.x"
    )


def get_pack_ref_from_metadata(metadata, pack_directory_name=None):
    """
    Utility function which retrieves pack "ref" attribute from the pack metadata file.

    If this attribute is not provided, an attempt is made to infer "ref" from the "name" attribute.

    :rtype: ``str``
    """
    pack_ref = None

    # The rules for the pack ref are as follows:
    # 1. If ref attribute is available, we used that
    # 2. If pack_directory_name is available we use that (this only applies to packs
    # which are in sub-directories)
    # 2. If attribute is not available, but pack name is and pack name meets the valid name
    # criteria, we use that
    if metadata.get("ref", None):
        pack_ref = metadata["ref"]
    elif pack_directory_name and re.match(
        PACK_REF_WHITELIST_REGEX, pack_directory_name
    ):
        pack_ref = pack_directory_name
    else:
        if re.match(PACK_REF_WHITELIST_REGEX, metadata["name"]):
            pack_ref = metadata["name"]
        else:
            msg = (
                'Pack name "%s" contains invalid characters and "ref" attribute is not '
                'available. You either need to add "ref" attribute which contains only word '
                "characters to the pack metadata file or update name attribute to contain only"
                "word characters."
            )
            raise ValueError(msg % (metadata["name"]))

    return pack_ref


def get_pack_metadata(pack_dir):
    """
    Return parsed metadata for a particular pack directory.

    :rtype: ``dict``
    """
    manifest_path = os.path.join(pack_dir, MANIFEST_FILE_NAME)

    if not os.path.isfile(manifest_path):
        raise ValueError(
            'Pack "%s" is missing %s file' % (pack_dir, MANIFEST_FILE_NAME)
        )

    meta_loader = MetaLoader()
    content = meta_loader.load(manifest_path)
    if not content:
        raise ValueError('Pack "%s" metadata file is empty' % (pack_dir))

    return content


def get_pack_warnings(pack_metadata):
    """
    Return warning string if pack metadata indicates only python 2 is supported

    :rtype: ``str``
    """
    warning = None
    versions = pack_metadata.get("python_versions", None)
    pack_name = pack_metadata.get("name", None)
    if versions and set(versions) == set(["2"]):
        warning = PACK_PYTHON2_WARNING % pack_name
    return warning


def validate_config_against_schema(
    config_schema, config_object, config_path, pack_name=None
):
    """
    Validate provided config dictionary against the provided config schema
    dictionary.
    """
    # NOTE: Lazy improt to avoid performance overhead of importing this module when it's not used
    import jsonschema

    pack_name = pack_name or "unknown"

    schema = util_schema.get_schema_for_resource_parameters(
        parameters_schema=config_schema, allow_additional_properties=True
    )
    instance = config_object

    try:
        cleaned = util_schema.validate(
            instance=instance,
            schema=schema,