# Copyright 2017 Amazon.com, Inc. 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
#
# http://aws.amazon.com/apache2.0/
#
# 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.
"""Unit test suite for ``aws_encryption_sdk_cli.key_providers``."""
import pytest
from aws_encryption_sdk.key_providers.kms import DiscoveryFilter
from mock import sentinel
from pytest_mock import mocker  # noqa pylint: disable=unused-import

from aws_encryption_sdk_cli import key_providers
from aws_encryption_sdk_cli.exceptions import BadUserArgumentError
from aws_encryption_sdk_cli.internal.identifiers import USER_AGENT_SUFFIX

pytestmark = [pytest.mark.unit, pytest.mark.local]


@pytest.fixture
def patch_deepcopy(mocker):
    mocker.patch.object(key_providers.copy, "deepcopy")
    yield key_providers.copy.deepcopy


@pytest.fixture
def patch_botocore_session(mocker):
    mocker.patch.object(key_providers.botocore.session, "Session")
    key_providers.botocore.session.Session.return_value = sentinel.botocore_session
    yield key_providers.botocore.session.Session


@pytest.fixture
def patch_discovery_master_key_provider(mocker):
    mocker.patch.object(key_providers, "MRKAwareDiscoveryAwsKmsMasterKeyProvider")
    yield key_providers.MRKAwareDiscoveryAwsKmsMasterKeyProvider


@pytest.fixture
def patch_strict_master_key_provider(mocker):
    mocker.patch.object(key_providers, "MRKAwareStrictAwsKmsMasterKeyProvider")
    yield key_providers.MRKAwareStrictAwsKmsMasterKeyProvider


@pytest.mark.parametrize(
    "source, expected",
    (
        ({}, {"botocore_session": sentinel.botocore_session}),  # empty baseline
        ({"discovery": True}, {"botocore_session": sentinel.botocore_session}),  # explicit discovery
        (  # arbitrary non-empty baseline
            {"a": "a thing", "b": "another thing"},
            {"a": "a thing", "b": "another thing", "botocore_session": sentinel.botocore_session},
        ),
        (  # region_names without region
            {"a": "a thing", "region_names": ["us-east-2", "ca-central-1"]},
            {
                "a": "a thing",
                "region_names": ["us-east-2", "ca-central-1"],
                "botocore_session": sentinel.botocore_session,
            },
        ),
        (  # region without region_names
            {"a": "a thing", "region": ["eu-central-1"]},
            {"a": "a thing", "region_names": ["eu-central-1"], "botocore_session": sentinel.botocore_session},
        ),
        (  # region and region_names specified
            {"a": "a thing", "region_names": ["us-east-2", "ca-central-1"], "region": ["eu-central-1"]},
            {"a": "a thing", "region_names": ["eu-central-1"], "botocore_session": sentinel.botocore_session},
        ),
        (  # profile specified
            {"a": "a thing", "profile": [sentinel.profile_name]},
            {"a": "a thing", "botocore_session": sentinel.botocore_session},
        ),
        (  # with discovery filter
            {"discovery": True, "discovery-account": ["123"], "discovery-partition": "aws"},
            {
                "botocore_session": sentinel.botocore_session,
                "discovery_filter": DiscoveryFilter(account_ids=["123"], partition="aws"),
            },
        ),
    ),
)
def test_discovery_master_key_provider_post_processing(
    patch_botocore_session, patch_discovery_master_key_provider, source, expected
):
    test = key_providers.aws_kms_master_key_provider(**source)

    patch_discovery_master_key_provider.assert_called_once_with(**expected)
    assert test is patch_discovery_master_key_provider.return_value


@pytest.mark.parametrize(
    "source, expected",
    (({"discovery": False, "key_ids": ["foo"]}, {"botocore_session": sentinel.botocore_session, "key_ids": ["foo"]}),),
)
def test_strict_master_key_provider_post_processing(
    patch_botocore_session, patch_strict_master_key_provider, source, expected
):
    test = key_providers.aws_kms_master_key_provider(**source)

    patch_strict_master_key_provider.assert_called_once_with(**expected)
    assert test is patch_strict_master_key_provider.return_value


def test_kms_master_key_provider_post_processing_named_profile(
    patch_botocore_session, patch_discovery_master_key_provider
):
    key_providers.aws_kms_master_key_provider(profile=["a profile name"])

    patch_botocore_session.assert_called_once_with(profile="a profile name")
    assert patch_botocore_session.return_value.user_agent_extra == USER_AGENT_SUFFIX


def test_kms_master_key_provider_post_processing_default_profile(
    patch_botocore_session, patch_discovery_master_key_provider
):
    key_providers.aws_kms_master_key_provider()

    patch_botocore_session.assert_called_once_with(profile=None)


@pytest.mark.parametrize("profile_names", ([], [sentinel.a, sentinel.b]))
def test_kms_master_key_provider_post_processing_not_one_profile(profile_names):
    with pytest.raises(BadUserArgumentError) as excinfo:
        key_providers.aws_kms_master_key_provider(profile=profile_names)

    excinfo.match(r"Only one profile may be specified per master key provider configuration. *")


@pytest.mark.parametrize("regions", ([], [sentinel.a, sentinel.b]))
def test_kms_master_key_provider_post_processing_not_one_region(regions):
    with pytest.raises(BadUserArgumentError) as excinfo:
        key_providers.aws_kms_master_key_provider(region=regions)

    excinfo.match(r"Only one region may be specified per master key provider configuration. *")