"""What "last signed in" is allowed to mean.

This exists because the owners page once reported sign-in dates for people who
had never signed in, and that was read as evidence of a break-in. Django
stamps `last_login` for ANY call to `login()` — including a request-less one
made server-side during development — so the page cannot be built on it.
"""
import datetime
import json
import tempfile

from django.contrib.auth import get_user_model
from django.core.management import call_command
from django.test import TestCase
from django.urls import reverse
from django.utils import timezone

from accounts.models import LoginEvent

User = get_user_model()


class RealSignInTests(TestCase):
    def setUp(self):
        self.staff = User.objects.create_user("staff", password="pw-staff-12345", is_staff=True)
        self.owner = User.objects.create_user("owner", password="pw-owner-12345")
        self.client.force_login(self.staff)

    def page(self):
        return self.client.get(reverse("backoffice:people"), secure=True).content.decode()

    def test_a_server_side_login_is_not_a_sign_in(self):
        """The exact bug: `force_login` writes an event and stamps
        `last_login`, and the owner appeared to have signed in."""
        self.client.force_login(self.owner)          # request-less: no IP, no UA
        event = LoginEvent.objects.filter(user=self.owner).latest("occurred_at")
        self.assertEqual(event.source, LoginEvent.INTERNAL)
        self.assertIsNone(event.ip)

        self.owner.refresh_from_db()
        self.assertIsNotNone(self.owner.last_login, "Django still stamps it — that is the trap")

        self.client.force_login(self.staff)
        row = self.page()
        self.assertIn("never", row, "a server-side login must not read as a sign-in")

    def test_a_real_browser_login_counts(self):
        self.client.logout()
        self.client.post(
            reverse("login"),
            {"username": "owner", "password": "pw-owner-12345"},
            secure=True, HTTP_USER_AGENT="Mozilla/5.0 (Test)",
        )
        event = LoginEvent.objects.filter(user=self.owner, succeeded=True).latest("occurred_at")
        self.assertEqual(event.source, LoginEvent.WEB)
        self.assertTrue(event.user_agent)

    def test_the_active_filter_uses_real_sign_ins_too(self):
        """Otherwise "Active" counts accounts nobody has ever used."""
        self.client.force_login(self.owner)          # internal only
        self.client.force_login(self.staff)
        body = self.client.get(reverse("backoffice:people") + "?show=active", secure=True)
        self.assertNotContains(body, ">owner<")

        LoginEvent.objects.create(
            user=self.owner, username="owner", succeeded=True,
            source=LoginEvent.WORDPRESS, occurred_at=timezone.now() - datetime.timedelta(days=30))
        body = self.client.get(reverse("backoffice:people") + "?show=active", secure=True)
        self.assertContains(body, "owner")


class WordPressHistoryImportTests(TestCase):
    def setUp(self):
        self.owner = User.objects.create_user(
            "jhagan", email="joan@example.com", password="pw-owner-12345")

    def run_import(self, rows, **kw):
        with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False) as fh:
            json.dump(rows, fh)
            path = fh.name
        call_command("import_login_history", file=path, **kw)

    def test_wordfence_history_becomes_real_sign_ins(self):
        when = datetime.datetime(2025, 6, 1, 12, 0, tzinfo=datetime.timezone.utc)
        self.run_import([{"username": "jhagan", "ctime": when.timestamp(), "fail": 0}])
        event = LoginEvent.objects.get(user=self.owner)
        self.assertEqual(event.source, LoginEvent.WORDPRESS)
        self.assertEqual(event.occurred_at, when)

    def test_importing_twice_adds_nothing(self):
        rows = [{"username": "jhagan", "ctime": 1749000000.0, "fail": 0}]
        self.run_import(rows)
        self.run_import(rows)
        self.assertEqual(LoginEvent.objects.count(), 1)

    def test_failed_attempts_and_strangers_are_not_imported(self):
        self.run_import([
            {"username": "jhagan", "ctime": 1749000001.0, "fail": 1},
            {"username": "nobody-here", "ctime": 1749000002.0, "fail": 0},
        ])
        self.assertEqual(LoginEvent.objects.count(), 0)

    def test_a_future_dated_row_is_ignored(self):
        """A clock-skewed export must not park someone in the future, where
        every "active in the last year" question answers yes forever."""
        ahead = (timezone.now() + datetime.timedelta(days=400)).timestamp()
        self.run_import([{"username": "jhagan", "ctime": ahead, "fail": 0}])
        self.assertEqual(LoginEvent.objects.count(), 0)


class TenancyAndTransportTests(TestCase):
    """Guards for the two things the client actually fears: a document
    reaching the wrong property, or leaving the machine in clear text."""

    def test_each_property_stores_its_documents_under_its_own_root(self):
        """The three properties are separate condo corporations. The boundary
        has to hold on disk, not only in the database — the compose file
        mounts each container ONLY its own `data/<site>` tree, and that is
        worth nothing if the media root wanders outside it."""
        from django.conf import settings

        root = str(settings.PRIVATE_MEDIA_ROOT)
        self.assertNotEqual(root.rstrip("/"), "/data", "media root must be per-property")

    def test_transport_security_is_asserted(self):
        from django.conf import settings

        self.assertGreaterEqual(settings.SECURE_HSTS_SECONDS, 15768000)
        self.assertTrue(settings.SECURE_HSTS_INCLUDE_SUBDOMAINS)
        self.assertFalse(settings.SECURE_HSTS_PRELOAD, "preload is not ours to declare")
        self.assertTrue(settings.SESSION_COOKIE_SECURE)
        self.assertTrue(settings.SESSION_COOKIE_HTTPONLY)
        self.assertTrue(settings.CSRF_COOKIE_SECURE)
        self.assertFalse(settings.DEBUG)

    def test_pdf_preview_still_works_from_our_own_origin(self):
        """SAMEORIGIN is deliberate: the detail page frames its own PDF. A
        well-meaning tightening to DENY would silently break preview."""
        from django.conf import settings

        self.assertEqual(settings.X_FRAME_OPTIONS, "SAMEORIGIN")
