import json
import logging

from django.test import tag
from django.urls import reverse
from rest_framework import status

from dcim.models import Site
from tenancy.choices import *
from tenancy.models import *
from utilities.testing import APITestCase, APIViewTestCases, disable_logging


class AppTestCase(APITestCase):

    def test_root(self):

        url = reverse('tenancy-api:api-root')
        response = self.client.get('{}?format=api'.format(url), **self.header)

        self.assertEqual(response.status_code, 200)


class TenantGroupTestCase(APIViewTestCases.APIViewTestCase):
    model = TenantGroup
    brief_fields = ['_depth', 'description', 'display', 'id', 'name', 'slug', 'tenant_count', 'url']
    bulk_update_data = {
        'description': 'New description',
        'comments': 'New Comment',
    }

    @classmethod
    def setUpTestData(cls):

        parent_tenant_groups = (
            TenantGroup.objects.create(name='Parent Tenant Group 1', slug='parent-tenant-group-1'),
            TenantGroup.objects.create(
                name='Parent Tenant Group 2', slug='parent-tenant-group-2', comments='Parent Group 2 comment',
            ),
        )

        TenantGroup.objects.create(name='Tenant Group 1', slug='tenant-group-1', parent=parent_tenant_groups[0])
        TenantGroup.objects.create(name='Tenant Group 2', slug='tenant-group-2', parent=parent_tenant_groups[0])
        TenantGroup.objects.create(
            name='Tenant Group 3', slug='tenant-group-3', parent=parent_tenant_groups[0],
            comments='Tenant Group 3 comment'
        )

        cls.create_data = [
            {
                'name': 'Tenant Group 4',
                'slug': 'tenant-group-4',
                'parent': parent_tenant_groups[1].pk,
            },
            {
                'name': 'Tenant Group 5',
                'slug': 'tenant-group-5',
                'parent': parent_tenant_groups[1].pk,
            },
            {
                'name': 'Tenant Group 6',
                'slug': 'tenant-group-6',
                'parent': parent_tenant_groups[1].pk,
                'comments': 'Tenant Group 6 comment',
            },
        ]

    @tag('regression')  # Ref: #22821
    def test_delete_tenant_group_with_conflicting_tenants(self):
        """
        Attempt and fail to delete a tenant group whose tenants cannot be ungrouped.
        """
        group = TenantGroup.objects.create(name='Tenant Group 7', slug='tenant-group-7')
        Tenant.objects.create(name='Tenant 1', slug='tenant-1a', group=group)
        Tenant.objects.create(name='Tenant 1', slug='tenant-1b')

        self.add_permissions('tenancy.delete_tenantgroup')
        url = reverse('tenancy-api:tenantgroup-detail', kwargs={'pk': group.pk})
        with disable_logging(level=logging.WARNING):
            response = self.client.delete(url, **self.header)

        self.assertHttpStatus(response, status.HTTP_409_CONFLICT)

        content = json.loads(response.content.decode('utf-8'))
        self.assertIn('detail', content)
        self.assertTrue(content['detail'].startswith('Unable to delete object.'))
        self.assertTrue(TenantGroup.objects.filter(pk=group.pk).exists())

    @tag('regression')  # Ref: #22821
    def test_bulk_delete_tenant_groups_with_conflicting_tenants(self):
        """
        Attempt and fail to bulk delete two tenant groups whose tenants conflict only once both are
        ungrouped, leaving every group and assignment intact.
        """
        group1 = TenantGroup.objects.create(name='Tenant Group 8', slug='tenant-group-8')
        group2 = TenantGroup.objects.create(name='Tenant Group 9', slug='tenant-group-9')
        tenant1 = Tenant.objects.create(name='Tenant 2', slug='tenant-2a', group=group1)
        tenant2 = Tenant.objects.create(name='Tenant 2', slug='tenant-2b', group=group2)

        self.add_permissions('tenancy.delete_tenantgroup')
        data = [{'id': group1.pk}, {'id': group2.pk}]
        with disable_logging(level=logging.WARNING):
            response = self.client.delete(self._get_list_url(), data, format='json', **self.header)

        self.assertHttpStatus(response, status.HTTP_409_CONFLICT)

        # The rolled back batch must not leave the first group deleted
        self.assertEqual(TenantGroup.objects.filter(pk__in=(group1.pk, group2.pk)).count(), 2)
        tenant1.refresh_from_db()
        tenant2.refresh_from_db()
        self.assertEqual(tenant1.group, group1)
        self.assertEqual(tenant2.group, group2)


class TenantTestCase(APIViewTestCases.APIViewTestCase):
    model = Tenant
    brief_fields = ['description', 'display', 'id', 'name', 'slug', 'url']
    bulk_update_data = {
        'group': None,
        'description': 'New description',
    }

    @classmethod
    def setUpTestData(cls):

        tenant_groups = (
            TenantGroup.objects.create(name='Tenant Group 1', slug='tenant-group-1'),
            TenantGroup.objects.create(name='Tenant Group 2', slug='tenant-group-2'),
        )

        tenants = (
            Tenant(name='Tenant 1', slug='tenant-1', group=tenant_groups[0]),
            Tenant(name='Tenant 2', slug='tenant-2', group=tenant_groups[0]),
            Tenant(name='Tenant 3', slug='tenant-3', group=tenant_groups[0]),
        )
        Tenant.objects.bulk_create(tenants)

        cls.create_data = [
            {
                'name': 'Tenant 4',
                'slug': 'tenant-4',
                'group': tenant_groups[1].pk,
            },
            {
                'name': 'Tenant 5',
                'slug': 'tenant-5',
                'group': tenant_groups[1].pk,
            },
            {
                'name': 'Tenant 6',
                'slug': 'tenant-6',
                'group': tenant_groups[1].pk,
            },
        ]


class ContactGroupTestCase(APIViewTestCases.APIViewTestCase):
    model = ContactGroup
    brief_fields = ['_depth', 'contact_count', 'description', 'display', 'id', 'name', 'slug', 'url']
    bulk_update_data = {
        'description': 'New description',
    }

    @classmethod
    def setUpTestData(cls):

        parent_contact_groups = (
            ContactGroup.objects.create(
                name='Parent Contact Group 1', slug='parent-contact-group-1', comments='Parent 1 comment'
            ),
            ContactGroup.objects.create(name='Parent Contact Group 2', slug='parent-contact-group-2'),
        )

        ContactGroup.objects.create(name='Contact Group 1', slug='contact-group-1', parent=parent_contact_groups[0])
        ContactGroup.objects.create(name='Contact Group 2', slug='contact-group-2', parent=parent_contact_groups[0])
        ContactGroup.objects.create(
            name='Contact Group 3', slug='contact-group-3', parent=parent_contact_groups[0],
            comments='Child Group 3 comment',
        )

        cls.create_data = [
            {
                'name': 'Contact Group 4',
                'slug': 'contact-group-4',
                'parent': parent_contact_groups[1].pk,
            },
            {
                'name': 'Contact Group 5',
                'slug': 'contact-group-5',
                'parent': parent_contact_groups[1].pk,
                'comments': '',
            },
            {
                'name': 'Contact Group 6',
                'slug': 'contact-group-6',
                'parent': parent_contact_groups[1].pk,
                'comments': 'Child Group 6 comment',
            },
        ]


class ContactRoleTestCase(APIViewTestCases.APIViewTestCase):
    model = ContactRole
    brief_fields = ['description', 'display', 'id', 'name', 'slug', 'url']
    create_data = [
        {
            'name': 'Contact Role 4',
            'slug': 'contact-role-4',
        },
        {
            'name': 'Contact Role 5',
            'slug': 'contact-role-5',
        },
        {
            'name': 'Contact Role 6',
            'slug': 'contact-role-6',
        },
    ]
    bulk_update_data = {
        'description': 'New description',
    }

    @classmethod
    def setUpTestData(cls):

        contact_roles = (
            ContactRole(name='Contact Role 1', slug='contact-role-1'),
            ContactRole(name='Contact Role 2', slug='contact-role-2'),
            ContactRole(name='Contact Role 3', slug='contact-role-3'),
        )
        ContactRole.objects.bulk_create(contact_roles)


class ContactTestCase(APIViewTestCases.APIViewTestCase):
    model = Contact
    brief_fields = ['description', 'display', 'id', 'name', 'url']
    bulk_update_data = {
        'groups': [],
        'comments': 'New comments',
    }

    @classmethod
    def setUpTestData(cls):

        contact_groups = (
            ContactGroup.objects.create(name='Contact Group 1', slug='contact-group-1'),
            ContactGroup.objects.create(name='Contact Group 2', slug='contact-group-2'),
        )

        contacts = (
            Contact(name='Contact 1'),
            Contact(name='Contact 2'),
            Contact(name='Contact 3'),
        )
        Contact.objects.bulk_create(contacts)
        contacts[0].groups.add(contact_groups[0])
        contacts[1].groups.add(contact_groups[0])
        contacts[2].groups.add(contact_groups[0])

        cls.create_data = [
            {
                'name': 'Contact 4',
                'groups': [contact_groups[1].pk],
            },
            {
                'name': 'Contact 5',
            },
            {
                'name': 'Contact 6',
            },
        ]


class ContactAssignmentTestCase(APIViewTestCases.APIViewTestCase):
    model = ContactAssignment
    brief_fields = ['contact', 'display', 'id', 'priority', 'role', 'url']
    bulk_update_data = {
        'priority': ContactPriorityChoices.PRIORITY_INACTIVE,
    }
    user_permissions = ('tenancy.view_contact', )

    @classmethod
    def setUpTestData(cls):

        sites = (
            Site(name='Site 1', slug='site-1'),
            Site(name='Site 2', slug='site-2'),
        )
        Site.objects.bulk_create(sites)

        contacts = (
            Contact(name='Contact 1'),
            Contact(name='Contact 2'),
            Contact(name='Contact 3'),
            Contact(name='Contact 4'),
            Contact(name='Contact 5'),
            Contact(name='Contact 6'),
        )
        Contact.objects.bulk_create(contacts)

        contact_roles = (
            ContactRole(name='Contact Role 1', slug='contact-role-1'),
            ContactRole(name='Contact Role 2', slug='contact-role-2'),
            ContactRole(name='Contact Role 3', slug='contact-role-3'),
        )
        ContactRole.objects.bulk_create(contact_roles)

        contact_assignments = (
            ContactAssignment(
                object=sites[0],
                contact=contacts[0],
                role=contact_roles[0],
                priority=ContactPriorityChoices.PRIORITY_PRIMARY,
            ),
            ContactAssignment(
                object=sites[0],
                contact=contacts[1],
                role=contact_roles[1],
                priority=ContactPriorityChoices.PRIORITY_SECONDARY,
            ),
            ContactAssignment(
                object=sites[0],
                contact=contacts[2],
                role=contact_roles[2],
                priority=ContactPriorityChoices.PRIORITY_TERTIARY,
            ),
        )
        ContactAssignment.objects.bulk_create(contact_assignments)

        cls.create_data = [
            {
                'object_type': 'dcim.site',
                'object_id': sites[1].pk,
                'contact': contacts[3].pk,
                'role': contact_roles[0].pk,
                'priority': ContactPriorityChoices.PRIORITY_PRIMARY,
            },
            {
                'object_type': 'dcim.site',
                'object_id': sites[1].pk,
                'contact': contacts[4].pk,
                'role': contact_roles[1].pk,
                'priority': ContactPriorityChoices.PRIORITY_SECONDARY,
            },
            {
                'object_type': 'dcim.site',
                'object_id': sites[1].pk,
                'contact': contacts[5].pk,
                'role': contact_roles[2].pk,
                'priority': ContactPriorityChoices.PRIORITY_TERTIARY,
            },
        ]
