from unittest.mock import patch

import pytest
from rest_framework import status
from rest_framework.reverse import reverse

from devices.enums import DeviceType
from devices.models.device import Device, DeviceGroup
from devices.models.endpoint_device import EndpointModel
from devices.tests.endpoint_utils import mock_redis_return_online


@pytest.mark.unit
@pytest.mark.django_db
class TestDeviceGroupAPI:

    @pytest.fixture(autouse=True)
    def setup_tests(self, django_user_model):
        self.admin_user = django_user_model.objects.get(username='admin')
        self.device1 = Device.objects.create(ip='1.1.1.1', port='1500', type='firewall')
        self.device2 = Device.objects.create(ip='2.2.2.2', port='5555', type='firewall')
        DeviceGroup.objects.create(name='group1')
        DeviceGroup.objects.create(name='group2', description='description2')

    def test_getting_list_of_device_group(self, api_client):
        api_client.force_authenticate(self.admin_user)
        response = api_client.get(reverse('device-groups-list'))
        assert response.data['count'] == 2
        assert response.data['results'][0]['name'] == 'group1'
        assert response.data['results'][0]['description'] is None

        assert response.data['results'][1]['name'] == 'group2'
        assert response.data['results'][1]['description'] == 'description2'

    def test_getting_device_group(self, api_client):
        api_client.force_authenticate(self.admin_user)
        group = DeviceGroup.objects.create(name='group')
        response = api_client.get(reverse('device-groups-detail', kwargs={'pk': group.pk}))
        assert response.status_code == status.HTTP_200_OK
        assert response.data['name'] == 'group'

    def test_updating_device_group_with_valid_data(self, api_client):
        api_client.force_authenticate(self.admin_user)
        group = DeviceGroup.objects.create(name='group')
        response = api_client.patch(
            reverse('device-groups-detail', kwargs={'pk': group.pk}),
            data={'name': 'new_name_group', 'description': 'description'}
        )
        assert response.status_code == status.HTTP_200_OK
        assert response.data['name'] == 'new_name_group'
        assert response.data['description'] == 'description'

        group = DeviceGroup.objects.get(pk=group.pk)
        assert group.name == 'new_name_group'
        assert group.description == 'description'

    def test_updating_device_with_invalid_data(self, api_client):
        api_client.force_authenticate(self.admin_user)
        group = DeviceGroup.objects.create(name='group')
        group_id = group.pk
        response = api_client.patch(
            reverse('device-groups-detail', kwargs={'pk': group_id}),
            data={'name': '', 'description': 'test'}  # invalid name
        )
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert 'name' in response.data

        # device doesnt update
        group = DeviceGroup.objects.get(pk=group_id)
        assert group.name == 'group'
        assert group.description is None

    def test_deleting_device_group(self, api_client):
        api_client.force_authenticate(self.admin_user)
        group = DeviceGroup.objects.create(name='group')
        group_pk = group.pk
        response = api_client.delete(
            reverse('device-groups-detail', kwargs={'pk': group_pk}),
        )
        assert response.status_code == status.HTTP_204_NO_CONTENT

        response = api_client.get(
            reverse('device-groups-detail', kwargs={'pk': group_pk}),
        )
        assert response.status_code == status.HTTP_404_NOT_FOUND

    def test_manage_device_in_group(self, api_client):
        api_client.force_authenticate(self.admin_user)

        group = DeviceGroup.objects.create(name='group')
        assert group.devices.count() == 0

        self.device1.group = group
        self.device1.save()

        response = api_client.get(reverse('device-groups-detail', kwargs={'pk': group.pk}))
        assert response.status_code == status.HTTP_200_OK
        assert response.data['devices'] == [{'id': self.device1.pk, 'name': self.device2.name}]

        # add device to group
        response = api_client.patch(
            reverse('device-groups-detail', kwargs={'pk': group.pk}),
            data={'devices': [self.device1.pk, self.device2.pk]}
        )
        assert response.status_code == status.HTTP_200_OK
        assert response.data['devices'] == [
            {'id': self.device1.pk, 'name': self.device1.name},
            {'id': self.device2.pk, 'name': self.device2.name},
        ]

        # remove one device
        response = api_client.patch(
            reverse('device-groups-detail', kwargs={'pk': group.pk}),
            data={'devices': [self.device2.pk]}
        )
        assert response.status_code == status.HTTP_200_OK
        assert response.data['devices'] == [{'id': self.device2.pk, 'name': self.device2.name}]

        # remove all device
        response = api_client.patch(
            reverse('device-groups-detail', kwargs={'pk': group.pk}),
            data={'devices': []},
            format='json'
        )
        assert response.status_code == status.HTTP_200_OK
        assert response.data['devices'] == []

    def test_create_device_group_without_devices(self, api_client):
        api_client.force_authenticate(self.admin_user)

        response = api_client.post(reverse('device-groups-list'), data={'name': 'group', 'description': 'group'})
        assert response.status_code == status.HTTP_201_CREATED
        assert response.data['devices'] == []
