"""Synthetic diagnostic work, unknown causes, history, queries and Admin."""
import uuid
from datetime import timedelta
from unittest.mock import patch

from django.contrib.auth import get_user_model
from django.contrib.auth.models import Permission
from django.core.exceptions import ValidationError
from django.db import IntegrityError, transaction
from django.db.models.deletion import Collector, ProtectedError
from django.test import Client, TestCase
from django.urls import reverse
from django.utils import timezone

from apps.access import services as access
from apps.catalog import services as catalog
from apps.devices import services as devices
from apps.service_catalog import services as taxonomy
from apps.service_catalog.models import FaultDiagnosis, RootCause
from . import diagnostic_services as services, diagnostic_queries as queries, engineer_queries
from .models import ServiceCase, ServiceEngineerAssignment, ServiceDiagnosticAssessment, ServiceDiagnosticFinding
from .services import cancel_service_case
from .test_engineer_assignment import setup_engineers, assign, reassign, unassign
from .tests import intake


def setup_diagnosis(test):
    setup_engineers(test)
    test.assignment = assign(test)
    test.fault = FaultDiagnosis.objects.create(code="SYNTHETIC-FAULT", name="Synthetic fault", applies_to_all_product_categories=True)
    test.fault2 = FaultDiagnosis.objects.create(code="SYNTHETIC-FAULT-2", name="Synthetic second fault", applies_to_all_product_categories=True)
    test.root = RootCause.objects.create(code="SYNTHETIC-CAUSE", name="Synthetic cause", applies_to_all_product_categories=True)


def begin(test, **kwargs):
    return services.begin_service_case_diagnosis(**dict(dict(service_case=test.case, actor=test.engineer), **kwargs))


def add(test, assessment, **kwargs):
    return services.add_diagnostic_finding(**dict(dict(assessment=assessment, actor=test.engineer, fault_diagnosis=test.fault), **kwargs))


def complete(test, assessment, **kwargs):
    return services.complete_service_case_diagnosis(**dict(dict(assessment=assessment, actor=test.engineer), **kwargs))


def abandon(test, assessment, **kwargs):
    return services.abandon_service_case_diagnosis(**dict(dict(assessment=assessment, actor=test.user, reason="Synthetic recovery"), **kwargs))


def assert_diagnostic_invariants(test):
    for case in ServiceCase.objects.all():
        assignments = list(ServiceEngineerAssignment.objects.filter(service_case=case, ended_at=None))
        assessments = list(ServiceDiagnosticAssessment.objects.filter(service_case=case, completed_at=None, abandoned_at=None))
        test.assertEqual(len(assignments), int(case.status in ("ASSIGNED", "DIAGNOSING", "DIAGNOSED")))
        test.assertEqual(len(assessments), int(case.status == "DIAGNOSING"))
        if assessments:
            test.assertEqual(assessments[0].engineer_assignment_id, assignments[0].pk)
        if case.status == "DIAGNOSED":
            test.assertTrue(ServiceDiagnosticAssessment.objects.filter(service_case=case, completed_at__isnull=False).exists())


class DiagnosisTests(TestCase):
    def setUp(self):
        setup_diagnosis(self)

    def tearDown(self):
        assert_diagnostic_invariants(self)

    def test_begin_uuid_actor_and_assignment_link(self):
        row = begin(self, expected_engineer_assignment_id=self.assignment.pk)
        self.assertIsInstance(row.pk, uuid.UUID)
        self.assertEqual(row.engineer_assignment, self.assignment)
        self.assertEqual(row.started_by, self.engineer)
        self.assertTrue(row.is_open)
        self.assertTrue(timezone.is_aware(row.started_at))

    def test_only_assigned_case_can_begin(self):
        with self.assertRaises(ValidationError):
            begin(self, service_case=intake(self))

    def test_other_engineer_and_superuser_cannot_begin(self):
        self.user.is_superuser = True
        self.user.save()
        for actor in (self.user, self.engineer2):
            with self.subTest(actor=actor), self.assertRaises(ValidationError):
                begin(self, actor=actor)

    def test_duplicate_begin_rejects(self):
        begin(self)
        with self.assertRaises(ValidationError):
            begin(self)
        self.assertEqual(ServiceDiagnosticAssessment.objects.count(), 1)

    def test_stale_assignment_begin_rejects(self):
        reassign(self)
        with self.assertRaises(ValidationError):
            begin(self, actor=self.engineer2, expected_engineer_assignment_id=self.assignment.pk)

    def test_revoked_eligibility_blocks_begin(self):
        access.deactivate_role_assignment(assignment=self.role_assignment)
        with self.assertRaises(ValidationError):
            begin(self)

    def test_inactive_user_blocks_technical_work(self):
        assessment = begin(self)
        get_user_model().objects.filter(pk=self.engineer.pk).update(is_active=False)
        with self.assertRaises(ValidationError):
            add(self, assessment)
        abandon(self, assessment)  # Explicit active administrator recovery still works.

    def test_inactive_device_blocks_new_diagnosis(self):
        devices.deactivate_device(device=self.device)
        with self.assertRaises(ValidationError):
            begin(self)

    def test_inactive_catalog_blocks_completion_preserves_history(self):
        assessment = begin(self)
        add(self, assessment)
        catalog.deactivate_category(category=self.category)
        with self.assertRaises(ValidationError):
            complete(self, assessment)
        self.assertEqual(queries.diagnostic_findings_for_assessment(assessment).count(), 1)
        abandon(self, assessment)

    def test_inactive_product_model_blocks_begin(self):
        catalog.deactivate_product_model(product_model=self.model)
        with self.assertRaises(ValidationError):
            begin(self)

    def test_draft_null_root_is_valid(self):
        finding = add(self, begin(self))
        self.assertIsInstance(finding.pk, uuid.UUID)
        self.assertIsNone(finding.root_cause)

    def test_null_root_completion_valid_without_root_lookup(self):
        assessment = begin(self)
        with patch("apps.service_catalog.queries.root_cause_applies_to_category", side_effect=AssertionError("NULL must not validate RootCause")):
            finding = add(self, assessment)
            row = complete(self, assessment)
        self.assertEqual(row.completed_by, self.engineer)
        finding.refresh_from_db()
        self.assertIsNone(finding.root_cause_id)
        self.assertFalse(row.is_open)
        self.assertIsNotNone(engineer_queries.current_engineer_assignment(self.case))

    def test_different_faults_with_null_causes_allowed(self):
        assessment = begin(self)
        add(self, assessment)
        add(self, assessment, fault_diagnosis=self.fault2)
        complete(self, assessment)
        self.assertEqual(assessment.findings.count(), 2)

    def test_duplicate_null_and_supplied_combinations_reject(self):
        assessment = begin(self)
        for cause in (None, self.root):
            add(self, assessment, root_cause=cause)
            with self.subTest(cause=cause), self.assertRaises(ValidationError):
                add(self, assessment, root_cause=cause)

    def test_required_fault(self):
        with self.assertRaises(ValidationError):
            add(self, begin(self), fault_diagnosis=None)

    def test_supplied_inactive_root_rejects(self):
        assessment = begin(self)
        taxonomy.deactivate_root_cause(root_cause=self.root)
        with self.assertRaises(ValidationError):
            add(self, assessment, root_cause=self.root)

    def test_supplied_inapplicable_root_rejects(self):
        taxonomy.set_root_cause_applicability(root_cause=self.root, applies_to_all_product_categories=False, product_categories=[])
        with self.assertRaises(ValidationError):
            add(self, begin(self), root_cause=self.root)

    def test_inactive_fault_rejects(self):
        assessment = begin(self)
        taxonomy.deactivate_fault_diagnosis(diagnosis=self.fault)
        with self.assertRaises(ValidationError):
            add(self, assessment)

    def test_inapplicable_fault_rejects(self):
        taxonomy.set_fault_diagnosis_applicability(diagnosis=self.fault, applies_to_all_product_categories=False, product_categories=[])
        with self.assertRaises(ValidationError):
            add(self, begin(self))

    def test_root_deactivation_before_completion_rejects(self):
        assessment = begin(self)
        add(self, assessment, root_cause=self.root)
        taxonomy.deactivate_root_cause(root_cause=self.root)
        with self.assertRaises(ValidationError):
            complete(self, assessment)

    def test_root_applicability_removal_before_completion_rejects(self):
        assessment = begin(self)
        add(self, assessment, root_cause=self.root)
        taxonomy.set_root_cause_applicability(root_cause=self.root, applies_to_all_product_categories=False, product_categories=[])
        with self.assertRaises(ValidationError):
            complete(self, assessment)

    def test_fault_applicability_removal_before_completion_rejects(self):
        assessment = begin(self)
        add(self, assessment)
        taxonomy.set_fault_diagnosis_applicability(diagnosis=self.fault, applies_to_all_product_categories=False, product_categories=[])
        with self.assertRaises(ValidationError):
            complete(self, assessment)

    def test_empty_assessment_cannot_complete(self):
        with self.assertRaises(ValidationError):
            complete(self, begin(self))

    def test_only_removed_findings_cannot_complete(self):
        assessment = begin(self)
        row = add(self, assessment)
        services.remove_diagnostic_finding(finding=row, actor=self.engineer)
        with self.assertRaises(ValidationError):
            complete(self, assessment)

    def test_correction_can_clear_invalid_root_to_unknown(self):
        assessment = begin(self)
        finding = add(self, assessment, root_cause=self.root)
        taxonomy.deactivate_root_cause(root_cause=self.root)
        services.update_diagnostic_finding(finding=finding, actor=self.engineer, root_cause=None, note="Cause unconfirmed")
        complete(self, assessment)

    def test_finding_update_and_revision(self):
        assessment = begin(self)
        finding = add(self, assessment)
        old = finding.updated_at
        row = services.update_diagnostic_finding(finding=finding, actor=self.engineer, fault_diagnosis=self.fault2, root_cause=self.root, note=" Synthetic ", expected_updated_at=old)
        self.assertEqual(row.note, "Synthetic")
        self.assertEqual(row.root_cause, self.root)
        with self.assertRaises(ValidationError):
            services.update_diagnostic_finding(finding=finding, actor=self.engineer, note="stale", expected_updated_at=old)

    def test_stale_removal_rejected(self):
        finding = add(self, begin(self))
        services.update_diagnostic_finding(finding=finding, actor=self.engineer, note="newer")
        with self.assertRaises(ValidationError):
            services.remove_diagnostic_finding(finding=finding, actor=self.engineer, expected_updated_at=finding.updated_at)

    def test_finding_mutations_reject_stale_aggregate_even_with_fresh_finding(self):
        assessment = begin(self)
        finding = add(self, assessment)
        assessment.refresh_from_db()
        services.update_diagnostic_assessment(assessment=assessment, actor=self.engineer, technical_note="newer")
        for operation in (services.update_diagnostic_finding, services.remove_diagnostic_finding):
            with self.assertRaises(ValidationError):
                operation(finding=finding, actor=self.engineer, expected_updated_at=finding.updated_at,
                          expected_assessment_updated_at=assessment.updated_at)

    def test_draft_removal_preserves_row_and_allows_replacement(self):
        assessment = begin(self)
        old = add(self, assessment)
        services.remove_diagnostic_finding(finding=old, actor=self.engineer)
        new = add(self, assessment)
        self.assertNotEqual(old.pk, new.pk)
        self.assertEqual(queries.diagnostic_findings_for_assessment(assessment).count(), 2)
        with self.assertRaises(ValidationError):
            services.update_diagnostic_finding(finding=old, actor=self.engineer, note="restore")

    def test_finding_mutation_invalidates_assessment_revision(self):
        assessment = begin(self)
        add(self, assessment)
        with self.assertRaises(ValidationError):
            complete(self, assessment, expected_updated_at=assessment.updated_at)

    def test_assessment_note_update_stale_guard(self):
        assessment = begin(self)
        row = services.update_diagnostic_assessment(assessment=assessment, actor=self.engineer, technical_note=" Synthetic measurement ", expected_updated_at=assessment.updated_at)
        self.assertEqual(row.technical_note, "Synthetic measurement")
        with self.assertRaises(ValidationError):
            services.update_diagnostic_assessment(assessment=assessment, actor=self.engineer, technical_note="stale", expected_updated_at=assessment.updated_at)

    def test_other_engineer_cannot_mutate_open_assessment(self):
        assessment = begin(self)
        finding = add(self, assessment)
        calls = [lambda: add(self, assessment, actor=self.engineer2, fault_diagnosis=self.fault2),
            lambda: services.update_diagnostic_finding(finding=finding, actor=self.engineer2, note="wrong actor"),
            lambda: services.remove_diagnostic_finding(finding=finding, actor=self.engineer2),
            lambda: complete(self, assessment, actor=self.engineer2),
            lambda: services.update_diagnostic_assessment(assessment=assessment, actor=self.engineer2, technical_note="wrong actor")]
        for call in calls:
            with self.assertRaises(ValidationError):
                call()

    def test_complete_note_timestamp_and_history(self):
        assessment = begin(self)
        add(self, assessment)
        row = complete(self, assessment, technical_note="Synthetic results")
        self.assertGreaterEqual(row.completed_at, row.started_at)
        self.assertEqual(row.technical_note, "Synthetic results")
        taxonomy.deactivate_fault_diagnosis(diagnosis=self.fault)
        self.assertEqual(queries.diagnostic_findings_for_assessment(row).count(), 1)

    def test_completed_null_cause_cannot_be_enriched_or_removed(self):
        assessment = begin(self)
        finding = add(self, assessment)
        complete(self, assessment)
        for call in (lambda: services.update_diagnostic_finding(finding=finding, actor=self.engineer, root_cause=self.root),
                     lambda: services.remove_diagnostic_finding(finding=finding, actor=self.engineer),
                     lambda: services.update_diagnostic_assessment(assessment=assessment, actor=self.engineer, technical_note="changed"),
                     lambda: complete(self, assessment), lambda: abandon(self, assessment)):
            with self.assertRaises(ValidationError):
                call()
        finding.root_cause = self.root
        with self.assertRaises(ValidationError):
            finding._persist()

    def test_abandon_requires_reason(self):
        assessment = begin(self)
        for reason in ("", "   ", None):
            with self.assertRaises(ValidationError):
                abandon(self, assessment, reason=reason)

    def test_stale_abandon_rejected(self):
        assessment = begin(self)
        add(self, assessment)
        with self.assertRaises(ValidationError):
            abandon(self, assessment, expected_updated_at=assessment.updated_at)

    def test_assessment_cannot_reference_another_cases_assignment(self):
        other_case = intake(self)
        wrong = ServiceDiagnosticAssessment(service_case=other_case, engineer_assignment=self.assignment, started_by=self.engineer)
        with self.assertRaises(ValidationError):
            wrong._persist()

    def test_foreign_company_case_cannot_borrow_engineer_eligibility(self):
        from apps.organization.assignment_services import create_assignment
        other_case = intake(self, company=self.other_company, service_center=self.other_center, customer=self.outsider)
        path = create_assignment(user=self.engineer2, company=self.other_company)
        access.create_role_assignment(user=self.engineer2, role=self.role, organization_assignment=path)
        assign(self, service_case=other_case, engineer=self.engineer2)
        with self.assertRaises(ValidationError):
            begin(self, service_case=other_case)
        foreign = begin(self, service_case=other_case, actor=self.engineer2)
        local = begin(self)
        self.assertEqual(list(queries.diagnostic_assessment_history(self.case)), [local])
        self.assertEqual(list(queries.diagnostic_assessment_history(other_case)), [foreign])
        self.assertEqual(list(queries.diagnosing_service_cases_for_engineer(self.engineer)), [self.case])

    def test_abandon_retains_findings_and_allows_reassignment(self):
        assessment = begin(self)
        add(self, assessment)
        row = abandon(self, assessment)
        self.assertEqual(row.abandoned_by, self.user)
        self.assertIsNone(row.completed_at)
        self.assertEqual(row.findings.count(), 1)
        reassign(self)
        next_assessment = begin(self, actor=self.engineer2)
        self.assertNotEqual(next_assessment.engineer_assignment_id, row.engineer_assignment_id)
        self.assertEqual(list(queries.diagnostic_assessment_history(self.case)), [row, next_assessment])

    def test_abandoned_history_immutable(self):
        assessment = begin(self)
        finding = add(self, assessment)
        closed = abandon(self, assessment)
        closed.technical_note = "rewrite"
        with self.assertRaises(ValidationError):
            closed._persist()
        with self.assertRaises(ValidationError):
            services.update_diagnostic_finding(finding=finding, actor=self.engineer, note="rewrite")

    def test_open_and_completed_diagnosis_block_assignment_changes(self):
        assessment = begin(self)
        add(self, assessment)
        for completed in (False, True):
            if completed:
                complete(self, assessment)
            for call in (lambda: reassign(self), lambda: unassign(self)):
                with self.assertRaises(ValidationError):
                    call()

    def test_cancellation_abandons_and_ends_at_same_time(self):
        assessment = begin(self)
        add(self, assessment)
        case = cancel_service_case(service_case=self.case, cancelled_by=self.user)
        assessment.refresh_from_db()
        self.assignment.refresh_from_db()
        self.assertEqual(assessment.abandoned_at, case.cancelled_at)
        self.assertEqual(self.assignment.ended_at, case.cancelled_at)
        self.assertEqual(assessment.abandoned_by, self.user)
        self.assertEqual(assessment.findings.count(), 1)

    def test_cancel_completed_keeps_completed_evidence(self):
        assessment = begin(self)
        add(self, assessment)
        complete(self, assessment)
        cancel_service_case(service_case=self.case, cancelled_by=self.user)
        assessment.refresh_from_db()
        self.assertIsNotNone(assessment.completed_at)
        self.assertIsNone(assessment.abandoned_at)

    def test_cancelled_case_cannot_begin(self):
        cancel_service_case(service_case=self.case, cancelled_by=self.user)
        with self.assertRaises(ValidationError):
            begin(self)

    def test_begin_rollback_after_assessment_insert(self):
        with patch.object(ServiceCase, "_persist", side_effect=ValidationError("Synthetic failure")):
            with self.assertRaises(ValidationError):
                begin(self)
        self.assertFalse(ServiceDiagnosticAssessment.objects.exists())

    def test_completion_rollback_after_assessment_closed(self):
        assessment = begin(self)
        add(self, assessment)
        with patch.object(ServiceCase, "_persist", side_effect=ValidationError("Synthetic failure")):
            with self.assertRaises(ValidationError):
                complete(self, assessment)
        self.assertIsNotNone(queries.current_diagnostic_assessment(self.case))

    def test_cancellation_rollback_restores_assessment_and_assignment(self):
        assessment = begin(self)
        add(self, assessment)
        with patch.object(ServiceCase, "_persist", side_effect=ValidationError("Synthetic failure")):
            with self.assertRaises(ValidationError):
                cancel_service_case(service_case=self.case, cancelled_by=self.user)
        self.assertIsNotNone(queries.current_diagnostic_assessment(self.case))
        self.assertIsNotNone(engineer_queries.current_engineer_assignment(self.case))

    def test_database_one_open_assessment(self):
        begin(self)
        with self.assertRaises(IntegrityError), transaction.atomic():
            ServiceDiagnosticAssessment.objects.bulk_create([ServiceDiagnosticAssessment(service_case=self.case, engineer_assignment=self.assignment, started_by=self.engineer)])

    def test_database_outcome_temporal_coherence(self):
        assessment = begin(self)
        for facts in (dict(completed_at=timezone.now()), dict(completed_by=self.engineer),
                      dict(abandoned_at=timezone.now(), abandoned_by=self.user),
                      dict(completed_at=assessment.started_at-timedelta(seconds=1), completed_by=self.engineer),
                      dict(abandoned_at=assessment.started_at-timedelta(seconds=1), abandoned_by=self.user, abandon_reason="Synthetic"),
                      dict(completed_at=timezone.now(), completed_by=self.engineer, abandoned_at=timezone.now(), abandoned_by=self.user, abandon_reason="Synthetic")):
            with self.subTest(facts=facts), self.assertRaises(IntegrityError), transaction.atomic():
                ServiceDiagnosticAssessment.objects.filter(pk=assessment.pk).update(**facts)

    def test_database_null_duplicate_constraint(self):
        assessment = begin(self)
        add(self, assessment)
        with self.assertRaises(IntegrityError), transaction.atomic():
            ServiceDiagnosticFinding.objects.bulk_create([ServiceDiagnosticFinding(assessment=assessment, fault_diagnosis=self.fault)])

    def test_protect_and_history_delete(self):
        assessment = begin(self)
        finding = add(self, assessment, root_cause=self.root)
        for obj in (self.fault, self.root, self.assignment, assessment):
            with self.assertRaises(ProtectedError):
                Collector(using="default").collect([obj])
        for obj in (assessment, finding):
            with self.assertRaises(ValidationError):
                obj.delete()
            with self.assertRaises(ValidationError):
                obj.save()

    def test_query_laziness_isolation_and_counts(self):
        assessment = begin(self)
        finding = add(self, assessment)
        with self.assertNumQueries(0):
            history = queries.diagnostic_assessment_history(self.case)
            findings = queries.diagnostic_findings_for_assessment(assessment)
            queue = queries.diagnosing_service_cases_for_engineer(self.engineer)
        with self.assertNumQueries(1):
            self.assertEqual([r.engineer_assignment.engineer for r in history], [self.engineer])
        with self.assertNumQueries(1):
            self.assertEqual([r.fault_diagnosis for r in findings], [self.fault])
        with self.assertNumQueries(1):
            self.assertEqual(queries.current_diagnostic_assessment(self.case).started_by, self.engineer)
        with self.assertNumQueries(1):
            self.assertEqual([r.company for r in queue], [self.company])
        self.assertFalse(queries.diagnostic_assessment_history(intake(self)).exists())
        self.assertFalse(queries.diagnostic_findings_for_assessment(None).exists())
        self.assertEqual(finding.assessment_id, assessment.pk)

    def test_queue_includes_all_assigned_work_states(self):
        with self.assertNumQueries(1):
            self.assertEqual(list(engineer_queries.assigned_service_cases_for_engineer(self.engineer)), [self.case])
        assessment = begin(self)
        add(self, assessment)
        self.assertEqual(list(queries.diagnosing_service_cases_for_engineer(self.engineer)), [self.case])
        complete(self, assessment)
        self.assertEqual(list(queries.diagnosed_service_cases_for_engineer(self.engineer)), [self.case])
        self.assertEqual(list(engineer_queries.assigned_service_cases_for_engineer(self.engineer)), [self.case])
        self.assertFalse(queries.diagnosed_service_cases_for_engineer(self.engineer2).exists())


class DiagnosisAdminTests(TestCase):
    def setUp(self):
        setup_diagnosis(self)
        self.engineer.is_staff = True
        self.engineer.save()
        self.engineer.user_permissions.add(Permission.objects.get(content_type__app_label="service", codename="change_servicecase"))
        self.client.force_login(self.engineer)
        self.url = reverse("admin:service_servicecase_diagnosis", args=[self.case.pk])

    def post(self, operation, **kwargs):
        token = self.client.get(self.url).context["form"].initial["revision"]
        return self.client.post(self.url, dict(operation=operation, revision=token, **kwargs))

    def test_admin_full_unknown_cause_workflow(self):
        self.assertEqual(self.post("begin").status_code, 302)
        self.assertEqual(self.post("add", fault_diagnosis=self.fault.pk).status_code, 302)
        finding = ServiceDiagnosticFinding.objects.get()
        self.assertEqual(self.post("update", finding=finding.pk, fault_diagnosis=self.fault2.pk, note="Synthetic correction").status_code, 302)
        self.assertEqual(self.post("note", technical_note="Synthetic measurements").status_code, 302)
        self.assertEqual(self.post("complete", technical_note="Synthetic results").status_code, 302)
        self.assertEqual(ServiceDiagnosticAssessment.objects.get().completed_by, self.engineer)
        self.assertIsNone(ServiceDiagnosticFinding.objects.get().root_cause_id)
        assert_diagnostic_invariants(self)

    def test_admin_remove_and_abandon(self):
        assessment = begin(self)
        finding = add(self, assessment)
        self.assertEqual(self.post("remove", finding=finding.pk).status_code, 302)
        self.assertEqual(self.post("abandon", reason="Synthetic recovery").status_code, 302)
        self.assertEqual(ServiceDiagnosticFinding.objects.count(), 1)

    def test_admin_other_engineer_and_superuser_cannot_impersonate(self):
        self.user.is_staff = self.user.is_superuser = True
        self.user.save()
        self.client.force_login(self.user)
        self.assertContains(self.post("begin"), "currently eligible assigned engineer")
        self.assertFalse(ServiceDiagnosticAssessment.objects.exists())

    def test_admin_csrf_and_permission_gate(self):
        client = Client(enforce_csrf_checks=True)
        client.force_login(self.engineer)
        self.assertEqual(client.post(self.url, {}).status_code, 403)
        self.engineer2.is_staff = True
        self.engineer2.save()
        self.client.force_login(self.engineer2)
        self.assertEqual(self.client.get(self.url).status_code, 403)

    def test_admin_stale_finding_and_completion_forms(self):
        assessment = begin(self)
        finding = add(self, assessment)
        token = self.client.get(self.url).context["form"].initial["revision"]
        services.update_diagnostic_finding(finding=finding, actor=self.engineer, note="newer")
        for operation in ("update", "complete"):
            response = self.client.post(self.url, dict(operation=operation, revision=token, finding=finding.pk, fault_diagnosis=self.fault.pk))
            self.assertContains(response, "changed; reload")

    def test_admin_fresh_choice_cannot_bypass_old_aggregate_revision(self):
        from .diagnostic_admin import DiagnosisForm
        assessment = begin(self)
        finding = add(self, assessment)
        token = self.client.get(self.url).context["form"].initial["revision"]
        original = DiagnosisForm.clean
        def intervening_write(form):
            data = original(form)
            updated = services.update_diagnostic_finding(finding=finding, actor=self.engineer, note="newer")
            data["finding"] = updated
            return data
        with patch.object(DiagnosisForm, "clean", intervening_write):
            response = self.client.post(self.url, dict(operation="update", revision=token, finding=finding.pk,
                fault_diagnosis=self.fault.pk, note="stale overwrite"))
        self.assertContains(response, "changed; reload")
        finding.refresh_from_db()
        self.assertEqual(finding.note, "newer")

    def test_admin_taxonomy_choices_filtered(self):
        assessment = begin(self)
        taxonomy.deactivate_root_cause(root_cause=self.root)
        taxonomy.set_fault_diagnosis_applicability(diagnosis=self.fault2, applies_to_all_product_categories=False, product_categories=[])
        form = self.client.get(self.url).context["form"]
        self.assertFalse(form.fields["root_cause"].queryset.exists())
        self.assertEqual(list(form.fields["fault_diagnosis"].queryset), [self.fault])
        self.assertEqual(assessment.service_case, self.case)

    def test_admin_completed_unknown_history_readonly_and_delete_disabled(self):
        assessment = begin(self)
        finding = add(self, assessment)
        complete(self, assessment)
        self.user.is_staff = self.user.is_superuser = True
        self.user.save()
        self.client.force_login(self.user)
        for model, obj in (("servicediagnosticassessment", assessment), ("servicediagnosticfinding", finding)):
            url = reverse(f"admin:service_{model}_change", args=[obj.pk])
            self.assertEqual(self.client.get(url).status_code, 200)
            self.assertEqual(self.client.post(url, dict(root_cause=self.root.pk)).status_code, 403)
            self.assertEqual(self.client.post(reverse(f"admin:service_{model}_delete", args=[obj.pk])).status_code, 403)
            self.assertEqual(self.client.get(reverse(f"admin:service_{model}_add")).status_code, 403)
        response = self.client.get(reverse("admin:service_servicecase_change", args=[self.case.pk]))
        self.assertNotContains(response, 'name="status"')

    def test_admin_cross_case_finding_selection_rejected(self):
        assessment = begin(self)
        finding = add(self, assessment)
        other_case = intake(self)
        assign(self, service_case=other_case)
        services.begin_service_case_diagnosis(service_case=other_case, actor=self.engineer)
        other_url = reverse("admin:service_servicecase_diagnosis", args=[other_case.pk])
        token = self.client.get(other_url).context["form"].initial["revision"]
        response = self.client.post(other_url, dict(operation="remove", finding=finding.pk, revision=token))
        self.assertEqual(response.status_code, 200)
        finding.refresh_from_db()
        self.assertIsNone(finding.removed_at)
