import unittest
from unittest.mock import MagicMock, Mock, patch

from ciscoconfparse import CiscoConfParse  # type: ignore

from vmngclient.api.templates import AttachedError, NameAlreadyExistError, NotFoundError, TemplateAPI
from vmngclient.dataclasses import Device, Template
from vmngclient.utils.creation_tools import create_dataclass
from vmngclient.utils.operation_status import OperationStatus
from vmngclient.utils.personality import Personality
from vmngclient.utils.reachability import Reachability


class TestTemplateAPI(unittest.TestCase):
    def setUp(self):
        self.data_template = [
            {
                "deviceType": "vedge-C8000V",
                "lastUpdatedBy": "user",
                "resourceGroup": "global",
                "templateClass": "cedge",
                "configType": "file",
                "templateId": "dummy_id_1",
                "factoryDefault": False,
                "templateName": "template_1",
                "devicesAttached": 0,
                "templateDescription": "dummy template 1",
                "draftMode": "Disabled",
                "lastUpdatedOn": 0,
                "templateAttached": 0,
            },
            {
                "deviceType": "vedge-cloud",
                "lastUpdatedBy": "admin",
                "resourceGroup": "global",
                "templateClass": "vedge",
                "configType": "file",
                "templateId": "dummy_id_2",
                "factoryDefault": False,
                "templateName": "template_2",
                "devicesAttached": 1,
                "templateDescription": "dummy template 2",
                "draftMode": "Disabled",
                "lastUpdatedOn": 0,
                "templateAttached": 0,
            },
        ]
        self.templates = [create_dataclass(Template, template) for template in self.data_template]
        self.device_info = Device(
            personality=Personality.EDGE,
            uuid="dummy_uuid",
            id="162.168.0.1",
            hostname="dummy_host",
            reachability=Reachability.REACHABLE,
            local_system_ip="192.168.0.1",
            memUsage=1.0,
            connected_vManages=["192.168.0.1"],
            model="vedge-cloud",
            status="normal",
        )

    @patch("vmngclient.session.vManageSession")
    def test_templates_success(self, mock_session):

        # Arrange
        mock_session.get_data.return_value = self.data_template

        # Act
        answer = TemplateAPI(mock_session).templates

        # Assert
        self.assertEqual(answer, self.templates)

    @patch("vmngclient.session.vManageSession")
    def test_templates_no_data(self, mock_session):

        # Arrange
        mock_session.get_data.return_value = []

        # Act
        answer = TemplateAPI(mock_session).templates

        # Assert
        self.assertEqual(answer, [])

    @patch.object(TemplateAPI, "templates")
    def test_get_success(self, mock_templates):

        # Arrage
        MockTemplates = Mock()
        mock_templates.return_value = MockTemplates
        session = Mock()
        test_object = TemplateAPI(session)
        test_object.templates = self.templates

        # Act
        answer = [test_object.get(template.name) for template in self.templates]

        # Assert
        self.assertEqual(answer, self.templates)

    @patch.object(TemplateAPI, "templates")
    def test_get_exception(self, mock_templates):

        # Arrage
        MockTemplates = Mock()
        mock_templates.return_value = MockTemplates
        session = Mock()
        test_object = TemplateAPI(session)
        test_object.templates = self.templates

        # Act
        def answer():
            return test_object.get("no_exist_name")

        # Assert
        self.assertRaises(NotFoundError, answer)

    @patch.object(TemplateAPI, "templates")
    def test_get_id_success(self, mock_templates):

        # Arrage
        MockTemplates = Mock()
        mock_templates.return_value = MockTemplates
        session = Mock()
        test_object = TemplateAPI(session)
        test_object.templates = self.templates

        # Act
        answer = [test_object.get_id(template.name) for template in self.templates]

        ids = [template.id for template in self.templates]
        # Assert
        self.assertEqual(answer, ids)

    @patch.object(TemplateAPI, "templates")
    def test_get_id_exception(self, mock_templates):

        # Arrage
        MockTemplates = Mock()
        mock_templates.return_value = MockTemplates
        session = Mock()
        test_object = TemplateAPI(session)
        test_object.templates = self.templates

        # Act
        def answer():
            return test_object.get_id("no_exist_name")

        # Assert
        self.assertRaises(NotFoundError, answer)

    @patch.object(TemplateAPI, "get_operation_status")
    def test_wait_for_complete_true(self, mock_operation):

        # Arrage
        MockOperatrion = Mock()
        mock_operation.return_value = MockOperatrion
        session = Mock()
        test_object = TemplateAPI(session)
        test_object.get_operation_status.return_value = [
            OperationStatus.SUCCESS,
            OperationStatus.SUCCESS,
        ]
        operation_id = "operation_id"

        # Act
        answer = test_object.wait_for_complete(operation_id)

        # Assert
        self.assertTrue(answer)

    @patch.object(TemplateAPI, "get_operation_status")
    def test_wait_for_complete_false(self, mock_operation):

        # Arrage
        MockOperatrion = Mock()
        mock_operation.return_value = MockOperatrion
        session = Mock()
        test_object = TemplateAPI(session)
        test_object.get_operation_status.return_value = [
            OperationStatus.IN_PROGRESS,
            OperationStatus.SUCCESS,
        ]
        operation_id = "operation_id"

        # Act
        answer = test_object.wait_for_complete(operation_id, timeout_seconds=2, sleep_seconds=1)

        # Assert
        self.assertFalse(answer)

    @patch("vmngclient.session.vManageSession")
    @patch.object(TemplateAPI, "templates")
    @patch.object(TemplateAPI, "wait_for_complete")
    def test_attach_exist_template(self, mock_session, mock_wait_for_complete, mock_templates):

        # Arrage
        mock_session.post_json.return_value = {"id": "operation_id"}
        test_object = TemplateAPI(mock_session)

        # mock templates
        MockTemplates = Mock()
        mock_templates.return_value = MockTemplates
        test_object.templates = self.templates

        # mock wait complete
        MockWaitComplete = Mock()
        mock_wait_for_complete.return_value = MockWaitComplete
        test_object.wait_for_complete.return_value = True

        # Act
        answer = test_object.attach("template_1", self.device_info)

        # Assert
        self.assertTrue(answer)

    @patch("vmngclient.session.vManageSession")
    @patch.object(TemplateAPI, "templates")
    @patch.object(TemplateAPI, "wait_for_complete")
    def test_attach_no_exist_template(self, mock_session, mock_wait_for_complete, mock_templates):

        # Arrage
        mock_session.post_json.return_value = {"id": "operation_id"}
        test_object = TemplateAPI(mock_session)

        # mock templates
        MockTemplates = Mock()
        mock_templates.return_value = MockTemplates
        test_object.templates = self.templates

        # mock wait complete
        MockWaitComplete = Mock()
        mock_wait_for_complete.return_value = MockWaitComplete
        test_object.wait_for_complete.return_value = True

        # Act
        answer = test_object.attach("no_exist_template", self.device_info)

        # Assert
        self.assertFalse(answer)

    @patch("vmngclient.session.vManageSession")
    @patch.object(TemplateAPI, "wait_for_complete")
    def test_device_to_cli_true(self, mock_session, mock_wait_for_complete):

        # Arrage
        mock_session.post_json.return_value = {"id": "operation_id"}
        test_object = TemplateAPI(mock_session)

        # mock wait complete
        MockWaitComplete = Mock()
        mock_wait_for_complete.return_value = MockWaitComplete
        test_object.wait_for_complete.return_value = True

        # Act
        answer = test_object.device_to_cli(self.device_info)

        # Assert
        self.assertTrue(answer)

    @patch("vmngclient.session.vManageSession")
    def test_get_operatrion_status_success(self, mock_session):

        # Arrage
        mock_session.get_data.return_value = [
            {"status": "In progress"},
            {"status": "Failure"},
            {"status": "Success"},
            {"status": "Scheduled"},
        ]
        test_object = TemplateAPI(mock_session)
        operation_id = "dummy_id"

        # Act
        answer = test_object.get_operation_status(operation_id)

        # Assert
        self.assertEqual(
            answer,
            [
                OperationStatus.IN_PROGRESS,
                OperationStatus.FAILURE,
                OperationStatus.SUCCESS,
                OperationStatus.SCHEDULED,
            ],
        )

    @patch("vmngclient.session.vManageSession")
    def test_get_operatrion_status_no_data(self, mock_session):

        # Arrage
        mock_session.get_data.return_value = []
        test_object = TemplateAPI(mock_session)
        operation_id = "dummy_id"

        # Act
        answer = test_object.get_operation_status(operation_id)

        # Assert
        self.assertEqual(answer, [])

    @patch("vmngclient.session.vManageSession")
    @patch.object(TemplateAPI, "templates")
    def test_delete_success(self, mock_session, mock_templates):

        # Arrage
        MockResponse = MagicMock()
        MockResponse.status_code = 200
        mock_session.delete.return_value = MockResponse
        test_object = TemplateAPI(mock_session)

        # mock templates
        MockTemplates = Mock()
        mock_templates.return_value = MockTemplates
        test_object.templates = self.templates

        # Act
        answer = test_object.delete("template_1")
        # Assert
        self.assertTrue(answer)

    @patch("vmngclient.session.vManageSession")
    @patch.object(TemplateAPI, "templates")
    def test_delete_wrong_status(self, mock_session, mock_templates):

        # Arrage
        MockResponse = MagicMock()
        MockResponse.ok = False
        mock_session.delete.return_value = MockResponse
        test_object = TemplateAPI(mock_session)

        # mock templates
        MockTemplates = Mock()
        mock_templates.return_value = MockTemplates
        test_object.templates = self.templates

        # Act
        answer = test_object.delete("template_1")

        # Assert
        self.assertFalse(answer)

    @patch("vmngclient.session.vManageSession")
    @patch.object(TemplateAPI, "templates")
    def test_delete_exception(self, mock_session, mock_templates):

        # Arrage
        MockResponse = MagicMock()
        MockResponse.status = 404
        mock_session.delete.return_value = MockResponse
        test_object = TemplateAPI(mock_session)

        # mock templates
        MockTemplates = Mock()
        mock_templates.return_value = MockTemplates
        test_object.templates = self.templates

        # Act
        def answer():
            return test_object.delete("template_2")

        # Assert
        self.assertRaises(AttachedError, answer)

    @patch("vmngclient.session.vManageSession")
    @patch.object(TemplateAPI, "templates")
    def test_create_exception(self, mock_session, mock_templates):

        # Arrage
        test_object = TemplateAPI(mock_session)

        # mock templates
        MockTemplates = Mock()
        mock_templates.return_value = MockTemplates
        test_object.templates = self.templates
        config = CiscoConfParse([])

        # Act
        def answer():
            return test_object.create(self.device_info, "template_1", "new_description", config)

        # Assert
        self.assertRaises(NameAlreadyExistError, answer)
