#
# # Copyright © 2023 Peak AI Limited. or its affiliates. All Rights Reserved.
# #
# # Licensed under the Apache License, Version 2.0 (the "License"). You
# # may not use this file except in compliance with the License. A copy of
# # the License is located at:
# #
# # https://github.com/PeakBI/peak-sdk/blob/main/LICENSE
# #
# # or in the "license" file accompanying this file. This file 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.
# #
# # This file is part of the peak-sdk.
# # see (https://github.com/PeakBI/peak-sdk)
# #
# # You should have received a copy of the APACHE LICENSE, VERSION 2.0
# # along with this program. If not, see <https://apache.org/licenses/LICENSE-2.0>
#

"""Collection of basic helper functions."""
from __future__ import annotations

import inspect
import json
from types import FrameType
from typing import Any, Dict, List, Optional


def parse_body_for_multipart_request(body: Dict[str, Any]) -> Dict[str, str]:
    """Parses an object to make it suitable for passing in a multipart request.

    Args:
        body (Dict[str, Any]): the object to be parsed

    Returns:
        Dict[str, str]: the parsed object
    """
    return {key: (value if type(value) == str else json.dumps(value)) for (key, value) in body.items()}


def remove_keys(body: Dict[str, Any], keys: List[str]) -> Dict[str, Any]:
    """Removes given keys from a dictionary.

    Args:
        body (Dict[str, Any]): the object to be parsed
        keys (List[str]): the keys to remove

    Returns:
        Dict[str, str]: the final object with required keys removed
    """
    return {key: value for (key, value) in body.items() if key not in keys}


def get_base_domain(stage: str, subdomain: Optional[str] = "service") -> str:
    """Gets the base domain for a stage with the given subdomain.

    Args:
        stage (str): the stage
        subdomain (Optional[str]): the subdomain

    Returns:
        str: the final base domain
    """
    if stage == "prod":
        stage = ""
    elif stage == "latest":
        stage = "dev"

    domain: str = f"https://{subdomain}.{stage}.peak.ai"
    domain = domain.replace("..", ".")  # for prod domain
    return domain


def parse_list_of_strings(param: List[str] | None) -> List[str] | None:
    """Split comma separated strings in the list and flatten that list.

    Args:
       param (List[str] | None): List of strings

    Returns:
        List[Any] | None: The final flattened list
    """
    if param is None or len(param) == 0:
        return param

    result: List[str] = []
    for e in param:
        result = result + e.split(",")

    return result


def snake_case_to_lower_camel_case(snake_case_string: str) -> str:
    """Converts underscore string to lower camel case.

    Args:
        snake_case_string (str): string in underscore

    Returns:
        str: lower camel case string
    """
    parts = snake_case_string.split("_")
    return parts[0] + "".join(part.capitalize() for part in parts[1:])


def variables_to_dict(*args: Any, frame: FrameType | None = None) -> Dict[str, str]:
    """Converts arbitary variables to a dictonary.

    Args:
        args (str|int): tuple of string|int variables
        frame (FrameType|None): Current Frame of caller

    Returns:
        Dict[str, str]: Dictionary containing key value pair of variables
    """
    if frame is None:
        frame = inspect.currentframe()
        if frame:
            # set frame to previous
            frame = frame.f_back

    var_dict = {}
    if frame and frame.f_locals:
        for var_name, var_value in frame.f_locals.items():
            if var_value in args and var_value:
                var_dict[snake_case_to_lower_camel_case(var_name)] = var_value

    del frame  # Explicitly release the frame to avoid reference cycles
    return var_dict


def combine_dictionaries(
    dict1: Dict[str, Any],
    dict2: Dict[str, Any],
    nested_keys_to_skip: Optional[List[str]] = [],  # noqa: B006
) -> Dict[str, Any]:
    """Combines two dictonaries. Values for second dictonary have higer precedence.

    Args:
        dict1 (Dict[str, Any]): dictionary 1
        dict2 (Dict[str, Any]): dictonary 2
        nested_keys_to_skip (List[str] | None): Keys for which nested combining is not required.

    Returns:
        Dict[str, Any]: Combined dictionary
    """
    if not dict1:
        return dict2

    combined_dict = dict(dict1)
    for key in dict2:
        if key in combined_dict and type(combined_dict[key]) is dict and key not in (nested_keys_to_skip or []):
            combined_dict[key] = combine_dictionaries(combined_dict[key], dict2[key])
        else:
            combined_dict[key] = dict2[key]
    return combined_dict


def map_user_options(
    user_options: Dict[str, Any],
    mapping: Dict[str, str],
    dict_type_keys: List[str] = [],  # noqa: B006
) -> Dict[str, Any]:
    """Maps user provided inputs to a specific format.

    Args:
        user_options (Dict[str, Any]): Dictionary containing user inputs
        mapping (Dict[str, Any]): Mapping to be used for conversion
        dict_type_keys (List[str]): List of keys which have json type values

    Returns:
        Dict[str, str]: Mappe dictionary
    """
    result: Dict[str, Any] = {}
    for key in user_options:
        if key in mapping:
            nested_dict = result[mapping[key]] if mapping[key] in result else {}
            nested_dict[key] = json.loads(user_options[key]) if key in dict_type_keys else user_options[key]
            result[mapping[key]] = nested_dict
        else:
            result[key] = json.loads(user_options[key]) if key in dict_type_keys else user_options[key]
    return result
