# Copyright 2020 Canonical Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
#     Unless required by applicable law or agreed to in writing, software
#     distributed under the License 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.


import asyncio
import logging
from unittest.mock import Mock


import asynctest
from n2vc.n2vc_juju_conn import N2VCJujuConnector
from osm_common import fslocal
from n2vc.exceptions import (
    N2VCBadArgumentsException,
    N2VCException,
)
from n2vc.tests.unit.utils import AsyncMock
from n2vc.vca.connection_data import ConnectionData


class N2VCJujuConnTestCase(asynctest.TestCase):
    @asynctest.mock.patch("n2vc.n2vc_juju_conn.MotorStore")
    @asynctest.mock.patch("n2vc.n2vc_juju_conn.get_connection")
    @asynctest.mock.patch("n2vc.vca.connection_data.base64_to_cacert")
    def setUp(
        self,
        mock_base64_to_cacert=None,
        mock_get_connection=None,
        mock_store=None,
    ):
        self.loop = asyncio.get_event_loop()
        self.db = Mock()
        mock_base64_to_cacert.return_value = """
    -----BEGIN CERTIFICATE-----
    SOMECERT
    -----END CERTIFICATE-----"""
        mock_store.return_value = AsyncMock()
        mock_vca_connection = Mock()
        mock_get_connection.return_value = mock_vca_connection
        mock_vca_connection.data.return_value = ConnectionData(
            **{
                "endpoints": ["1.2.3.4:17070"],
                "user": "user",
                "secret": "secret",
                "cacert": "cacert",
                "pubkey": "pubkey",
                "lxd-cloud": "cloud",
                "lxd-credentials": "credentials",
                "k8s-cloud": "k8s_cloud",
                "k8s-credentials": "k8s_credentials",
                "model-config": {},
                "api-proxy": "api_proxy",
            }
        )
        logging.disable(logging.CRITICAL)

        N2VCJujuConnector.get_public_key = Mock()
        self.n2vc = N2VCJujuConnector(
            db=self.db,
            fs=fslocal.FsLocal(),
            log=None,
            loop=self.loop,
            on_update_db=None,
        )
        N2VCJujuConnector.get_public_key.assert_not_called()
        self.n2vc.libjuju = Mock()


class GetMetricssTest(N2VCJujuConnTestCase):
    def setUp(self):
        super(GetMetricssTest, self).setUp()
        self.n2vc.libjuju.get_metrics = AsyncMock()

    def test_success(self):
        _ = self.loop.run_until_complete(self.n2vc.get_metrics("model", "application"))
        self.n2vc.libjuju.get_metrics.assert_called_once()

    def test_except(self):
        self.n2vc.libjuju.get_metrics.side_effect = Exception()
        with self.assertRaises(Exception):
            _ = self.loop.run_until_complete(
                self.n2vc.get_metrics("model", "application")
            )
        self.n2vc.libjuju.get_metrics.assert_called_once()


class UpdateVcaStatusTest(N2VCJujuConnTestCase):
    def setUp(self):
        super(UpdateVcaStatusTest, self).setUp()
        self.n2vc.libjuju.get_controller = AsyncMock()
        self.n2vc.libjuju.get_model = AsyncMock()
        self.n2vc.libjuju.get_executed_actions = AsyncMock()
        self.n2vc.libjuju.get_actions = AsyncMock()
        self.n2vc.libjuju.get_application_configs = AsyncMock()
        self.n2vc.libjuju._get_application = AsyncMock()

    def test_success(
        self,
    ):
        self.loop.run_until_complete(
            self.n2vc.update_vca_status(
                {"model": {"applications": {"app": {"actions": {}}}}}
            )
        )
        self.n2vc.libjuju.get_executed_actions.assert_called_once()
        self.n2vc.libjuju.get_actions.assert_called_once()
        self.n2vc.libjuju.get_application_configs.assert_called_once()

    def test_exception(self):
        self.n2vc.libjuju.get_model.return_value = None
        self.n2vc.libjuju.get_executed_actions.side_effect = Exception()
        with self.assertRaises(Exception):
            self.loop.run_until_complete(
                self.n2vc.update_vca_status(
                    {"model": {"applications": {"app": {"actions": {}}}}}
                )
            )
            self.n2vc.libjuju.get_executed_actions.assert_not_called()
            self.n2vc.libjuju.get_actions.assert_not_called_once()
            self.n2vc.libjuju.get_application_configs.assert_not_called_once()


@asynctest.mock.patch("osm_common.fslocal.FsLocal.file_exists")
@asynctest.mock.patch(
    "osm_common.fslocal.FsLocal.path", new_callable=asynctest.PropertyMock, create=True
)
class K8sProxyCharmsTest(N2VCJujuConnTestCase):
    def setUp(self):
        super(K8sProxyCharmsTest, self).setUp()
        self.n2vc.libjuju.model_exists = AsyncMock()
        self.n2vc.libjuju.add_model = AsyncMock()
        self.n2vc.libjuju.deploy_charm = AsyncMock()
        self.n2vc.libjuju.model_exists.return_value = False

    def test_success(
        self,
        mock_path,
        mock_file_exists,
    ):
        mock_file_exists.return_value = True
        mock_path.return_value = "/path"
        ee_id = self.loop.run_until_complete(
            self.n2vc.install_k8s_proxy_charm(
                "charm",
                "nsi-id.ns-id.vnf-id.vdu",
                "////path/",
                {},
            )
        )

        self.n2vc.libjuju.add_model.assert_called_once()
        self.n2vc.libjuju.deploy_charm.assert_called_once_with(
            model_name="ns-id-k8s",
            application_name="app-vnf-vnf-id-vdu-vdu",
            path="/path/path/",
            machine_id=None,
            db_dict={},
            progress_timeout=None,
            total_timeout=None,
            config=None,
        )
        self.assertEqual(ee_id, "ns-id-k8s.app-vnf-vnf-id-vdu-vdu.k8s")

    def test_no_artifact_path(
        self,
        mock_path,
        mock_file_exists,
    ):
        with self.assertRaises(N2VCBadArgumentsException):
            ee_id = self.loop.run_until_complete(
                self.n2vc.install_k8s_proxy_charm(
                    "charm",
                    "nsi-id.ns-id.vnf-id.vdu",
                    "",
                    {},
                )
            )
            self.assertIsNone(ee_id)

    def test_no_db(
        self,
        mock_path,
        mock_file_exists,
    ):
        with self.assertRaises(N2VCBadArgumentsException):
            ee_id = self.loop.run_until_complete(
                self.n2vc.install_k8s_proxy_charm(
                    "charm",
                    "nsi-id.ns-id.vnf-id.vdu",
                    "/path/",
                    None,
                )
            )
            self.assertIsNone(ee_id)

    def test_file_not_exists(
        self,
        mock_path,
        mock_file_exists,
    ):
        mock_file_exists.return_value = False
        with self.assertRaises(N2VCBadArgumentsException):
            ee_id = self.loop.run_until_complete(
                self.n2vc.install_k8s_proxy_charm(
                    "charm",
                    "nsi-id.ns-id.vnf-id.vdu",
                    "/path/",
                    {},
                )
            )
            self.assertIsNone(ee_id)

    def test_exception(
        self,
        mock_path,
        mock_file_exists,
    ):
        mock_file_exists.return_value = True
        mock_path.return_value = "/path"
        self.n2vc.libjuju.deploy_charm.side_effect = Exception()
        with self.assertRaises(N2VCException):
            ee_id = self.loop.run_until_complete(
                self.n2vc.install_k8s_proxy_charm(
                    "charm",
                    "nsi-id.ns-id.vnf-id.vdu",
                    "path/",
                    {},
                )
            )
            self.assertIsNone(ee_id)
