"""
Integration Tests for FSMTransitionView

Tests the FSMTransitionView without a browser, using Django's test client.
These tests verify:
- FSMTransitionView handles HTMX requests correctly
- HX-Trigger headers contain proper toast data
- Correct partial templates rendered for each model
- Permission validation before transition execution

These are faster than E2E tests and don't require Playwright.
"""

import json

from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.test import Client, TestCase
from django.urls import reverse

User = get_user_model()


class TestFSMTransitionViewHTMX(TestCase):
    """Tests for FSMTransitionView with HTMX requests."""

    @classmethod
    def setUpTestData(cls):
        """Set up test data for all tests in this class."""
        # Create regular user
        cls.user = User.objects.create_user(username="testuser", email="testuser@example.com", password="testpass123")

        # Create moderator user
        cls.moderator = User.objects.create_user(
            username="moderator", email="moderator@example.com", password="modpass123", is_staff=True
        )

        # Create admin user
        cls.admin = User.objects.create_user(
            username="admin", email="admin@example.com", password="adminpass123", is_staff=True, is_superuser=True
        )

    def setUp(self):
        """Set up test client for each test."""
        self.client = Client()

    def test_fsm_transition_view_with_htmx_header(self):
        """Test that FSMTransitionView handles HTMX requests correctly."""
        from apps.moderation.models import EditSubmission
        from apps.parks.models import Park

        self.client.login(username="moderator", password="modpass123")

        # Create a pending submission
        park = Park.objects.first()
        if not park:
            self.skipTest("No park available for testing")

        content_type = ContentType.objects.get_for_model(Park)

        submission = EditSubmission.objects.create(
            user=self.user,
            content_type=content_type,
            object_id=park.pk,
            submission_type="EDIT",
            changes={"description": "Test change"},
            reason="Integration test",
            status="PENDING",
        )

        url = reverse(
            "core:fsm_transition",
            kwargs={
                "app_label": "moderation",
                "model_name": "editsubmission",
                "pk": submission.pk,
                "transition_name": "transition_to_approved",
            },
        )

        # Make request with HTMX header
        response = self.client.post(url, HTTP_HX_REQUEST="true")

        # Should return 200 OK
        self.assertEqual(response.status_code, 200)

        # Should have HX-Trigger header
        self.assertIn("HX-Trigger", response)

        # Parse HX-Trigger header
        trigger_data = json.loads(response["HX-Trigger"])
        self.assertIn("showToast", trigger_data)
        self.assertEqual(trigger_data["showToast"]["type"], "success")

        # Cleanup
        submission.delete()

    def test_fsm_transition_view_without_htmx_header(self):
        """Test that FSMTransitionView handles non-HTMX requests correctly."""
        from apps.moderation.models import EditSubmission
        from apps.parks.models import Park

        self.client.login(username="moderator", password="modpass123")

        park = Park.objects.first()
        if not park:
            self.skipTest("No park available for testing")

        content_type = ContentType.objects.get_for_model(Park)

        submission = EditSubmission.objects.create(
            user=self.user,
            content_type=content_type,
            object_id=park.pk,
            submission_type="EDIT",
            changes={"description": "Test change non-htmx"},
            reason="Integration test non-htmx",
            status="PENDING",
        )

        url = reverse(
            "core:fsm_transition",
            kwargs={
                "app_label": "moderation",
                "model_name": "editsubmission",
                "pk": submission.pk,
                "transition_name": "transition_to_approved",
            },
        )

        # Make request WITHOUT HTMX header
        response = self.client.post(url)

        # Should return 200 OK with JSON response
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response["Content-Type"], "application/json")

        # Parse JSON response
        data = response.json()
        self.assertTrue(data["success"])
        self.assertIn("message", data)

        # Cleanup
        submission.delete()

    def test_fsm_transition_view_returns_correct_partial(self):
        """Test that FSMTransitionView returns correct partial template."""
        from apps.moderation.models import EditSubmission
        from apps.parks.models import Park

        self.client.login(username="moderator", password="modpass123")

        park = Park.objects.first()
        if not park:
            self.skipTest("No park available for testing")

        content_type = ContentType.objects.get_for_model(Park)

        submission = EditSubmission.objects.create(
            user=self.user,
            content_type=content_type,
            object_id=park.pk,
            submission_type="EDIT",
            changes={"description": "Test partial"},
            reason="Partial test",
            status="PENDING",
        )

        url = reverse(
            "core:fsm_transition",
            kwargs={
                "app_label": "moderation",
                "model_name": "editsubmission",
                "pk": submission.pk,
                "transition_name": "transition_to_approved",
            },
        )

        response = self.client.post(url, HTTP_HX_REQUEST="true")

        # Response should contain HTML (partial template)
        self.assertIn("text/html", response["Content-Type"])

        # Cleanup
        submission.delete()

    def test_fsm_transition_view_adds_toast_trigger(self):
        """Test that FSMTransitionView adds correct HX-Trigger for toast."""
        from apps.parks.models import Park

        self.client.login(username="moderator", password="modpass123")

        park = Park.objects.filter(status="OPERATING").first()
        if not park:
            self.skipTest("No operating park available for testing")

        url = reverse(
            "core:fsm_transition",
            kwargs={
                "app_label": "parks",
                "model_name": "park",
                "pk": park.pk,
                "transition_name": "transition_to_closed_temp",
            },
        )

        response = self.client.post(url, HTTP_HX_REQUEST="true")

        # Parse HX-Trigger header
        trigger_data = json.loads(response["HX-Trigger"])

        # Verify toast structure
        self.assertIn("showToast", trigger_data)
        self.assertIn("message", trigger_data["showToast"])
        self.assertIn("type", trigger_data["showToast"])
        self.assertEqual(trigger_data["showToast"]["type"], "success")

        # Reset park status for other tests
        park.status = "OPERATING"
        park.save()

    def test_fsm_transition_view_handles_invalid_model(self):
        """Test that FSMTransitionView handles invalid model gracefully."""
        self.client.login(username="moderator", password="modpass123")

        url = reverse(
            "core:fsm_transition",
            kwargs={
                "app_label": "nonexistent",
                "model_name": "fakemodel",
                "pk": 1,
                "transition_name": "fake_transition",
            },
        )

        response = self.client.post(url, HTTP_HX_REQUEST="true")

        # Should return 404
        self.assertEqual(response.status_code, 404)

        # Should have error toast trigger
        self.assertIn("HX-Trigger", response)
        trigger_data = json.loads(response["HX-Trigger"])
        self.assertEqual(trigger_data["showToast"]["type"], "error")

    def test_fsm_transition_view_handles_invalid_transition(self):
        """Test that FSMTransitionView handles invalid transition name."""
        from apps.parks.models import Park

        self.client.login(username="moderator", password="modpass123")

        park = Park.objects.first()
        if not park:
            self.skipTest("No park available for testing")

        url = reverse(
            "core:fsm_transition",
            kwargs={
                "app_label": "parks",
                "model_name": "park",
                "pk": park.pk,
                "transition_name": "nonexistent_transition",
            },
        )

        response = self.client.post(url, HTTP_HX_REQUEST="true")

        # Should return 400 Bad Request
        self.assertEqual(response.status_code, 400)

        # Should have error toast trigger
        self.assertIn("HX-Trigger", response)
        trigger_data = json.loads(response["HX-Trigger"])
        self.assertEqual(trigger_data["showToast"]["type"], "error")

    def test_fsm_transition_view_validates_permissions(self):
        """Test that FSMTransitionView validates user permissions."""
        from apps.moderation.models import EditSubmission
        from apps.parks.models import Park

        # Login as regular user (not moderator)
        self.client.login(username="testuser", password="testpass123")

        park = Park.objects.first()
        if not park:
            self.skipTest("No park available for testing")

        content_type = ContentType.objects.get_for_model(Park)

        submission = EditSubmission.objects.create(
            user=self.user,
            content_type=content_type,
            object_id=park.pk,
            submission_type="EDIT",
            changes={"description": "Permission test"},
            reason="Permission test",
            status="PENDING",
        )

        url = reverse(
            "core:fsm_transition",
            kwargs={
                "app_label": "moderation",
                "model_name": "editsubmission",
                "pk": submission.pk,
                "transition_name": "transition_to_approved",
            },
        )

        response = self.client.post(url, HTTP_HX_REQUEST="true")

        # Should return 400 or 403 (permission denied)
        self.assertIn(response.status_code, [400, 403])

        # Verify submission was NOT changed
        submission.refresh_from_db()
        self.assertEqual(submission.status, "PENDING")

        # Cleanup
        submission.delete()


class TestFSMTransitionViewParkModel(TestCase):
    """Tests for FSMTransitionView with Park model."""

    @classmethod
    def setUpTestData(cls):
        cls.moderator = User.objects.create_user(
            username="mod_park", email="mod_park@example.com", password="modpass123", is_staff=True
        )

    def setUp(self):
        self.client = Client()
        self.client.login(username="mod_park", password="modpass123")

    def test_park_close_temporarily_transition(self):
        """Test park close temporarily transition via view."""
        from apps.parks.models import Park

        park = Park.objects.filter(status="OPERATING").first()
        if not park:
            self.skipTest("No operating park available")

        url = reverse(
            "core:fsm_transition",
            kwargs={
                "app_label": "parks",
                "model_name": "park",
                "pk": park.pk,
                "transition_name": "transition_to_closed_temp",
            },
        )

        response = self.client.post(url, HTTP_HX_REQUEST="true")

        self.assertEqual(response.status_code, 200)

        # Verify park status changed
        park.refresh_from_db()
        self.assertEqual(park.status, "CLOSED_TEMP")

        # Reset for other tests
        park.status = "OPERATING"
        park.save()

    def test_park_reopen_transition(self):
        """Test park reopen transition via view."""
        from apps.parks.models import Park

        park = Park.objects.filter(status="OPERATING").first()
        if not park:
            self.skipTest("No park available")

        # First close the park
        park.status = "CLOSED_TEMP"
        park.save()

        url = reverse(
            "core:fsm_transition",
            kwargs={
                "app_label": "parks",
                "model_name": "park",
                "pk": park.pk,
                "transition_name": "transition_to_operating",
            },
        )

        response = self.client.post(url, HTTP_HX_REQUEST="true")

        self.assertEqual(response.status_code, 200)

        # Verify park status changed
        park.refresh_from_db()
        self.assertEqual(park.status, "OPERATING")

    def test_park_slug_based_transition(self):
        """Test FSM transition using slug instead of pk."""
        from apps.parks.models import Park

        park = Park.objects.filter(status="OPERATING").first()
        if not park:
            self.skipTest("No operating park available")

        url = reverse(
            "core:fsm_transition_by_slug",
            kwargs={
                "app_label": "parks",
                "model_name": "park",
                "slug": park.slug,
                "transition_name": "transition_to_closed_temp",
            },
        )

        response = self.client.post(url, HTTP_HX_REQUEST="true")

        self.assertEqual(response.status_code, 200)

        # Verify park status changed
        park.refresh_from_db()
        self.assertEqual(park.status, "CLOSED_TEMP")

        # Reset for other tests
        park.status = "OPERATING"
        park.save()


class TestFSMTransitionViewRideModel(TestCase):
    """Tests for FSMTransitionView with Ride model."""

    @classmethod
    def setUpTestData(cls):
        cls.moderator = User.objects.create_user(
            username="mod_ride", email="mod_ride@example.com", password="modpass123", is_staff=True
        )

    def setUp(self):
        self.client = Client()
        self.client.login(username="mod_ride", password="modpass123")

    def test_ride_close_temporarily_transition(self):
        """Test ride close temporarily transition via view."""
        from apps.rides.models import Ride

        ride = Ride.objects.filter(status="OPERATING").first()
        if not ride:
            self.skipTest("No operating ride available")

        url = reverse(
            "core:fsm_transition",
            kwargs={
                "app_label": "rides",
                "model_name": "ride",
                "pk": ride.pk,
                "transition_name": "transition_to_closed_temp",
            },
        )

        response = self.client.post(url, HTTP_HX_REQUEST="true")

        self.assertEqual(response.status_code, 200)

        # Verify ride status changed
        ride.refresh_from_db()
        self.assertEqual(ride.status, "CLOSED_TEMP")

        # Reset for other tests
        ride.status = "OPERATING"
        ride.save()

    def test_ride_mark_sbno_transition(self):
        """Test ride mark SBNO transition via view."""
        from apps.rides.models import Ride

        ride = Ride.objects.filter(status="OPERATING").first()
        if not ride:
            self.skipTest("No operating ride available")

        url = reverse(
            "core:fsm_transition",
            kwargs={"app_label": "rides", "model_name": "ride", "pk": ride.pk, "transition_name": "transition_to_sbno"},
        )

        response = self.client.post(url, HTTP_HX_REQUEST="true")

        self.assertEqual(response.status_code, 200)

        # Verify ride status changed
        ride.refresh_from_db()
        self.assertEqual(ride.status, "SBNO")

        # Reset for other tests
        ride.status = "OPERATING"
        ride.save()


class TestFSMTransitionViewModerationModels(TestCase):
    """Tests for FSMTransitionView with moderation models."""

    @classmethod
    def setUpTestData(cls):
        cls.user = User.objects.create_user(username="submitter", email="submitter@example.com", password="testpass123")

        cls.moderator = User.objects.create_user(
            username="mod_moderation", email="mod_moderation@example.com", password="modpass123", is_staff=True
        )

    def setUp(self):
        self.client = Client()
        self.client.login(username="mod_moderation", password="modpass123")

    def test_edit_submission_approve_transition(self):
        """Test EditSubmission approve transition."""
        from apps.moderation.models import EditSubmission
        from apps.parks.models import Park

        park = Park.objects.first()
        if not park:
            self.skipTest("No park available")

        content_type = ContentType.objects.get_for_model(Park)

        submission = EditSubmission.objects.create(
            user=self.user,
            content_type=content_type,
            object_id=park.pk,
            submission_type="EDIT",
            changes={"description": "Approve test"},
            reason="Approve test",
            status="PENDING",
        )

        url = reverse(
            "core:fsm_transition",
            kwargs={
                "app_label": "moderation",
                "model_name": "editsubmission",
                "pk": submission.pk,
                "transition_name": "transition_to_approved",
            },
        )

        response = self.client.post(url, HTTP_HX_REQUEST="true")

        self.assertEqual(response.status_code, 200)

        # Verify submission status changed
        submission.refresh_from_db()
        self.assertEqual(submission.status, "APPROVED")

        # Cleanup
        submission.delete()

    def test_edit_submission_reject_transition(self):
        """Test EditSubmission reject transition."""
        from apps.moderation.models import EditSubmission
        from apps.parks.models import Park

        park = Park.objects.first()
        if not park:
            self.skipTest("No park available")

        content_type = ContentType.objects.get_for_model(Park)

        submission = EditSubmission.objects.create(
            user=self.user,
            content_type=content_type,
            object_id=park.pk,
            submission_type="EDIT",
            changes={"description": "Reject test"},
            reason="Reject test",
            status="PENDING",
        )

        url = reverse(
            "core:fsm_transition",
            kwargs={
                "app_label": "moderation",
                "model_name": "editsubmission",
                "pk": submission.pk,
                "transition_name": "transition_to_rejected",
            },
        )

        response = self.client.post(url, HTTP_HX_REQUEST="true")

        self.assertEqual(response.status_code, 200)

        # Verify submission status changed
        submission.refresh_from_db()
        self.assertEqual(submission.status, "REJECTED")

        # Cleanup
        submission.delete()

    def test_edit_submission_escalate_transition(self):
        """Test EditSubmission escalate transition."""
        from apps.moderation.models import EditSubmission
        from apps.parks.models import Park

        park = Park.objects.first()
        if not park:
            self.skipTest("No park available")

        content_type = ContentType.objects.get_for_model(Park)

        submission = EditSubmission.objects.create(
            user=self.user,
            content_type=content_type,
            object_id=park.pk,
            submission_type="EDIT",
            changes={"description": "Escalate test"},
            reason="Escalate test",
            status="PENDING",
        )

        url = reverse(
            "core:fsm_transition",
            kwargs={
                "app_label": "moderation",
                "model_name": "editsubmission",
                "pk": submission.pk,
                "transition_name": "transition_to_escalated",
            },
        )

        response = self.client.post(url, HTTP_HX_REQUEST="true")

        self.assertEqual(response.status_code, 200)

        # Verify submission status changed
        submission.refresh_from_db()
        self.assertEqual(submission.status, "ESCALATED")

        # Cleanup
        submission.delete()


class TestFSMTransitionViewStateLog(TestCase):
    """Tests that FSM transitions create proper StateLog entries."""

    @classmethod
    def setUpTestData(cls):
        cls.user = User.objects.create_user(
            username="submitter_log", email="submitter_log@example.com", password="testpass123"
        )

        cls.moderator = User.objects.create_user(
            username="mod_log", email="mod_log@example.com", password="modpass123", is_staff=True
        )

    def setUp(self):
        self.client = Client()
        self.client.login(username="mod_log", password="modpass123")

    def test_transition_creates_state_log(self):
        """Test that FSM transition creates a StateLog entry."""
        from django_fsm_log.models import StateLog

        from apps.moderation.models import EditSubmission
        from apps.parks.models import Park

        park = Park.objects.first()
        if not park:
            self.skipTest("No park available")

        content_type = ContentType.objects.get_for_model(Park)

        submission = EditSubmission.objects.create(
            user=self.user,
            content_type=content_type,
            object_id=park.pk,
            submission_type="EDIT",
            changes={"description": "StateLog test"},
            reason="StateLog test",
            status="PENDING",
        )

        # Count existing StateLog entries
        initial_log_count = StateLog.objects.filter(
            content_type=ContentType.objects.get_for_model(EditSubmission), object_id=submission.pk
        ).count()

        url = reverse(
            "core:fsm_transition",
            kwargs={
                "app_label": "moderation",
                "model_name": "editsubmission",
                "pk": submission.pk,
                "transition_name": "transition_to_approved",
            },
        )

        self.client.post(url, HTTP_HX_REQUEST="true")

        # Check that a new StateLog entry was created
        new_log_count = StateLog.objects.filter(
            content_type=ContentType.objects.get_for_model(EditSubmission), object_id=submission.pk
        ).count()

        self.assertEqual(new_log_count, initial_log_count + 1)

        # Verify the StateLog entry details
        latest_log = StateLog.objects.filter(
            content_type=ContentType.objects.get_for_model(EditSubmission), object_id=submission.pk
        ).latest("timestamp")

        self.assertEqual(latest_log.state, "APPROVED")
        self.assertEqual(latest_log.by, self.moderator)

        # Cleanup
        submission.delete()
