from django.test import TestCase
from django.test import override_settings

from circuits.api.serializers import ProviderSerializer
from circuits.forms import ProviderForm
from circuits.models import Provider
from ipam.models import ASN, RIR
from netbox.choices import CSVDelimiterChoices, ImportFormatChoices
from utilities.testing import APITestCase, ModelViewTestCase, create_tags, post_data


class ModelFormCustomValidationTest(TestCase):

    @override_settings(CUSTOM_VALIDATORS={
        'circuits.provider': [
            {'tags': {'required': True}}
        ]
    })
    def test_tags_validation(self):
        """
        Check that custom validation rules work for tag assignment.
        """
        data = {
            'name': 'Provider 1',
            'slug': 'provider-1',
        }
        form = ProviderForm(data)
        self.assertFalse(form.is_valid())

        tags = create_tags('Tag1', 'Tag2', 'Tag3')
        data['tags'] = [tag.pk for tag in tags]
        form = ProviderForm(data)
        self.assertTrue(form.is_valid())

    @override_settings(CUSTOM_VALIDATORS={
        'circuits.provider': [
            {'asns': {'required': True}}
        ]
    })
    def test_m2m_validation(self):
        """
        Check that custom validation rules work for many-to-many fields.
        """
        data = {
            'name': 'Provider 1',
            'slug': 'provider-1',
        }
        form = ProviderForm(data)
        self.assertFalse(form.is_valid())

        rir = RIR.objects.create(name='RIR 1', slug='rir-1')
        asns = ASN.objects.bulk_create((
            ASN(rir=rir, asn=65001),
            ASN(rir=rir, asn=65002),
            ASN(rir=rir, asn=65003),
        ))
        data['asns'] = [asn.pk for asn in asns]
        form = ProviderForm(data)
        self.assertTrue(form.is_valid())


class BulkEditCustomValidationTest(ModelViewTestCase):
    model = Provider

    @classmethod
    def setUpTestData(cls):
        rir = RIR.objects.create(name='RIR 1', slug='rir-1')
        asns = ASN.objects.bulk_create((
            ASN(rir=rir, asn=65001),
            ASN(rir=rir, asn=65002),
            ASN(rir=rir, asn=65003),
        ))

        providers = (
            Provider(name='Provider 1', slug='provider-1'),
            Provider(name='Provider 2', slug='provider-2'),
            Provider(name='Provider 3', slug='provider-3'),
        )
        Provider.objects.bulk_create(providers)
        for provider in providers:
            provider.asns.set(asns)

    @override_settings(CUSTOM_VALIDATORS={
        'circuits.provider': [
            {'asns': {'required': True}}
        ]
    })
    def test_bulk_edit_without_m2m(self):
        """
        Check that custom validation rules do not interfere with bulk editing.
        """
        data = {
            'pk': list(Provider.objects.values_list('pk', flat=True)),
            '_apply': '',
            'description': 'New description',
        }
        self.add_permissions(
            'circuits.view_provider',
            'circuits.change_provider',
        )

        # Bulk edit the description without changing ASN assignments
        request = {
            'path': self._get_url('bulk_edit'),
            'data': post_data(data),
        }
        response = self.client.post(**request)
        self.assertHttpStatus(response, 302)
        self.assertEqual(
            Provider.objects.filter(description=data['description']).count(),
            len(data['pk'])
        )

    @override_settings(CUSTOM_VALIDATORS={
        'circuits.provider': [
            {'asns': {'required': True}}
        ]
    })
    def test_bulk_edit_m2m(self):
        """
        Test that custom validation rules are enforced during bulk editing.
        """
        data = {
            'pk': list(Provider.objects.values_list('pk', flat=True)),
            '_apply': '',
            'description': 'New description',
        }
        self.add_permissions(
            'circuits.view_provider',
            'circuits.change_provider',
            'ipam.view_asn',
        )

        # Change the ASN assignments
        asn = ASN.objects.first()
        data['asns'] = [asn.pk]
        request = {
            'path': self._get_url('bulk_edit'),
            'data': post_data(data),
        }
        response = self.client.post(**request)
        self.assertHttpStatus(response, 302)
        for provider in Provider.objects.all():
            self.assertEqual(len(provider.asns.all()), 1)

        # Attempt to remove the ASN assignments
        data.pop('asns')
        data['_nullify'] = 'asns'
        request = {
            'path': self._get_url('bulk_edit'),
            'data': post_data(data),
        }
        response = self.client.post(**request)
        self.assertHttpStatus(response, 200)
        for provider in Provider.objects.all():
            self.assertTrue(provider.asns.exists())


class BulkImportCustomValidationTest(ModelViewTestCase):
    model = Provider

    @classmethod
    def setUpTestData(cls):
        create_tags('Tag1', 'Tag2', 'Tag3')

    @override_settings(CUSTOM_VALIDATORS={
        'circuits.provider': [
            {'tags': {'required': True}}
        ]
    })
    def test_bulk_import_invalid(self):
        """
        Test that custom validation rules are enforced during bulk import.
        """
        csv_data = (
            "name,slug",
            "Provider 1,provider-1",
            "Provider 2,provider-2",
            "Provider 3,provider-3",
        )
        data = {
            'data': '\n'.join(csv_data),
            'format': ImportFormatChoices.CSV,
            'csv_delimiter': CSVDelimiterChoices.COMMA,
        }
        self.add_permissions(
            'circuits.view_provider',
            'circuits.add_provider',
            'extras.view_tag',
        )

        # Attempt to import providers without tags
        request = {
            'path': self._get_url('bulk_import'),
            'data': post_data(data),
        }
        response = self.client.post(**request)
        self.assertHttpStatus(response, 200)
        self.assertFalse(Provider.objects.exists())

        # Import providers successfully with tag assignments
        csv_data = (
            "name,slug,tags",
            "Provider 1,provider-1,tag1",
            "Provider 2,provider-2,tag2",
            "Provider 3,provider-3,tag3",
        )
        data['data'] = '\n'.join(csv_data)
        request = {
            'path': self._get_url('bulk_import'),
            'data': post_data(data),
        }
        response = self.client.post(**request)
        self.assertHttpStatus(response, 302)
        self.assertTrue(Provider.objects.exists())


class APISerializerCustomValidationTest(APITestCase):

    @override_settings(CUSTOM_VALIDATORS={
        'circuits.provider': [
            {'tags': {'required': True}}
        ]
    })
    def test_tags_validation(self):
        """
        Check that custom validation rules work for tag assignment.
        """
        data = {
            'name': 'Provider 1',
            'slug': 'provider-1',
        }
        serializer = ProviderSerializer(data=data)
        self.assertFalse(serializer.is_valid())

        tags = create_tags('Tag1', 'Tag2', 'Tag3')
        data['tags'] = [tag.pk for tag in tags]
        serializer = ProviderSerializer(data=data)
        self.assertTrue(serializer.is_valid())

    @override_settings(CUSTOM_VALIDATORS={
        'circuits.provider': [
            {'asns': {'required': True}}
        ]
    })
    def test_m2m_validation(self):
        """
        Check that custom validation rules work for many-to-many fields.
        """
        data = {
            'name': 'Provider 1',
            'slug': 'provider-1',
        }
        serializer = ProviderSerializer(data=data)
        self.assertFalse(serializer.is_valid())

        rir = RIR.objects.create(name='RIR 1', slug='rir-1')
        asns = ASN.objects.bulk_create((
            ASN(rir=rir, asn=65001),
            ASN(rir=rir, asn=65002),
            ASN(rir=rir, asn=65003),
        ))
        data['asns'] = [asn.pk for asn in asns]
        serializer = ProviderSerializer(data=data)
        self.assertTrue(serializer.is_valid())
