from datetime import timedelta

from django import VERSION as DJANGO_VERSION
from django.contrib.auth.models import Group, Permission
from django.test import TestCase
from django.urls import reverse
from django.utils import timezone
from freezegun import freeze_time

from wagtail.admin.log_action_registry import LogActionRegistry
from wagtail.core.models import GroupPagePermission, Page, PageLogEntry, PageViewRestriction
from wagtail.tests.testapp.models import SimplePage
from wagtail.tests.utils import WagtailTestUtils


def test_hook(actions):
    return actions.register_action('test.custom_action', 'Custom action', 'Tested!')


class TestAuditLogHooks(TestCase, WagtailTestUtils):
    def setUp(self):
        self.root_page = Page.objects.get(id=2)

    def test_register_log_actions_hook(self):
        # testapp/wagtail_hooks.py defines a 'blockquote' rich text feature with a hallo.js
        # plugin, via the register_rich_text_features hook; test that we can retrieve it here
        log_actions = LogActionRegistry()
        actions = log_actions.get_actions()
        self.assertIn('wagtail.create', actions)

    def test_action_format_message(self):
        log_entry = PageLogEntry.objects.log_action(self.root_page, action='test.custom_action')

        log_actions = LogActionRegistry()
        self.assertEqual(log_actions.format_message(log_entry), "Unknown test.custom_action")
        self.assertNotIn('test.custom_action', log_actions.get_actions())

        with self.register_hook('register_log_actions', test_hook):
            log_actions = LogActionRegistry()
            self.assertIn('test.custom_action', log_actions.get_actions())
            self.assertEqual(log_actions.format_message(log_entry), "Tested!")
            self.assertEqual(log_actions.get_action_label('test.custom_action'), 'Custom action')


class TestAuditLogAdmin(TestCase, WagtailTestUtils):
    def setUp(self):
        self.root_page = Page.objects.get(id=2)

        self.hello_page = SimplePage(
            title="Hello world!",
            slug='hello-world',
            content="hello",
            live=False,
        )
        self.root_page.add_child(instance=self.hello_page)

        self.about_page = SimplePage(title="About", slug="about", content="hello")
        self.root_page.add_child(instance=self.about_page)

        self.administrator = self.create_superuser(
            username='administrator',
            email='administrator@email.com',
            password='password'
        )
        self.editor = self.create_user(username='the_editor', email='the_editor@example.com', password='password')
        sub_editors = Group.objects.create(name='Sub editors')
        sub_editors.permissions.add(Permission.objects.get(
            content_type__app_label='wagtailadmin',
            codename='access_admin'
        ))
        self.editor.groups.add(sub_editors)

        for permission_type in ['add', 'edit', 'publish']:
            GroupPagePermission.objects.create(
                group=sub_editors, page=self.hello_page, permission_type=permission_type
            )

    def _update_page(self, page):
        # save revision
        page.save_revision(user=self.editor, log_action=True)
        # schedule for publishing
        page.go_live_at = timezone.now() + timedelta(seconds=1)
        revision = page.save_revision(user=self.editor, log_action=True)
        revision.publish(user=self.editor)

        # publish
        with freeze_time(timezone.now() + timedelta(seconds=2)):
            revision.publish(user=self.editor)

            # lock/unlock
            page.save(user=self.editor, log_action='wagtail.lock')
            page.save(user=self.editor, log_action='wagtail.unlock')

            # change privacy
            restriction = PageViewRestriction(page=page, restriction_type=PageViewRestriction.LOGIN)
            restriction.save(user=self.editor)
            restriction.restriction_type = PageViewRestriction.PASSWORD
            restriction.save(user=self.administrator)
            restriction.delete()

    def test_page_history(self):
        self._update_page(self.hello_page)

        history_url = reverse('wagtailadmin_pages:history', kwargs={'page_id': self.hello_page.id})

        self.login(user=self.editor)

        response = self.client.get(history_url)
        self.assertEqual(response.status_code, 200)

        self.assertContains(response, "Created", 1)
        self.assertContains(response, "Draft saved", 2)
        self.assertContains(response, "Locked", 1)
        self.assertContains(response, "Unlocked", 1)
        self.assertContains(response, "Page scheduled for publishing", 1)
        self.assertContains(response, "Published", 1)

        if DJANGO_VERSION >= (3, 0):
            self.assertContains(
                response, "Added the &#x27;Private, accessible to logged-in users&#x27; view restriction"
            )
            self.assertContains(
                response,
                "Updated the view restriction to &#x27;Private, accessible with the following password&#x27;"
            )
            self.assertContains(
                response,
                "Removed the &#x27;Private, accessible with the following password&#x27; view restriction"
            )
        else:
            self.assertContains(
                response, "Added the &#39;Private, accessible to logged-in users&#39; view restriction"
            )
            self.assertContains(
                response,
                "Updated the view restriction to &#39;Private, accessible with the following password&#39;"
            )
            self.assertContains(
                response,
                "Removed the &#39;Private, accessible with the following password&#39; view restriction"
            )

        self.assertContains(response, 'system', 2)  # create without a user + remove restriction
        self.assertContains(response, 'the_editor', 9)  # 7 entries by editor + 1 in sidebar menu + 1 in filter
        self.assertContains(response, 'administrator', 2)  # the final restriction change + filter

    def test_page_history_filters(self):
        self.login(user=self.editor)
        self._update_page(self.hello_page)

        history_url = reverse('wagtailadmin_pages:history', kwargs={'page_id': self.hello_page.id})
        response = self.client.get(history_url + '?action=wagtail.edit')
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "Draft saved", count=2)
        self.assertNotContains(response, "Locked")
        self.assertNotContains(response, "Unlocked")
        self.assertNotContains(response, "Page scheduled for publishing")
        self.assertNotContains(response, "Published")

    def test_site_history(self):
        self._update_page(self.hello_page)
        self.about_page.save_revision(user=self.administrator, log_action=True)
        self.about_page.delete(user=self.administrator)

        site_history_url = reverse('wagtailadmin_reports:site_history')

        # the editor has access to the root page, so should see everything
        self.login(user=self.editor)

        response = self.client.get(site_history_url)
        self.assertEqual(response.status_code, 200)

        self.assertNotContains(response, 'About')
        self.assertContains(response, "Draft saved", 2)
        self.assertNotContains(response, 'Deleted')

        # once a page is deleted, its log entries are only visible to super admins or users with
        # permissions on the root page
        self.hello_page.delete(user=self.administrator)
        response = self.client.get(site_history_url)
        self.assertContains(response, "No log entries found")

        # add the editor user to the Editors group which has permissions on the root page
        self.editor.groups.add(Group.objects.get(name='Editors'))
        response = self.client.get(site_history_url)

        self.assertContains(response, 'About', 3)  # create, save draft, delete
        self.assertContains(response, 'Created', 2)
        self.assertContains(response, 'Deleted', 2)

        # check with super admin
        self.login(user=self.administrator)
        response = self.client.get(site_history_url)

        self.assertContains(response, 'About', 3)  # create, save draft, delete
        self.assertContains(response, 'Deleted', 2)

    def test_edit_form_has_history_link(self):
        self.hello_page.save_revision()
        self.login(user=self.editor)
        response = self.client.get(
            reverse('wagtailadmin_pages:edit', args=[self.hello_page.id])
        )
        self.assertEqual(response.status_code, 200)
        history_url = reverse('wagtailadmin_pages:history', args=[self.hello_page.id])
        self.assertContains(response, history_url)

    def test_create_and_publish_does_not_log_revision_save(self):
        self.login(user=self.administrator)
        post_data = {
            'title': "New page!",
            'content': "Some content",
            'slug': 'hello-world-redux',
            'action-publish': 'action-publish',
        }
        response = self.client.post(
            reverse('wagtailadmin_pages:add', args=('tests', 'simplepage', self.root_page.id)),
            post_data, follow=True
        )
        self.assertEqual(response.status_code, 200)

        page_id = Page.objects.get(path__startswith=self.root_page.path, slug='hello-world-redux').id

        self.assertListEqual(
            list(PageLogEntry.objects.filter(page=page_id).values_list('action', flat=True)),
            ['wagtail.publish', 'wagtail.create']
        )

    def test_revert_and_publish_logs_reversion_and_publish(self):
        revision = self.hello_page.save_revision(user=self.editor)
        self.hello_page.save_revision(user=self.editor)

        self.login(user=self.administrator)
        response = self.client.post(
            reverse('wagtailadmin_pages:edit', args=(self.hello_page.id, )),
            {
                'title': "Hello World!",
                'content': "another hello",
                'slug': 'hello-world',
                'revision': revision.id, 'action-publish': 'action-publish'}, follow=True
        )
        self.assertEqual(response.status_code, 200)

        entries = PageLogEntry.objects.filter(page=self.hello_page).values_list('action', flat=True)
        self.assertListEqual(
            list(entries),
            ['wagtail.publish', 'wagtail.rename', 'wagtail.revert', 'wagtail.create']
        )
