# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you 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.    


import time
import re
import rapidjson

from service.sdk_validator.resdb_validator.exceptions import ValidationError


def gen_timestamp():
    """The Unix time, rounded to the nearest second.
        See https://en.wikipedia.org/wiki/Unix_time

        Returns:
            str: the Unix time
    """
    return str(round(time.time()))


def serialize(data):
    """Serialize a dict into a JSON formatted string.

        This function enforces rules like the separator and order of keys.
        This ensures that all dicts are serialized in the same way.

        This is specially important for hashing data. We need to make sure that
        everyone serializes their data in the same way so that we do not have
        hash mismatches for the same structure due to serialization
        differences.

        Args:
            data (dict): dict to serialize

        Returns:
            str: JSON formatted string

    """
    return rapidjson.dumps(data, skipkeys=False, ensure_ascii=False,
                           sort_keys=True)


def deserialize(data):
    """Deserialize a JSON formatted string into a dict.

        Args:
            data (str): JSON formatted string.

        Returns:
            dict: dict resulting from the serialization of a JSON formatted
            string.
    """
    return rapidjson.loads(data)


def validate_txn_obj(obj_name, obj, key, validation_fun):
    """Validate value of `key` in `obj` using `validation_fun`.

        Args:
            obj_name (str): name for `obj` being validated.
            obj (dict): dictionary object.
            key (str): key to be validated in `obj`.
            validation_fun (function): function used to validate the value
            of `key`.

        Returns:
            None: indicates validation successful

        Raises:
            ValidationError: `validation_fun` will raise exception on failure
    """
    print("No backend yet")
    # backend = resdb_validator.config['database']['backend']
    backend = None

    if backend == 'localmongodb':
        data = obj.get(key, {})
        if isinstance(data, dict):
            validate_all_keys_in_obj(obj_name, data, validation_fun)
        elif isinstance(data, list):
            validate_all_items_in_list(obj_name, data, validation_fun)


def validate_all_items_in_list(obj_name, data, validation_fun):
    for item in data:
        if isinstance(item, dict):
            validate_all_keys_in_obj(obj_name, item, validation_fun)
        elif isinstance(item, list):
            validate_all_items_in_list(obj_name, item, validation_fun)


def validate_all_keys_in_obj(obj_name, obj, validation_fun):
    """Validate all (nested) keys in `obj` by using `validation_fun`.

        Args:
            obj_name (str): name for `obj` being validated.
            obj (dict): dictionary object.
            validation_fun (function): function used to validate the value
            of `key`.

        Returns:
            None: indicates validation successful

        Raises:
            ValidationError: `validation_fun` will raise this error on failure
    """
    for key, value in obj.items():
        validation_fun(obj_name, key)
        if isinstance(value, dict):
            validate_all_keys_in_obj(obj_name, value, validation_fun)
        elif isinstance(value, list):
            validate_all_items_in_list(obj_name, value, validation_fun)


def validate_all_values_for_key_in_obj(obj, key, validation_fun):
    """Validate value for all (nested) occurrence  of `key` in `obj`
       using `validation_fun`.

        Args:
            obj (dict): dictionary object.
            key (str): key whose value is to be validated.
            validation_fun (function): function used to validate the value
            of `key`.

        Raises:
            ValidationError: `validation_fun` will raise this error on failure
    """
    for vkey, value in obj.items():
        if vkey == key:
            validation_fun(value)
        elif isinstance(value, dict):
            validate_all_values_for_key_in_obj(value, key, validation_fun)
        elif isinstance(value, list):
            validate_all_values_for_key_in_list(value, key, validation_fun)


def validate_all_values_for_key_in_list(input_list, key, validation_fun):
    for item in input_list:
        if isinstance(item, dict):
            validate_all_values_for_key_in_obj(item, key, validation_fun)
        elif isinstance(item, list):
            validate_all_values_for_key_in_list(item, key, validation_fun)


def validate_key(obj_name, key):
    """Check if `key` contains ".", "$" or null characters.

       https://docs.mongodb.com/manual/reference/limits/#Restrictions-on-Field-Names

        Args:
            obj_name (str): object name to use when raising exception
            key (str): key to validated

        Returns:
            None: validation successful

        Raises:
            ValidationError: will raise exception in case of regex match.
    """
    if re.search(r'^[$]|\.|\x00', key):
        error_str = ('Invalid key name "{}" in {} object. The '
                     'key name cannot contain characters '
                     '".", "$" or null characters').format(key, obj_name)
        raise ValidationError(error_str)
