"""Two-session revisions, terminal Admin gates and request-boundary audit."""
import uuid

from django.test import TestCase, Client
from django.urls import reverse

from .test_phase3a_audit import setup_audit, reach, advance, snapshot, ServiceCase

PAGES = ("engineer", "diagnosis", "repair", "quality_control", "handover")


class Phase3AAdminAuditTests(TestCase):
    @classmethod
    def setUpTestData(cls):
        setup_audit(cls)
        for actor in (cls.user, cls.engineer, cls.inspector):
            actor.is_staff = actor.is_superuser = True
            actor.save()

    def url(self, page, pk=None):
        return reverse("admin:service_servicecase_" + page, args=[pk or self.case.pk])

    def browser(self, actor):
        client = Client()
        client.force_login(actor)
        return client

    def stale(self, state, page, actor_name, operation, transition, **data):
        reach(self, state)
        first = self.browser(getattr(self, actor_name))
        second = self.browser(getattr(self, actor_name))
        first_form = first.get(self.url(page)).context["form"]
        self.assertEqual(second.get(self.url(page)).status_code, 200)
        token = first_form.initial["revision"]
        advance(self, transition)
        before = snapshot()
        response = first.post(self.url(page), dict(operation=operation, revision=token, **data))
        self.assertIn(response.status_code, (200, 403))
        if response.status_code == 200:
            self.assertTrue(response.context["form"].errors)
        self.assertEqual(snapshot(), before)

    def test_two_sessions_assignment(self):
        self.stale("RECEIVED", "engineer", "user", "assign", "assign", engineer=self.engineer.pk)

    def test_two_sessions_diagnosis(self):
        self.stale("DIAGNOSING", "diagnosis", "engineer", "note", "diagnosis_complete", technical_note="Stale synthetic note")

    def test_two_sessions_repair(self):
        self.stale("REPAIRING", "repair", "engineer", "note", "repair_success", execution_note="Stale synthetic note")

    def test_two_sessions_qc(self):
        self.stale("QC_IN_PROGRESS", "quality_control", "inspector", "summary", "qc_pass", summary="Stale synthetic note")

    def test_two_sessions_release(self):
        self.stale("QC_PASSED", "handover", "inspector", "ready", "release", device_confirmed="on")

    def test_two_sessions_handover(self):
        self.stale("READY_FOR_DELIVERY", "handover", "inspector", "handover", "deliver", device_confirmed="on",
            physical_handover_confirmed="on", verification_confirmed="on", recipient_type="CUSTOMER", verification_method="CUSTOMER_RECORD")

    def test_two_sessions_closure(self):
        self.stale("DELIVERED", "handover", "inspector", "close", "close")

    def terminal(self, state):
        reach(self, state)
        client = self.browser(self.inspector)
        before = snapshot()
        for page in PAGES:
            with self.subTest(page=page):
                self.assertEqual(client.post(self.url(page), dict(operation="begin", status="ASSIGNED")).status_code, 403)
        self.assertEqual(client.post(reverse("admin:service_servicecase_change", args=[self.case.pk]), {"status": "RECEIVED"}).status_code, 403)
        self.assertEqual(snapshot(), before)
        self.assertEqual(client.get(self.url("handover")).status_code, 200)
        self.assertEqual(client.get(self.url("diagnosis")).status_code, 200)

    def test_closed_admin_is_terminal_with_readable_history(self):
        self.terminal("CLOSED")

    def test_cancelled_admin_is_terminal_with_readable_history(self):
        self.terminal("CANCELLED")

    def test_get_parameters_never_mutate_all_workflow_pages(self):
        client = self.browser(self.inspector)
        before = snapshot()
        for page in PAGES:
            client.get(self.url(page), dict(operation="assign", engineer=self.engineer.pk, status="CLOSED"))
        self.assertEqual(snapshot(), before)

    def test_all_workflows_require_authentication_and_csrf(self):
        anonymous = Client()
        protected = Client(enforce_csrf_checks=True)
        protected.force_login(self.inspector)
        for page in PAGES:
            self.assertEqual(anonymous.get(self.url(page)).status_code, 302)
            self.assertEqual(protected.post(self.url(page), {}).status_code, 403)

    def test_forged_case_uuid_cannot_select_a_case(self):
        client = self.browser(self.inspector)
        before = snapshot()
        for page in PAGES:
            self.assertEqual(client.post(self.url(page, uuid.uuid4()), {}).status_code, 404)
        self.assertEqual(snapshot(), before)
