"""The staff walkthrough (v2 — rian's script).

Offered once to each staff member, automatically, after signing in; finishing
or skipping records the version so it never auto-opens again. It can always be
rerun from "Site walkthrough" under the profile icon. Owners never see any of
it — and neither does a staff member demoing in owner view.
"""
import json
import re

from django.contrib.auth import get_user_model
from django.test import TestCase
from django.urls import reverse

from accounts.tour import TOUR_ENABLED, TOUR_VERSION, should_offer_tour

User = get_user_model()


class TourOfferTests(TestCase):
    def setUp(self):
        self.staff = User.objects.create_user(
            "staff", password="pw-staff-12345", is_staff=True, first_name="Joan")
        self.owner = User.objects.create_user("owner", password="pw-owner-12345")

    def library(self):
        return self.client.get(reverse("documents:library"), secure=True).content.decode()

    def test_the_tour_is_enabled_at_version_three(self):
        self.assertTrue(TOUR_ENABLED)
        self.assertEqual(TOUR_VERSION, 3)

    def test_a_new_staff_member_is_offered_the_tour_once(self):
        self.client.force_login(self.staff)
        body = self.library()
        self.assertIn('data-autostart="1"', body)
        self.assertIn('data-first-name="Joan"', body)

    def test_finishing_records_it_and_stops_the_auto_open(self):
        self.client.force_login(self.staff)
        response = self.client.post(reverse("backoffice:tour_done"), secure=True)
        self.assertEqual(response.status_code, 200)
        self.staff.refresh_from_db()
        self.assertEqual(self.staff.tour_seen_version, TOUR_VERSION)
        self.assertIn('data-autostart="0"', self.library())
        # The engine stays on the page for the menu retrigger.
        self.assertIn('id="tour-root"', self.library())

    def test_a_v1_tester_is_reoffered_v2(self):
        self.staff.tour_seen_version = 1
        self.staff.save(update_fields=["tour_seen_version"])
        self.assertTrue(should_offer_tour(self.staff))

    def test_owners_get_no_tour_markup_at_all(self):
        self.client.force_login(self.owner)
        body = self.library()
        self.assertNotIn("tour-root", body)
        self.assertNotIn("Site walkthrough", body)

    def test_owner_view_hides_the_tour_too(self):
        """Mid-demo, the walkthrough popping up would be the worst moment."""
        self.client.force_login(self.staff)
        self.client.post(reverse("backoffice:view_toggle"), {"next": "/"}, secure=True)
        body = self.library()
        self.assertNotIn("tour-root", body)

    def test_the_menu_carries_the_retrigger_for_staff(self):
        self.client.force_login(self.staff)
        self.assertIn("data-start-tour", self.library())
        self.assertIn("Site walkthrough", self.library())

    def test_owners_cannot_mark_the_tour_done(self):
        self.client.force_login(self.owner)
        response = self.client.post(reverse("backoffice:tour_done"), secure=True)
        self.assertEqual(response.status_code, 302)


class TourStepTests(TestCase):
    """The steps are data; these keep them honest against the real UI.

    The pages are seeded first: several targets (a document group, a board
    member's admin controls) only render when content exists, and an empty
    fixture would validate nothing.
    """

    def setUp(self):
        import shutil
        import tempfile

        from django.core.files.uploadedfile import SimpleUploadedFile
        from django.test import override_settings

        from documents.models import BoardMember, Category, Collection, Document

        self._tmp = tempfile.mkdtemp(prefix="hartling-tour-test-")
        self._override = override_settings(PRIVATE_MEDIA_ROOT=self._tmp)
        self._override.enable()
        self.addCleanup(self._override.disable)
        self.addCleanup(lambda: shutil.rmtree(self._tmp, ignore_errors=True))

        self.staff = User.objects.create_user(
            "staff", password="pw-staff-12345", is_staff=True)
        category = Category.objects.create(name="Minutes", slug="minutes")
        batch = Collection.objects.create(title="AGM", slug="agm", occurred_on="2026-05-01")
        Document.objects.create(
            title="Minutes", category=category, collection=batch,
            published=True, published_date="2026-05-01",
            file=SimpleUploadedFile("m.pdf", b"%PDF-1.4", content_type="application/pdf"))
        BoardMember.objects.create(name="Pat Chair", title="Chair", active=True, order=1)
        self.client.force_login(self.staff)

    def steps(self):
        body = self.client.get(reverse("documents:library"), secure=True).content.decode()
        raw = re.search(r"data-steps='(\[.*?\])'>", body, re.S).group(1)
        return json.loads(raw)

    def test_the_script_is_twelve_steps_over_three_pages(self):
        steps = self.steps()
        self.assertEqual(len(steps), 12)
        self.assertEqual(
            [s["page"] for s in steps],
            sorted([s["page"] for s in steps], key=[s["page"] for s in steps].index),
            "steps must stay grouped by page — the tour navigates in order")

    def test_a_step_pointing_at_the_nav_offers_a_fallback(self):
        """The nav folds into the hamburger on a phone, where those items have
        no layout box. The engine picks the first target that is actually laid
        out, so each nav-pointing step must carry something else to fall back
        to — otherwise it would silently vanish on a narrow screen."""
        for step in self.steps():
            targets = step.get("targets") or [step["target"]]
            if targets[0].startswith("#nav-"):
                self.assertGreater(
                    len(targets), 1,
                    "%r points at the nav with no fallback" % step["title"])

    def test_every_target_exists_on_its_page(self):
        """A missing target is silently skipped by the engine — this fails
        loudly instead, so a redesign cannot quietly hollow out the tour."""
        import re as _re
        pages = {}
        for step in self.steps():
            targets = step.get("targets") or [step["target"]]
            pages.setdefault(step["page"], []).extend(targets)
        for page, targets in pages.items():
            body = self.client.get(page, secure=True).content.decode()
            # The steps JSON itself contains every selector — validating
            # against a page that embeds it is circular. Cut it out first.
            body = re.sub(r"<div id=\"tour-root\"[\s\S]*?></div>", "", body)
            for target in targets:
                if target.startswith("#"):
                    self.assertIn('id="%s"' % target[1:], body, "%s on %s" % (target, page))
                else:
                    cls = target.split()[-1].lstrip(".")
                    self.assertIn(cls, body, "%s on %s" % (target, page))
