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
from devices.models.endpoint_device import EndpointModel
from devices.tests.devices_utils import mock_export_csv_task
from devices.tests.endpoint_utils import mock_redis_return_online

TMP_DIR_VECTOR = '/tmp/vector'


@patch('devices.services.vector.VECTOR_CONFIG_DIR', TMP_DIR_VECTOR)
@pytest.mark.integration
@pytest.mark.django_db
class TestDevicesAPI:

    @pytest.fixture(autouse=True)
    def setup_tests(self, django_user_model):
        self.admin_user = django_user_model.objects.get(username='admin')
        Device.objects.create(ip='1.1.1.1', port='1500', type='firewall')

    def test_getting_list_of_device(self, api_client):
        api_client.force_authenticate(self.admin_user)
        response = api_client.get(reverse('device-list'))
        assert response.data['count'] == 1
        assert response.data['results'][0]['ip'] == '1.1.1.1'
        assert response.data['results'][0]['port'] == 1500

    def test_getting_device_with_search(self, api_client):
        api_client.force_authenticate(self.admin_user)
        Device.objects.create(ip='2.2.2.2', port='2500', type='firewall', name='Good device')
        Device.objects.create(ip='3.3.3.3', port='9999', type='firewall', name='Bad device')
        response = api_client.get(reverse('device-list'))
        assert response.data['count'] == 3

        response = api_client.get(reverse('device-list'), {'search': 'good'})
        assert response.data['count'] == 1
        assert response.data['results'][0]['ip'] == '2.2.2.2'

    def test_getting_device(self, api_client):
        api_client.force_authenticate(self.admin_user)
        device = Device.objects.create(ip='2.2.2.2', port='2500', type='firewall')
        response = api_client.get(reverse('device-detail', kwargs={'pk': device.pk}))
        assert response.data['ip'] == '2.2.2.2'
        assert response.data['port'] == 2500

    def test_updating_device_with_valid_data(self, api_client):
        api_client.force_authenticate(self.admin_user)
        device = Device.objects.create(ip='2.2.2.2', port='2500', type='firewall')
        response = api_client.patch(
            reverse('device-detail', kwargs={'pk': device.pk}),
            data={'port': 60000, 'ip': '3.3.3.3'}
        )
        assert response.status_code == status.HTTP_200_OK
        assert response.data['ip'] == '3.3.3.3'
        assert response.data['port'] == 60000

        device = Device.objects.get(pk=device.pk)
        assert device.ip == '3.3.3.3'
        assert device.port == 60000

    def test_updating_device_with_invalid_data(self, api_client):
        api_client.force_authenticate(self.admin_user)
        device = Device.objects.create(ip='2.2.2.2', port='2500', type='firewall')
        response = api_client.patch(
            reverse('device-detail', kwargs={'pk': device.pk}),
            data={'port': 66666}  # invalid port
        )
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert 'port' in response.data
        assert response.json()['port'] == ['Ensure this value is less than or equal to 65535.']

        # device doesnt update
        new_device = Device.objects.get(pk=device.pk)
        assert new_device.ip == '2.2.2.2'
        assert new_device.port == 2500

    def test_deleting_device(self, api_client):
        api_client.force_authenticate(self.admin_user)
        device = Device.objects.create(ip='2.2.2.2', port='2500', type='firewall')

        response = api_client.delete(
            reverse('device-detail', kwargs={'pk': device.pk}),
        )
        assert response.status_code == status.HTTP_204_NO_CONTENT

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

    @patch('devices.services.endpoint.endpoint_get_status.RedisInterface', side_effect=mock_redis_return_online)
    def test_endpoint_status_in_device_list(self, _, api_client):
        """Test check endpoint with status online and config error status in device list api."""
        Device.objects.all().delete()
        data = {
            'name': 'test_endpoint',
            'type': DeviceType.ENDPOINT,
            'ip': '127.0.0.1',
            'port': '5555',
            'settings_changed': False,
            'request_config': False,
        }
        endpoint = EndpointModel.objects.create(**data)
        api_client.force_authenticate(self.admin_user)
        response = api_client.get(reverse('device-list'))
        assert response.data['count'] == 1
        endpoint_data = response.data['results'][0]
        assert endpoint_data['id'] == endpoint.pk
        assert endpoint_data['status'] == {'status': 'online'}

    @patch('core.mixins.export_task', mock_export_csv_task())
    def test_export_devices_to_csv(self, api_client):
        api_client.force_authenticate(self.admin_user)
        response = api_client.get(reverse('device-csv-export'))

        assert response.status_code == status.HTTP_302_FOUND
        assert response.url == reverse('store-download', kwargs={'pk': 98765})  # pk defined in mock function
