# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------
import functools
import unittest
import sys
from unittest.mock import Mock
import pytest
from devtools_testutils import EnvironmentVariableLoader
from devtools_testutils.aio import recorded_by_proxy_async
from testcase import has_feature_flag
from asynctestcase import AppConfigTestCase
from test_constants import (
    APPCONFIGURATION_ENDPOINT_STRING,
    APPCONFIGURATION_KEYVAULT_SECRET_URL,
    FEATURE_MANAGEMENT_KEY,
)
from azure.appconfiguration import ConfigurationSetting
from azure.appconfiguration.provider import WatchKey

AppConfigProviderPreparer = functools.partial(
    EnvironmentVariableLoader,
    "appconfiguration",
    appconfiguration_endpoint_string=APPCONFIGURATION_ENDPOINT_STRING,
    appconfiguration_keyvault_secret_url=APPCONFIGURATION_KEYVAULT_SECRET_URL,
)

try:
    # Python 3.7 does not support AsyncMock
    from unittest.mock import AsyncMock

    class TestAppConfigurationProvider(AppConfigTestCase, unittest.TestCase):
        # method: refresh
        @AppConfigProviderPreparer()
        @recorded_by_proxy_async
        @pytest.mark.skipif(sys.version_info < (3, 8), reason="Python 3.7 does not support AsyncMock")
        @pytest.mark.asyncio
        async def test_refresh(self, appconfiguration_endpoint_string, appconfiguration_keyvault_secret_url):
            mock_callback = Mock()
            async with await self.create_client(
                endpoint=appconfiguration_endpoint_string,
                keyvault_secret_url=appconfiguration_keyvault_secret_url,
                refresh_on=[WatchKey("refresh_message")],
                refresh_interval=1,
                on_refresh_success=mock_callback,
                feature_flag_enabled=True,
                feature_flag_refresh_enabled=True,
            ) as client:
                assert client["refresh_message"] == "original value"
                assert client["my_json"]["key"] == "value"
                assert FEATURE_MANAGEMENT_KEY in client
                assert has_feature_flag(client, "Alpha")

                appconfig_client = self.create_appconfig_client(appconfiguration_endpoint_string)

                setting = await appconfig_client.get_configuration_setting(key="refresh_message")
                setting.value = "updated value"
                feature_flag = await appconfig_client.get_configuration_setting(key=".appconfig.featureflag/Alpha")
                feature_flag.enabled = True
                await appconfig_client.set_configuration_setting(setting)
                await appconfig_client.set_configuration_setting(feature_flag)

                # Expire the refresh timers to simulate time passing
                client._refresh_timer._next_refresh_time = 0
                client._feature_flag_refresh_timer._next_refresh_time = 0

                await client.refresh()
                assert client["refresh_message"] == "updated value"
                assert has_feature_flag(client, "Alpha", True)
                assert mock_callback.call_count == 1

                setting.value = "original value"
                feature_flag.enabled = False
                await appconfig_client.set_configuration_setting(setting)
                await appconfig_client.set_configuration_setting(feature_flag)

                # Expire the refresh timers to simulate time passing
                client._refresh_timer._next_refresh_time = 0
                client._feature_flag_refresh_timer._next_refresh_time = 0

                await client.refresh()
                assert client["refresh_message"] == "original value"
                assert has_feature_flag(client, "Alpha", False)
                assert mock_callback.call_count == 2

                setting.value = "updated value 2"
                feature_flag.enabled = True
                await appconfig_client.set_configuration_setting(setting)
                await appconfig_client.set_configuration_setting(feature_flag)

                # Not waiting for the refresh interval to pass
                await client.refresh()
                assert client["refresh_message"] == "original value"
                assert has_feature_flag(client, "Alpha", False)
                assert mock_callback.call_count == 2

                setting.value = "original value"
                feature_flag.enabled = False
                await appconfig_client.set_configuration_setting(setting)
                await appconfig_client.set_configuration_setting(feature_flag)

                await client.refresh()
                assert client["refresh_message"] == "original value"
                assert mock_callback.call_count == 2

        # method: refresh
        @AppConfigProviderPreparer()
        @recorded_by_proxy_async
        @pytest.mark.skipif(sys.version_info < (3, 8), reason="Python 3.7 does not support AsyncMock")
        @pytest.mark.asyncio
        async def test_no_refresh(self, appconfiguration_endpoint_string, appconfiguration_keyvault_secret_url):

            appconfig_client = self.create_appconfig_client(appconfiguration_endpoint_string)

            watch_key = ConfigurationSetting(key="watch key", value="0")
            await appconfig_client.set_configuration_setting(watch_key)

            mock_callback = Mock()
            async with await self.create_client(
                endpoint=appconfiguration_endpoint_string,
                keyvault_secret_url=appconfiguration_keyvault_secret_url,
                refresh_on=[WatchKey("watch key")],
                refresh_interval=1,
                on_refresh_success=mock_callback,
                feature_flag_enabled=True,
                feature_flag_refresh_enabled=True,
            ) as client:
                assert client["refresh_message"] == "original value"
                assert client["my_json"]["key"] == "value"
                assert FEATURE_MANAGEMENT_KEY in client
                assert has_feature_flag(client, "Alpha")

                setting = await appconfig_client.get_configuration_setting(key="refresh_message")
                setting.value = "updated value"
                await appconfig_client.set_configuration_setting(setting)

                # Expire the refresh timers to simulate time passing
                client._refresh_timer._next_refresh_time = 0
                client._feature_flag_refresh_timer._next_refresh_time = 0

                await client.refresh()
                # No Change the Watch Key wasn't updated
                assert client["refresh_message"] == "original value"
                assert has_feature_flag(client, "Alpha", False)
                assert mock_callback.call_count == 0

                watch_key.value = "1"
                await appconfig_client.set_configuration_setting(watch_key)

                # Expire the refresh timers to simulate time passing
                client._refresh_timer._next_refresh_time = 0
                client._feature_flag_refresh_timer._next_refresh_time = 0

                await client.refresh()
                assert client["refresh_message"] == "updated value"
                assert has_feature_flag(client, "Alpha", False)
                assert mock_callback.call_count == 1

                # Reset modified settings
                setting.value = "original value"
                await appconfig_client.set_configuration_setting(setting)
                await appconfig_client.delete_configuration_setting(key="watch key")

        @AppConfigProviderPreparer()
        @recorded_by_proxy_async
        @pytest.mark.skipif(sys.version_info < (3, 8), reason="Python 3.7 does not support AsyncMock")
        @pytest.mark.asyncio
        async def test_refresh_disabled(self, appconfiguration_endpoint_string, appconfiguration_keyvault_secret_url):
            mock_callback = AsyncMock()
            async with await self.create_client(
                endpoint=appconfiguration_endpoint_string,
                keyvault_secret_url=appconfiguration_keyvault_secret_url,
                refresh_on=[WatchKey("refresh_message")],
                refresh_interval=1,
                on_refresh_success=mock_callback,
                feature_flag_enabled=True,
                feature_flag_refresh_enabled=True,
                refresh_enabled=False,
            ) as client:
                assert client["refresh_message"] == "original value"
                assert client["my_json"]["key"] == "value"
                assert FEATURE_MANAGEMENT_KEY in client
                assert has_feature_flag(client, "Alpha")

                appconfig_client = self.create_appconfig_client(appconfiguration_endpoint_string)

                setting = await appconfig_client.get_configuration_setting(key="refresh_message")
                setting.value = "updated value"
                await appconfig_client.set_configuration_setting(setting)

                # Expire the refresh timers to simulate time passing
                client._refresh_timer._next_refresh_time = 0

                await client.refresh()
                # Refresh is disabled, so the value should not change
                assert client["refresh_message"] == "original value"
                assert mock_callback.call_count == 0

                setting.value = "original value"
                await appconfig_client.set_configuration_setting(setting)

except ImportError:
    pass
