"""Phase 6F: final cross-role browser fixtures and UI handoff regression."""
from django.test import TransactionTestCase, override_settings
from django.urls import reverse

from apps.devices.models import Device, CustomerDeviceRelationship
from apps.frontdesk.models import QueueEntry
from apps.service.models import ServiceCase
from apps.operations.uat import test_journeys as journeys
from apps.operations.test_ui import capture_page


@override_settings(COMMUNICATION_PROVIDERS={})
class FinalOperationalUATTests(TransactionTestCase):
    # Reuse synthetic setup and UI drivers, without inheriting frozen test cases.
    persona = journeys.JourneyTests.persona
    setUp = journeys.JourneyTests.setUp

    def capture(self, response, name):
        capture_page(response, "final-" + name)

    def test_device_registration_cancel_preserves_customer_context(self):
        self.client.force_login(self.front)
        url = reverse("operations:register_device", args=[self.customer.pk])
        back = reverse("operations:detail", args=["customers", self.customer.pk])
        before = (Device.objects.count(), CustomerDeviceRelationship.objects.count())
        for method, status in (("get", 200), ("post", 400)):
            with self.subTest(method=method):
                response = getattr(self.client, method)(url, {})
                self.assertContains(response, f'href="{back}"', status_code=status)
                self.assertContains(response, "Cancel and return to customer", status_code=status)
                self.capture(response, "device-registration-" + method)
        self.assertEqual(self.client.get(back).status_code, 200)
        from apps.catalog.models import ProductVariant
        foreign_variant = ProductVariant.objects.filter(product_model=self.model2).first()
        for values in ({"product_model": "not-a-uuid", "serial": "6F1-PRESERVED-SERIAL"},
                       {"product_model": self.model.pk, "product_variant": foreign_variant.pk,
                        "ownership_confirmed": "on", "serial": "6F1-PRESERVED-SERIAL"}):
            response = self.client.post(url, values)
            self.assertContains(response, f'href="{back}"', status_code=400)
            self.assertContains(response, 'value="6F1-PRESERVED-SERIAL"', status_code=400)
            self.assertTrue(response.context["form"].errors)
            self.assertNotContains(response, "Traceback", status_code=400)
        foreign_url = reverse("operations:register_device", args=[self.outsider.pk])
        for method in ("get", "post"):
            self.assertEqual(getattr(self.client, method)(foreign_url, {}).status_code, 404)
        self.assertEqual(before, (Device.objects.count(), CustomerDeviceRelationship.objects.count()))
        self.client.force_login(self.engineer)
        self.assertEqual(self.client.get(url).status_code, 404)
        self.assertEqual(self.client.post(url, {}).status_code, 404)

    def ui(self, actor, route, action, data=None):
        """Every primary handoff checks direct GET/POST denial before success."""
        inappropriate = self.cashier if actor in (self.front, self.engineer, self.inspector, self.warehouse) else self.engineer
        url = reverse("operations:" + route, args=[self.case.pk, action])
        self.client.force_login(inappropriate)
        for method in ("get", "post"):
            response = getattr(self.client, method)(url, data or {})
            self.assertIn(response.status_code, (403, 404), (method, action))
        response = journeys.JourneyTests.ui(self, actor, route, action, data)
        landing = self.client.get(response.url)
        self.assertEqual(landing.status_code, 200)
        self.capture(landing, "handoff-" + action + "-" + str((data or {}).get("operation", "result")))
        return response

    def arrive_through_ui(self):
        self.client.force_login(self.front)
        search = self.client.get(reverse("operations:search"), {"q": self.customer.customer_number})
        customer_url = reverse("operations:detail", args=["customers", self.customer.pk])
        self.assertContains(search, customer_url)
        customer = self.client.get(customer_url)
        register = reverse("operations:register_device", args=[self.customer.pk])
        self.assertContains(customer, register)
        before = set(Device.objects.values_list("pk", flat=True))
        response = self.client.post(register, {"product_model": self.model.pk, "ownership_confirmed": "on"})
        self.assertEqual(response.status_code, 302, response.content.decode())
        self.device = Device.objects.exclude(pk__in=before).get()
        self.assertEqual(self.client.get(response.url).status_code, 200)
        desk_url = reverse("frontdesk:desk", args=[self.center.pk])
        walk_in = reverse("frontdesk:operation", args=[self.center.pk, "walk-in"])
        self.assertContains(self.client.get(desk_url), walk_in)
        self.assertEqual(self.client.get(walk_in).status_code, 200)
        reason = "Intermittent charging connector: inspect and confirm cause. " * 12
        self.assertEqual(self.client.post(walk_in, {"customer": self.customer.pk,
            "device": self.device.pk, "reason": reason}).status_code, 302)
        queue = QueueEntry.objects.get(customer=self.customer)
        for action in ("call", "serve", "intake"):
            url = reverse("frontdesk:record-operation", args=[self.center.pk, action, queue.pk])
            self.assertContains(self.client.get(desk_url), url)
            self.assertEqual(self.client.get(url).status_code, 200)
            if action == "intake":
                self.client.force_login(self.engineer)
                self.assertIn(self.client.post(url, {"device": self.device.pk}).status_code, (403, 404))
                self.client.force_login(self.front)
            self.assertEqual(self.client.post(url, {"device": self.device.pk}).status_code, 302)
        queue.refresh_from_db()
        self.case = queue.service_case
        self.assertEqual(self.case.intake_note, reason.strip())
        # Replaying intake resolves to the same authoritative case.
        self.assertEqual(self.client.post(url, {"device": self.device.pk}).status_code, 302)
        self.assertEqual(ServiceCase.objects.count(), 1)
        self.capture(self.client.get(desk_url), "frontdesk-intake")
        complete = reverse("frontdesk:record-operation", args=[self.center.pk, "complete", queue.pk])
        self.assertEqual(self.client.post(complete, {}).status_code, 302)

    def test_personas_are_scoped_and_empty_workspaces_render(self):
        people = (self.front, self.engineer, self.engineer2, self.inspector,
                  self.warehouse, self.cashier, self.finance, self.supervisor)
        for person in people:
            self.assertFalse(person.is_superuser)
            self.client.force_login(person)
            response = self.client.get(reverse("operations:dashboard"))
            self.assertEqual(response.status_code, 200)
            self.capture(response, "empty-dashboard-" + person.username)
        self.assertTrue(self.admin.is_superuser)
        for actor, route in ((self.supervisor, "sla:dashboard"),
                             (self.supervisor, "communications:history"),
                             (self.admin, "configuration:index")):
            self.client.force_login(actor)
            response = self.client.get(reverse(route))
            self.assertEqual(response.status_code, 200)
            self.capture(response, route.replace(":", "-"))
        self.client.logout()
        self.capture(self.client.get(reverse("operations:login")), "login")

    def test_appointment_uuid_search_does_not_grant_engineer_access(self):
        from datetime import time, timedelta
        from django.utils import timezone
        from apps.frontdesk import services
        slot = services.create_slot(actor=self.admin, service_center=self.center,
            date=timezone.localdate() + timedelta(days=1), start_time=time(9),
            end_time=time(10), capacity=2)
        booking = services.book_appointment(actor=self.front, slot=slot,
            customer=self.customer, device=self.device)
        detail = reverse("operations:detail", args=["appointments", booking.pk])
        self.client.force_login(self.front)
        response = self.client.get(reverse("operations:search"), {"q": str(booking.pk)})
        self.assertContains(response, detail)
        self.assertEqual(self.client.get(detail).status_code, 200)
        self.capture(response, "appointment-search")
        self.client.force_login(self.engineer)
        response = self.client.get(reverse("operations:search"), {"q": str(booking.pk)})
        self.assertNotContains(response, detail)
        self.assertNotContains(response, self.customer.display_name)
        self.assertEqual(self.client.get(detail).status_code, 404)
        self.assertEqual(self.client.post(detail, {}).status_code, 405)
        self.capture(response, "search-no-accessible-result")

    def test_intake_remarks_survive_the_frontdesk_engineer_handoff(self):
        from apps.service.tests import intake
        from django.utils.html import escape
        from django.db import connection
        from django.test.utils import CaptureQueriesContext
        note = "6F1-PRIVATE-REMARK <script>alert('uat')</script> " + "long-observation-" * 40
        case = intake(self, intake_note=note)
        blank = intake(self)
        sibling = intake(self, service_center=self.center2, intake_note="SIBLING-INVISIBLE-INTAKE")
        foreign = intake(self, company=self.other_company, service_center=self.other_center, customer=self.outsider, device=self.device2,
                         intake_note="FOREIGN-INVISIBLE-INTAKE")
        before = ServiceCase.objects.filter(pk=case.pk).values().get()
        # Match the frozen technical-only history budget's permission set.
        self.client.force_login(self.engineer2)
        for section, budget in (("diagnosis", 12), ("history", 16)):
            with CaptureQueriesContext(connection) as empty_queries:
                empty = self.client.get(reverse("operations:job_section", args=[blank.pk, section]))
            self.assertContains(empty, "Not recorded")
            self.assertNotContains(empty, "6F1-PRIVATE-REMARK")
            with CaptureQueriesContext(connection) as populated_queries:
                response = self.client.get(reverse("operations:job_section", args=[case.pk, section]))
            self.assertEqual(len(empty_queries), len(populated_queries))
            self.assertLessEqual(len(populated_queries), budget)
            print(f"6F.1 {section}, empty/populated remarks: {len(empty_queries)}/{len(populated_queries)} queries")
            self.assertContains(response, "Intake remarks")
            self.assertContains(response, escape(note))
            self.assertNotContains(response, "<script>alert('uat')</script>")
            self.assertNotContains(response, "FOREIGN-INVISIBLE-INTAKE")
            self.assertNotContains(response, "SIBLING-INVISIBLE-INTAKE")
            for hidden in (foreign, sibling):
                self.assertEqual(self.client.get(reverse("operations:job_section", args=[hidden.pk, section])).status_code, 404)
                self.assertEqual(self.client.get(reverse("operations:detail", args=["cases", hidden.pk])).status_code, 404)
            import re
            for navigation in re.findall(r"<nav\b.*?</nav>", response.content.decode(), re.S):
                self.assertNotIn("6F1-PRIVATE-REMARK", navigation)
            self.capture(response, "intake-remarks-" + section)
        self.client.force_login(self.engineer)
        for url in (reverse("operations:dashboard"), reverse("operations:search") + "?q=" + case.job_number,
                    reverse("operations:detail", args=["cases", case.pk]),
                    *(reverse("operations:job_section", args=[case.pk, section]) for section in ("repair", "delivery", "parts"))):
            response = self.client.get(url)
            self.assertEqual(response.status_code, 200)
            self.assertNotContains(response, "6F1-PRIVATE-REMARK")
        unauthorized = self.persona("no-case", ["customers.view_customer"], company_scope=True)
        self.client.force_login(unauthorized)
        for section in ("diagnosis", "history"):
            response = self.client.get(reverse("operations:job_section", args=[case.pk, section]))
            self.assertEqual(response.status_code, 404)
            self.assertNotContains(response, "6F1-PRIVATE-REMARK", status_code=404)
        self.assertNotContains(self.client.get(reverse("operations:dashboard")), "6F1-PRIVATE-REMARK")
        self.assertEqual(before, ServiceCase.objects.filter(pk=case.pk).values().get())
        self.assertFalse(case.complaints.exists())
        self.assertFalse(case.diagnostic_assessments.exists())

    def test_frontdesk_to_closure_through_scoped_operational_forms(self):
        self.arrive_through_ui()
        self.ui(self.front,"workflow","assignment",dict(operation="assign",engineer=self.engineer.pk))
        self.ui(self.engineer,"workflow","diagnosis",dict(operation="begin"))
        self.ui(self.engineer,"workflow","diagnosis",dict(operation="add",fault_diagnosis=self.fault.pk))
        self.ui(self.engineer,"workflow","diagnosis",dict(operation="complete"))
        self.ui(self.engineer,"workflow","repair",dict(operation="begin"))
        self.ui(self.engineer,"workflow","repair",dict(operation="add",repair_action=self.action_type.pk))
        from apps.service.models import ServiceRepairExecution, ServiceRepairAction
        self.execution=ServiceRepairExecution.objects.get(service_case=self.case)
        self.action=ServiceRepairAction.objects.get(repair_execution=self.execution)
        self.ui(self.cashier,"commercial_workflow","quote-create",dict(currency="BDT"))
        self.ui(self.cashier,"commercial_workflow","quote-lines",{
            "lines-TOTAL_FORMS":"1","lines-INITIAL_FORMS":"0","lines-0-kind":"PART","lines-0-responsibility":"CUSTOMER",
            "lines-0-description":"UAT connector","lines-0-quantity":"2","lines-0-unit_price":"100",
            "lines-0-discount":"0","lines-0-tax_rate":"0","lines-0-spare_part":self.part.pk})
        self.ui(self.cashier,"commercial_workflow","quote-submit")
        self.ui(self.cashier,"commercial_workflow","quote-decision",dict(outcome="APPROVED",channel="IN_PERSON",recipient_name="Synthetic",reference="UAT"))
        self.ui(self.warehouse,"parts_workflow","receive",dict(spare_part=self.part.pk,location=self.location.pk,quantity=2,reference="UAT UI receipt"))
        self.ui(self.engineer,"parts_workflow","request",dict(spare_part=self.part.pk,quantity=2))
        from apps.inventory.models import PartsRequest,StockReservation,PartsIssue
        request=PartsRequest.objects.get(service_case=self.case)
        self.ui(self.warehouse,"parts_workflow","approve",dict(request=request.pk))
        self.ui(self.warehouse,"parts_workflow","reserve",dict(line=request.lines.get().pk,location=self.location.pk,quantity=2))
        reservation=StockReservation.objects.get(service_case=self.case)
        self.ui(self.warehouse,"parts_workflow","issue",dict(reservation=reservation.pk))
        issue=PartsIssue.objects.get(service_case=self.case)
        self.ui(self.engineer,"workflow","repair",dict(operation="perform",action=self.action.pk))
        self.ui(self.engineer,"parts_workflow","consume",dict(issue=issue.pk,repair_action=self.action.pk,quantity=1))
        self.ui(self.warehouse,"parts_workflow","return",dict(issue=issue.pk,location=self.location.pk,quantity=1))
        self.ui(self.engineer,"workflow","repair",dict(operation="complete",outcome="REPAIRED"))
        self.ui(self.inspector,"workflow","qc",dict(operation="submit"))
        self.ui(self.inspector,"workflow","qc",dict(operation="begin"))
        from apps.service import quality_control_queries as qq
        quality=qq.current_quality_control(self.case)
        for check in qq.quality_control_checks(quality):
            self.ui(self.inspector,"workflow","qc",dict(operation="check",check=check.pk,result="PASS"))
        self.assertFalse(qq.quality_control_complaint_checks(quality).exists())
        self.ui(self.inspector,"workflow","qc",dict(operation="pass"))
        self.ui(self.front,"workflow","delivery",dict(operation="ready",device_confirmed="on"))
        self.ui(self.cashier,"commercial_workflow","invoice-prepare")
        self.ui(self.cashier,"commercial_workflow","invoice-finalize")
        self.ui(self.cashier,"commercial_workflow","payment-receive",dict(amount="100",method="CASH"))
        self.ui(self.front,"workflow","delivery",dict(operation="handover",recipient_type="CUSTOMER",verification_method="CUSTOMER_RECORD",
            device_confirmed="on",verification_confirmed="on",physical_handover_confirmed="on"))
        self.ui(self.front,"workflow","delivery",dict(operation="close"))
        self.assertEqual(self.case.status,"CLOSED")
        self.assertEqual(journeys.inventory.stock_on_hand(actor=self.warehouse,location=self.location,spare_part=self.part),1)
        from apps.commercial import invoice_queries, payment_queries
        invoice = invoice_queries.finalized_service_invoice(actor=self.cashier, service_case=self.case)
        settlement = payment_queries.invoice_settlement_summary(actor=self.cashier, invoice=invoice)
        self.assertEqual((invoice.customer_pay_total, settlement["balance"], settlement["financially_clear"]), (100, 0, True))
        from apps.service.models import ServiceDiagnosticFinding
        self.assertIsNone(ServiceDiagnosticFinding.objects.get().root_cause_id)
        self.client.force_login(self.front)
        self.capture(self.client.get(reverse("operations:detail", args=["cases", self.case.pk])), "closed-job")
