"""
E2E Tests for Park and Ride FSM Status Transitions

Tests the complete HTMX FSM transition flow for park and ride status changes:
- Park: close_temporarily, reopen, close_permanently, demolish, relocate
- Ride: close_temporarily, mark_sbno, close_permanently, demolish, relocate

These tests verify:
- Status badge updates in detail page header
- Available transitions update based on current state
- Toast notifications appear with correct message/type
- HTMX swap occurs without full page reload
- StateLog entry created in database
"""

import re
from datetime import date, timedelta

import pytest
from playwright.sync_api import Page, expect


@pytest.fixture
def operating_park(db):
    """Create an operating Park for testing status transitions."""
    from tests.factories import ParkFactory

    # Use factory to create a complete park
    park = ParkFactory(name="E2E Test Park", slug="e2e-test-park", status="OPERATING")

    yield park

    # Cleanup handled by factory


@pytest.fixture
def operating_ride(db, operating_park):
    """Create an operating Ride for testing status transitions."""
    from tests.factories import RideFactory

    ride = RideFactory(name="E2E Test Ride", slug="e2e-test-ride", park=operating_park, status="OPERATING")

    yield ride


class TestParkStatusTransitions:
    """Tests for Park FSM status transitions via HTMX."""

    def test_park_close_temporarily_as_moderator(self, mod_page: Page, operating_park, live_server):
        """Test closing a park temporarily as a moderator."""
        mod_page.goto(f"{live_server.url}/parks/{operating_park.slug}/")
        mod_page.wait_for_load_state("networkidle")

        # Verify initial status badge shows Operating
        status_section = mod_page.locator("[data-park-status-actions]")
        status_badge = mod_page.locator("[data-status-badge]")
        expect(status_badge).to_contain_text("Operating")

        # Find and click "Close Temporarily" button
        close_temp_btn = status_section.get_by_role("button", name="Close Temporarily")

        if not close_temp_btn.is_visible():
            # May be in a dropdown menu
            actions_dropdown = status_section.locator("[data-actions-dropdown]")
            if actions_dropdown.is_visible():
                actions_dropdown.click()
                close_temp_btn = mod_page.get_by_role("button", name="Close Temporarily")

        # Handle confirmation dialog
        mod_page.on("dialog", lambda dialog: dialog.accept())

        close_temp_btn.click()

        # Verify toast notification
        toast = mod_page.locator("[data-toast]")
        expect(toast).to_be_visible(timeout=5000)

        # Verify status badge updated
        expect(status_badge).to_contain_text("Temporarily Closed", timeout=5000)

        # Verify database state
        operating_park.refresh_from_db()
        assert operating_park.status == "CLOSED_TEMP"

    def test_park_reopen_from_closed_temp(self, mod_page: Page, operating_park, live_server):
        """Test reopening a temporarily closed park."""
        # First close the park temporarily
        operating_park.status = "CLOSED_TEMP"
        operating_park.save()

        mod_page.goto(f"{live_server.url}/parks/{operating_park.slug}/")
        mod_page.wait_for_load_state("networkidle")

        # Verify initial status badge shows Temporarily Closed
        status_badge = mod_page.locator("[data-status-badge]")
        expect(status_badge).to_contain_text("Temporarily Closed")

        # Find and click "Reopen" button
        status_section = mod_page.locator("[data-park-status-actions]")
        reopen_btn = status_section.get_by_role("button", name="Reopen")

        mod_page.on("dialog", lambda dialog: dialog.accept())
        reopen_btn.click()

        # Verify toast notification
        toast = mod_page.locator("[data-toast]")
        expect(toast).to_be_visible(timeout=5000)

        # Verify status badge updated
        expect(status_badge).to_contain_text("Operating", timeout=5000)

        # Verify database state
        operating_park.refresh_from_db()
        assert operating_park.status == "OPERATING"

    def test_park_close_permanently_as_moderator(self, mod_page: Page, operating_park, live_server):
        """Test closing a park permanently as a moderator."""
        mod_page.goto(f"{live_server.url}/parks/{operating_park.slug}/")
        mod_page.wait_for_load_state("networkidle")

        status_section = mod_page.locator("[data-park-status-actions]")
        status_badge = mod_page.locator("[data-status-badge]")

        # Find and click "Close Permanently" button
        close_perm_btn = status_section.get_by_role("button", name="Close Permanently")

        if not close_perm_btn.is_visible():
            actions_dropdown = status_section.locator("[data-actions-dropdown]")
            if actions_dropdown.is_visible():
                actions_dropdown.click()
                close_perm_btn = mod_page.get_by_role("button", name="Close Permanently")

        mod_page.on("dialog", lambda dialog: dialog.accept())
        close_perm_btn.click()

        # Verify toast notification
        toast = mod_page.locator("[data-toast]")
        expect(toast).to_be_visible(timeout=5000)

        # Verify status badge updated
        expect(status_badge).to_contain_text("Permanently Closed", timeout=5000)

        # Verify database state
        operating_park.refresh_from_db()
        assert operating_park.status == "CLOSED_PERM"

    def test_park_demolish_from_closed_perm(self, mod_page: Page, operating_park, live_server):
        """Test transitioning a permanently closed park to demolished."""
        # Set park to permanently closed
        operating_park.status = "CLOSED_PERM"
        operating_park.closing_date = date.today() - timedelta(days=365)
        operating_park.save()

        mod_page.goto(f"{live_server.url}/parks/{operating_park.slug}/")
        mod_page.wait_for_load_state("networkidle")

        status_section = mod_page.locator("[data-park-status-actions]")
        status_badge = mod_page.locator("[data-status-badge]")

        # Find and click "Mark as Demolished" button
        demolish_btn = status_section.get_by_role("button", name="Mark as Demolished")

        if not demolish_btn.is_visible():
            actions_dropdown = status_section.locator("[data-actions-dropdown]")
            if actions_dropdown.is_visible():
                actions_dropdown.click()
                demolish_btn = mod_page.get_by_role("button", name="Mark as Demolished")

        mod_page.on("dialog", lambda dialog: dialog.accept())
        demolish_btn.click()

        # Verify toast notification
        toast = mod_page.locator("[data-toast]")
        expect(toast).to_be_visible(timeout=5000)

        # Verify status badge updated
        expect(status_badge).to_contain_text("Demolished", timeout=5000)

        # Verify database state
        operating_park.refresh_from_db()
        assert operating_park.status == "DEMOLISHED"

    def test_park_available_transitions_update(self, mod_page: Page, operating_park, live_server):
        """Test that available transitions update based on current state."""
        mod_page.goto(f"{live_server.url}/parks/{operating_park.slug}/")
        mod_page.wait_for_load_state("networkidle")

        status_section = mod_page.locator("[data-park-status-actions]")

        # Operating park should have Close Temporarily and Close Permanently
        expect(status_section.get_by_role("button", name="Close Temporarily")).to_be_visible()

        # Should NOT have Reopen (not applicable for Operating state)
        expect(status_section.get_by_role("button", name="Reopen")).not_to_be_visible()

        # Now close temporarily and verify buttons change
        operating_park.status = "CLOSED_TEMP"
        operating_park.save()

        mod_page.reload()
        mod_page.wait_for_load_state("networkidle")

        # Now should have Reopen button
        expect(status_section.get_by_role("button", name="Reopen")).to_be_visible()


class TestRideStatusTransitions:
    """Tests for Ride FSM status transitions via HTMX."""

    def test_ride_close_temporarily_as_moderator(self, mod_page: Page, operating_ride, live_server):
        """Test closing a ride temporarily as a moderator."""
        mod_page.goto(f"{live_server.url}/parks/{operating_ride.park.slug}/rides/{operating_ride.slug}/")
        mod_page.wait_for_load_state("networkidle")

        status_section = mod_page.locator("[data-ride-status-actions]")
        status_badge = mod_page.locator("[data-status-badge]")
        expect(status_badge).to_contain_text("Operating")

        # Find and click "Close Temporarily" button
        close_temp_btn = status_section.get_by_role("button", name="Close Temporarily")

        mod_page.on("dialog", lambda dialog: dialog.accept())
        close_temp_btn.click()

        # Verify toast notification
        toast = mod_page.locator("[data-toast]")
        expect(toast).to_be_visible(timeout=5000)

        # Verify status badge updated
        expect(status_badge).to_contain_text("Temporarily Closed", timeout=5000)

        # Verify database state
        operating_ride.refresh_from_db()
        assert operating_ride.status == "CLOSED_TEMP"

    def test_ride_mark_sbno_as_moderator(self, mod_page: Page, operating_ride, live_server):
        """Test marking a ride as Standing But Not Operating (SBNO)."""
        mod_page.goto(f"{live_server.url}/parks/{operating_ride.park.slug}/rides/{operating_ride.slug}/")
        mod_page.wait_for_load_state("networkidle")

        status_section = mod_page.locator("[data-ride-status-actions]")
        status_badge = mod_page.locator("[data-status-badge]")

        # Find and click "Mark SBNO" button
        sbno_btn = status_section.get_by_role("button", name="Mark SBNO")

        if not sbno_btn.is_visible():
            actions_dropdown = status_section.locator("[data-actions-dropdown]")
            if actions_dropdown.is_visible():
                actions_dropdown.click()
                sbno_btn = mod_page.get_by_role("button", name="Mark SBNO")

        mod_page.on("dialog", lambda dialog: dialog.accept())
        sbno_btn.click()

        # Verify toast notification
        toast = mod_page.locator("[data-toast]")
        expect(toast).to_be_visible(timeout=5000)

        # Verify status badge updated
        expect(status_badge).to_contain_text("SBNO", timeout=5000)

        # Verify database state
        operating_ride.refresh_from_db()
        assert operating_ride.status == "SBNO"

    def test_ride_reopen_from_closed_temp(self, mod_page: Page, operating_ride, live_server):
        """Test reopening a temporarily closed ride."""
        # First close the ride temporarily
        operating_ride.status = "CLOSED_TEMP"
        operating_ride.save()

        mod_page.goto(f"{live_server.url}/parks/{operating_ride.park.slug}/rides/{operating_ride.slug}/")
        mod_page.wait_for_load_state("networkidle")

        status_section = mod_page.locator("[data-ride-status-actions]")
        status_badge = mod_page.locator("[data-status-badge]")

        # Find and click "Reopen" button
        reopen_btn = status_section.get_by_role("button", name="Reopen")

        mod_page.on("dialog", lambda dialog: dialog.accept())
        reopen_btn.click()

        # Verify toast notification
        toast = mod_page.locator("[data-toast]")
        expect(toast).to_be_visible(timeout=5000)

        # Verify status badge updated
        expect(status_badge).to_contain_text("Operating", timeout=5000)

        # Verify database state
        operating_ride.refresh_from_db()
        assert operating_ride.status == "OPERATING"

    def test_ride_close_permanently_as_moderator(self, mod_page: Page, operating_ride, live_server):
        """Test closing a ride permanently as a moderator."""
        mod_page.goto(f"{live_server.url}/parks/{operating_ride.park.slug}/rides/{operating_ride.slug}/")
        mod_page.wait_for_load_state("networkidle")

        status_section = mod_page.locator("[data-ride-status-actions]")
        status_badge = mod_page.locator("[data-status-badge]")

        # Find and click "Close Permanently" button
        close_perm_btn = status_section.get_by_role("button", name="Close Permanently")

        if not close_perm_btn.is_visible():
            actions_dropdown = status_section.locator("[data-actions-dropdown]")
            if actions_dropdown.is_visible():
                actions_dropdown.click()
                close_perm_btn = mod_page.get_by_role("button", name="Close Permanently")

        mod_page.on("dialog", lambda dialog: dialog.accept())
        close_perm_btn.click()

        # Verify toast notification
        toast = mod_page.locator("[data-toast]")
        expect(toast).to_be_visible(timeout=5000)

        # Verify status badge updated
        expect(status_badge).to_contain_text("Permanently Closed", timeout=5000)

        # Verify database state
        operating_ride.refresh_from_db()
        assert operating_ride.status == "CLOSED_PERM"

    def test_ride_demolish_from_closed_perm(self, mod_page: Page, operating_ride, live_server):
        """Test transitioning a permanently closed ride to demolished."""
        # Set ride to permanently closed
        operating_ride.status = "CLOSED_PERM"
        operating_ride.closing_date = date.today() - timedelta(days=365)
        operating_ride.save()

        mod_page.goto(f"{live_server.url}/parks/{operating_ride.park.slug}/rides/{operating_ride.slug}/")
        mod_page.wait_for_load_state("networkidle")

        status_section = mod_page.locator("[data-ride-status-actions]")
        status_badge = mod_page.locator("[data-status-badge]")

        # Find and click "Mark as Demolished" button
        demolish_btn = status_section.get_by_role("button", name="Mark as Demolished")

        if not demolish_btn.is_visible():
            actions_dropdown = status_section.locator("[data-actions-dropdown]")
            if actions_dropdown.is_visible():
                actions_dropdown.click()
                demolish_btn = mod_page.get_by_role("button", name="Mark as Demolished")

        mod_page.on("dialog", lambda dialog: dialog.accept())
        demolish_btn.click()

        # Verify toast notification
        toast = mod_page.locator("[data-toast]")
        expect(toast).to_be_visible(timeout=5000)

        # Verify status badge updated
        expect(status_badge).to_contain_text("Demolished", timeout=5000)

        # Verify database state
        operating_ride.refresh_from_db()
        assert operating_ride.status == "DEMOLISHED"

    def test_ride_relocate_from_closed_perm(self, mod_page: Page, operating_ride, live_server):
        """Test transitioning a permanently closed ride to relocated."""
        # Set ride to permanently closed
        operating_ride.status = "CLOSED_PERM"
        operating_ride.closing_date = date.today() - timedelta(days=365)
        operating_ride.save()

        mod_page.goto(f"{live_server.url}/parks/{operating_ride.park.slug}/rides/{operating_ride.slug}/")
        mod_page.wait_for_load_state("networkidle")

        status_section = mod_page.locator("[data-ride-status-actions]")
        status_badge = mod_page.locator("[data-status-badge]")

        # Find and click "Mark as Relocated" button
        relocate_btn = status_section.get_by_role("button", name="Mark as Relocated")

        if not relocate_btn.is_visible():
            actions_dropdown = status_section.locator("[data-actions-dropdown]")
            if actions_dropdown.is_visible():
                actions_dropdown.click()
                relocate_btn = mod_page.get_by_role("button", name="Mark as Relocated")

        mod_page.on("dialog", lambda dialog: dialog.accept())
        relocate_btn.click()

        # Verify toast notification
        toast = mod_page.locator("[data-toast]")
        expect(toast).to_be_visible(timeout=5000)

        # Verify status badge updated
        expect(status_badge).to_contain_text("Relocated", timeout=5000)

        # Verify database state
        operating_ride.refresh_from_db()
        assert operating_ride.status == "RELOCATED"


class TestRideClosingWorkflow:
    """Tests for the special CLOSING status workflow with automatic transitions."""

    def test_ride_set_closing_with_future_date(self, mod_page: Page, operating_ride, live_server):
        """Test setting a ride to CLOSING status with a future closing date."""
        mod_page.goto(f"{live_server.url}/parks/{operating_ride.park.slug}/rides/{operating_ride.slug}/")
        mod_page.wait_for_load_state("networkidle")

        status_section = mod_page.locator("[data-ride-status-actions]")

        # Find and click "Set Closing" button
        set_closing_btn = status_section.get_by_role("button", name="Set Closing")

        if set_closing_btn.is_visible():
            mod_page.on("dialog", lambda dialog: dialog.accept())
            set_closing_btn.click()

            # Verify status badge updated
            status_badge = mod_page.locator("[data-status-badge]")
            expect(status_badge).to_contain_text("Closing", timeout=5000)

            # Verify database state
            operating_ride.refresh_from_db()
            assert operating_ride.status == "CLOSING"
        else:
            pytest.skip("Set Closing button not available")

    def test_ride_closing_shows_countdown(self, mod_page: Page, operating_ride, live_server):
        """Test that a ride in CLOSING status shows a countdown to closing date."""
        # Set ride to CLOSING with future date
        future_date = date.today() + timedelta(days=30)
        operating_ride.status = "CLOSING"
        operating_ride.closing_date = future_date
        operating_ride.save()

        mod_page.goto(f"{live_server.url}/parks/{operating_ride.park.slug}/rides/{operating_ride.slug}/")
        mod_page.wait_for_load_state("networkidle")

        # Verify closing countdown is displayed
        closing_info = mod_page.locator("[data-closing-countdown]")
        if closing_info.is_visible():
            expect(closing_info).to_contain_text("30")
        else:
            # May just show the status badge
            status_badge = mod_page.locator("[data-status-badge]")
            expect(status_badge).to_contain_text("Closing")


class TestStatusBadgeStyling:
    """Tests for correct status badge styling based on state."""

    def test_operating_status_badge_style(self, mod_page: Page, operating_park, live_server):
        """Test that Operating status has correct green styling."""
        mod_page.goto(f"{live_server.url}/parks/{operating_park.slug}/")
        mod_page.wait_for_load_state("networkidle")

        status_badge = mod_page.locator("[data-status-badge]")
        expect(status_badge).to_have_class(re.compile(r"bg-green|text-green|success"))

    def test_closed_temp_status_badge_style(self, mod_page: Page, operating_park, live_server):
        """Test that Temporarily Closed status has correct yellow/warning styling."""
        operating_park.status = "CLOSED_TEMP"
        operating_park.save()

        mod_page.goto(f"{live_server.url}/parks/{operating_park.slug}/")
        mod_page.wait_for_load_state("networkidle")

        status_badge = mod_page.locator("[data-status-badge]")
        expect(status_badge).to_have_class(re.compile(r"bg-yellow|text-yellow|warning"))

    def test_closed_perm_status_badge_style(self, mod_page: Page, operating_park, live_server):
        """Test that Permanently Closed status has correct red/danger styling."""
        operating_park.status = "CLOSED_PERM"
        operating_park.save()

        mod_page.goto(f"{live_server.url}/parks/{operating_park.slug}/")
        mod_page.wait_for_load_state("networkidle")

        status_badge = mod_page.locator("[data-status-badge]")
        expect(status_badge).to_have_class(re.compile(r"bg-red|text-red|danger"))

    def test_demolished_status_badge_style(self, mod_page: Page, operating_park, live_server):
        """Test that Demolished status has correct gray styling."""
        operating_park.status = "DEMOLISHED"
        operating_park.save()

        mod_page.goto(f"{live_server.url}/parks/{operating_park.slug}/")
        mod_page.wait_for_load_state("networkidle")

        status_badge = mod_page.locator("[data-status-badge]")
        expect(status_badge).to_have_class(re.compile(r"bg-gray|text-gray|muted"))
