"""Utility functions for Raptor"""

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import os
from collections.abc import Iterable

import yaml
from logger.logger import RaptorLogger
from mozgeckoprofiler import view_gecko_profile

LOG = RaptorLogger(component="raptor-utils")
here = os.path.dirname(os.path.realpath(__file__))

external_tools_path = os.environ.get("EXTERNALTOOLSPATH", None)

if external_tools_path is not None:
    # running in production via mozharness
    TOOLTOOL_PATH = os.path.join(external_tools_path, "tooltool.py")


def strtobool(value: str):
    # Copied from `mach.util` since this script can run outside of a mach environment
    # Reimplementation of distutils.util.strtobool
    # https://docs.python.org/3.9/distutils/apiref.html#distutils.util.strtobool
    true_vals = ("y", "yes", "t", "true", "on", "1")
    false_vals = ("n", "no", "f", "false", "off", "0")

    value = value.lower()
    if value in true_vals:
        return 1
    if value in false_vals:
        return 0

    raise ValueError(f"Expected one of: {', '.join(true_vals + false_vals)}")


def flatten(data, parent_dir, sep="/"):
    """
    Converts a dictionary with nested entries like this
        {
            "dict1": {
                "dict2": {
                    "key1": value1,
                    "key2": value2,
                    ...
                },
                ...
            },
            ...
            "dict3": {
                "key3": value3,
                "key4": value4,
                ...
            }
            ...
        }

    to a "flattened" dictionary like this that has no nested entries:
        {
            "dict1-dict2-key1": value1,
            "dict1-dict2-key2": value2,
            ...
            "dict3-key3": value3,
            "dict3-key4": value4,
            ...
        }

    :param Iterable data : json data.
    :param tuple parent_dir: json fields.

    :return dict: {subtest: value}
    """
    result = {}

    if not data:
        return result

    if isinstance(data, list):
        for item in data:
            for k, v in flatten(item, parent_dir, sep=sep).items():
                result.setdefault(k, []).extend(v)
    elif isinstance(data, dict):
        for k, v in data.items():
            current_dir = parent_dir + (k,)
            subtest = sep.join(current_dir)
            if isinstance(v, Iterable) and not isinstance(v, str):
                for x, y in flatten(v, current_dir, sep=sep).items():
                    result.setdefault(x, []).extend(y)
            elif v or v == 0:
                result.setdefault(subtest, []).append(v)
    else:
        result.setdefault(sep.join(parent_dir), []).append(data)

    return result


def transform_platform(
    str_to_transform, config_platform, config_processor=None, mitmproxy_version=None
):
    """Transform platform name i.e. 'mitmproxy-rel-bin-{platform}.manifest'
    transforms to 'mitmproxy-rel-bin-osx.manifest'.
    Also transform '{x64}' if needed for 64 bit / win 10"""
    if "{platform}" not in str_to_transform and "{x64}" not in str_to_transform:
        return str_to_transform

    if "win" in config_platform:
        platform_id = "win"
    elif config_platform == "mac":
        # Bug 1920821
        # If we are using mitmproxy 11 we need to ensure platform_id is configured
        # correctly for the folder structure. Having this check also keeps the ability to
        # playback on older versions which don't have ARM support
        if config_processor == "arm" and mitmproxy_version == "11.0.0":
            platform_id = "osx-arm64"
        else:
            platform_id = "osx"
    else:
        platform_id = "linux64"

    if "{platform}" in str_to_transform:
        str_to_transform = str_to_transform.replace("{platform}", platform_id)

    if "{x64}" in str_to_transform and config_processor is not None:
        if "x86_64" in config_processor:
            str_to_transform = str_to_transform.replace("{x64}", "_x64")
        else:
            str_to_transform = str_to_transform.replace("{x64}", "")

    return str_to_transform


def transform_subtest(str_to_transform, subtest_name):
    """Transform subtest name i.e. 'mitm5-linux-firefox-{subtest}.manifest'
    transforms to 'mitm5-linux-firefox-amazon.manifest'."""
    if "{subtest}" not in str_to_transform:
        return str_to_transform

    return str_to_transform.replace("{subtest}", subtest_name)


def view_gecko_profile_from_raptor():
    # automatically load the latest raptor gecko-profile archive in profiler.firefox.com
    LOG_GECKO = RaptorLogger(component="raptor-view-gecko-profile")

    profile_zip_path = os.environ.get("RAPTOR_LATEST_GECKO_PROFILE_ARCHIVE", None)
    if profile_zip_path is None or not os.path.exists(profile_zip_path):
        LOG_GECKO.info(
            "No local raptor gecko profiles were found so not "
            "launching profiler.firefox.com"
        )
        return

    LOG_GECKO.info("Profile saved locally to: %s" % profile_zip_path)
    view_gecko_profile(profile_zip_path)


def write_yml_file(yml_file, yml_data):
    # write provided data to specified local yaml file
    LOG.info("writing %s to %s" % (yml_data, yml_file))

    try:
        with open(yml_file, "w") as outfile:
            yaml.dump(yml_data, outfile, default_flow_style=False)
    except Exception as e:
        LOG.critical("failed to write yaml file, exeption: %s" % e)


def bool_from_str(boolean_string):
    return bool(strtobool(boolean_string))
