# Copyright 2018 The Kubeflow Authors
#
# 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.

__all__ = [
    '_normalize_identifier_name',
    '_sanitize_kubernetes_resource_name',
    '_sanitize_python_function_name',
    '_sanitize_file_name',
    '_convert_to_human_name',
    '_generate_unique_suffix',
    '_make_name_unique_by_adding_index',
    '_convert_name_and_make_it_unique_by_adding_number',
    'generate_unique_name_conversion_table',
]

import sys
from typing import Callable, Sequence, Mapping


def _normalize_identifier_name(name):
    import re
    normalized_name = name.lower()
    normalized_name = re.sub(r'[\W_]', ' ',
                             normalized_name)  #No non-word characters
    normalized_name = re.sub(
        ' +', ' ',
        normalized_name).strip()  #No double spaces, leading or trailing spaces
    if re.match(r'\d', normalized_name):
        normalized_name = 'n' + normalized_name  #No leading digits
    return normalized_name


def _sanitize_kubernetes_resource_name(name):
    return _normalize_identifier_name(name).replace(' ', '-')


def _sanitize_python_function_name(name):
    return _normalize_identifier_name(name).replace(' ', '_')


def _sanitize_file_name(name):
    import re
    return re.sub('[^-_.0-9a-zA-Z]+', '_', name)


def _convert_to_human_name(name: str):
    """Converts underscore or dash delimited name to space-delimited name that
    starts with a capital letter.

    Does not handle "camelCase" names.
    """
    return name.replace('_', ' ').replace('-', ' ').strip().capitalize()


def _generate_unique_suffix(data):
    import time
    import hashlib
    string_data = str((data, time.time()))
    return hashlib.sha256(string_data.encode()).hexdigest()[0:8]


def _make_name_unique_by_adding_index(name: str, collection, delimiter: str):
    unique_name = name
    if unique_name in collection:
        for i in range(2, sys.maxsize**10):
            unique_name = name + delimiter + str(i)
            if unique_name not in collection:
                break
    return unique_name


def _convert_name_and_make_it_unique_by_adding_number(
        name: str, used_converted_names, conversion_func: Callable[[str], str]):
    converted_name = conversion_func(name)
    if converted_name in used_converted_names:
        for i in range(
                2, sys.maxsize**
                10):  #Starting indices from 2: "Something", "Something_2", ...
            converted_name = conversion_func(name + ' ' + str(i))
            if converted_name not in used_converted_names:
                break
    return converted_name


def generate_unique_name_conversion_table(
        names: Sequence[str],
        conversion_func: Callable[[str], str]) -> Mapping[str, str]:
    """Given a list of names and conversion_func, this function generates a map
    from original names to converted names that are made unique by adding
    numbers."""
    forward_map = {}
    reverse_map = {}

    # Names that do not change when applying the conversion_func should remain unchanged in the table
    names_that_need_conversion = []
    for name in names:
        if conversion_func(name) == name:
            forward_map[name] = name
            reverse_map[name] = name
        else:
            names_that_need_conversion.append(name)

    for name in names_that_need_conversion:
        if name in forward_map:
            raise ValueError('Original name {} is not unique.'.format(name))
        converted_name = _convert_name_and_make_it_unique_by_adding_number(
            name, reverse_map, conversion_func)
        forward_map[name] = converted_name
        reverse_map[converted_name] = name
    return forward_map
