#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2019 tribe29 GmbH - License: GNU General Public License v2
# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
# conditions defined in the file COPYING, which is part of this source code package.

import json
from typing import Any, Dict, Mapping, Optional, Tuple, Union

from cmk.utils import password_store

# TODO: Cleanup this separation violation. This should be accessed through some API
from cmk.base.config import SpecialAgentConfiguration


def _get_tag_options(tag_values, prefix):
    options = []
    for key, values in tag_values:
        options.append("--%s-tag-key" % prefix)
        options.append(key)
        options.append("--%s-tag-values" % prefix)
        options += values
    return options


def _get_services_config(services):
    # '--services': {
    #   's3': {'selection': ('tags', [('KEY', ['VAL1', 'VAL2'])])},
    #   'ec2': {'selection': 'all'},
    #   'ebs': {'selection': ('names', ['ebs1', 'ebs2'])},
    # }
    service_args = []
    for service_name, service_config in services.items():
        if service_config is None:
            continue

        if service_config.get("limits"):
            service_args += ["--%s-limits" % service_name]

        selection = service_config.get("selection")
        if not isinstance(selection, tuple):
            # Here: value of selection is 'all' which means there's no
            # restriction (names or tags) to the instances of a specific
            # AWS service. The commandline option already includes this
            # service '--services SERVICE1 SERVICE2 ...' (see below).
            continue

        selection_type, selection_values = selection
        if not selection_values:
            continue

        if selection_type == "names":
            service_args.append("--%s-names" % service_name)
            service_args += selection_values

        elif selection_type == "tags":
            service_args += _get_tag_options(selection_values, service_name)
    return service_args


def _get_proxy_args_and_secrets(details):
    proxy_args = ["--proxy-host", details["proxy_host"]]

    if proxy_port := details.get("proxy_port"):
        proxy_args += ["--proxy-port", str(proxy_port)]

    proxy_secrets: Dict[str, str] = {}
    for secret in ("proxy_user", "proxy_password"):
        if secret in details:
            value = details[secret]
            if secret == "proxy_password":
                value = password_store.extract(details[secret])
            proxy_secrets[secret] = str(value)
    return proxy_args, proxy_secrets


def _agent_aws_stdin(
    *,
    access_key_id: str,
    secret_access_key: Union[str, Tuple[str, str]],
    proxy_secrets: Mapping[str, Any],
) -> str:
    if isinstance(secret_access_key, tuple):
        secret_access_key = password_store.extract(secret_access_key)

    return json.dumps(
        {
            "access_key_id": access_key_id,
            "secret_access_key": secret_access_key,
            **proxy_secrets,
        }
    )


def agent_aws_arguments(  # pylint: disable=too-many-branches
    params: Mapping[str, Any], hostname: str, ipaddress: Optional[str]
) -> SpecialAgentConfiguration:

    args, proxy_secrets = (
        _get_proxy_args_and_secrets(params["proxy_details"])
        if "proxy_details" in params
        else ([], {})
    )

    if params.get("assume_role"):
        args += ["--assume-role"]
        role_arn_id = params["assume_role"].get("role_arn_id")
        if role_arn_id:
            if role_arn_id[0]:
                args += ["--role-arn", role_arn_id[0]]
            if role_arn_id[1]:
                args += ["--external-id", role_arn_id[1]]

    regions = params.get("regions")
    if regions:
        args.append("--regions")
        args += regions

    global_services = params.get("global_services", {})
    if global_services:
        args.append("--global-services")
        # We need to sort the inner services-as-a-dict-params
        # in order to create reliable tests
        args += sorted(global_services)
        args += _get_services_config(global_services)

    services = params.get("services", {})

    # for backwards compatibility
    if "cloudwatch" in services:
        services["cloudwatch_alarms"] = services["cloudwatch"]
        del services["cloudwatch"]

    if services:
        args.append("--services")
        # We need to sort the inner services-as-a-dict-params
        # in order to create reliable tests
        args += sorted(services)
        args += _get_services_config(services)

    if "requests" in services.get("s3", {}):
        args += ["--s3-requests"]

    alarms = services.get("cloudwatch_alarms", {}).get("alarms")
    if alarms:
        # {'alarms': 'all'} is handled by no additionally specified names
        args += ["--cloudwatch-alarms"]
        if isinstance(alarms, tuple):
            args += alarms[1]

    if "cloudfront" in services.get("wafv2", {}):
        args += ["--wafv2-cloudfront"]

    if "cloudfront" in global_services:
        cloudfront_host_assignment = global_services["cloudfront"]["host_assignment"]
        args += ["--cloudfront-host-assignment", cloudfront_host_assignment]

    # '--overall-tags': [('KEY_1', ['VAL_1', 'VAL_2']), ...)],
    args += _get_tag_options(params.get("overall_tags", []), "overall")
    args += [
        "--hostname",
        hostname,
    ]
    return SpecialAgentConfiguration(
        args,
        _agent_aws_stdin(
            access_key_id=params["access_key_id"],
            secret_access_key=params["secret_access_key"],
            proxy_secrets=proxy_secrets,
        ),
    )


special_agent_info["aws"] = agent_aws_arguments
