import contextlib
import tempfile
from pathlib import Path

import pytest
from playwright.sync_api import Page


@pytest.fixture(scope="session")
def setup_test_data(django_db_setup, django_db_blocker):
    """
    Setup test data before the test session using factories.

    This fixture:
    - Uses factories instead of shelling out to management commands
    - Is scoped to session (not autouse per test) to reduce overhead
    - Uses django_db_blocker to allow database access in session-scoped fixture
    """
    with django_db_blocker.unblock():
        from django.contrib.auth import get_user_model

        User = get_user_model()

        # Create test users if they don't exist
        test_users = [
            {"username": "testuser", "email": "testuser@example.com", "password": "testpass123"},
            {"username": "moderator", "email": "moderator@example.com", "password": "modpass123", "is_staff": True},
            {
                "username": "admin",
                "email": "admin@example.com",
                "password": "adminpass123",
                "is_staff": True,
                "is_superuser": True,
            },
        ]

        for user_data in test_users:
            password = user_data.pop("password")
            user, created = User.objects.get_or_create(username=user_data["username"], defaults=user_data)
            if created:
                user.set_password(password)
                user.save()

    yield

    # Cleanup is handled automatically by pytest-django's transactional database


@pytest.fixture(autouse=True)
def setup_page(page: Page):
    """Configure page for tests"""
    # Set viewport size
    page.set_viewport_size({"width": 1280, "height": 720})

    # Set default navigation timeout
    page.set_default_timeout(5000)

    # Listen for console errors
    page.on(
        "console",
        lambda msg: (print(f"Browser console {msg.type}: {msg.text}") if msg.type == "error" else None),
    )

    yield page


@pytest.fixture
def auth_page(page: Page, live_server, setup_test_data):
    """Fixture for authenticated page"""
    # Login using live_server URL
    page.goto(f"{live_server.url}/accounts/login/")
    page.get_by_label("Username").fill("testuser")
    page.get_by_label("Password").fill("testpass123")
    page.get_by_role("button", name="Sign In").click()

    yield page


@pytest.fixture
def mod_page(page: Page, live_server, setup_test_data):
    """Fixture for moderator page"""
    # Login as moderator using live_server URL
    page.goto(f"{live_server.url}/accounts/login/")
    page.get_by_label("Username").fill("moderator")
    page.get_by_label("Password").fill("modpass123")
    page.get_by_role("button", name="Sign In").click()

    yield page


@pytest.fixture(scope="session")
def test_images():
    """Generate temporary test images for upload tests.

    Creates simple valid JPEG images using raw bytes.
    Images are cleaned up after the test session.
    """
    # Minimal valid JPEG image (1x1 red pixel)
    # This is a valid JPEG that any image library will accept
    jpeg_bytes = bytes(
        [
            0xFF,
            0xD8,
            0xFF,
            0xE0,
            0x00,
            0x10,
            0x4A,
            0x46,
            0x49,
            0x46,
            0x00,
            0x01,
            0x01,
            0x00,
            0x00,
            0x01,
            0x00,
            0x01,
            0x00,
            0x00,
            0xFF,
            0xDB,
            0x00,
            0x43,
            0x00,
            0x08,
            0x06,
            0x06,
            0x07,
            0x06,
            0x05,
            0x08,
            0x07,
            0x07,
            0x07,
            0x09,
            0x09,
            0x08,
            0x0A,
            0x0C,
            0x14,
            0x0D,
            0x0C,
            0x0B,
            0x0B,
            0x0C,
            0x19,
            0x12,
            0x13,
            0x0F,
            0x14,
            0x1D,
            0x1A,
            0x1F,
            0x1E,
            0x1D,
            0x1A,
            0x1C,
            0x1C,
            0x20,
            0x24,
            0x2E,
            0x27,
            0x20,
            0x22,
            0x2C,
            0x23,
            0x1C,
            0x1C,
            0x28,
            0x37,
            0x29,
            0x2C,
            0x30,
            0x31,
            0x34,
            0x34,
            0x34,
            0x1F,
            0x27,
            0x39,
            0x3D,
            0x38,
            0x32,
            0x3C,
            0x2E,
            0x33,
            0x34,
            0x32,
            0xFF,
            0xC0,
            0x00,
            0x0B,
            0x08,
            0x00,
            0x01,
            0x00,
            0x01,
            0x01,
            0x01,
            0x11,
            0x00,
            0xFF,
            0xC4,
            0x00,
            0x1F,
            0x00,
            0x00,
            0x01,
            0x05,
            0x01,
            0x01,
            0x01,
            0x01,
            0x01,
            0x01,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x00,
            0x01,
            0x02,
            0x03,
            0x04,
            0x05,
            0x06,
            0x07,
            0x08,
            0x09,
            0x0A,
            0x0B,
            0xFF,
            0xC4,
            0x00,
            0xB5,
            0x10,
            0x00,
            0x02,
            0x01,
            0x03,
            0x03,
            0x02,
            0x04,
            0x03,
            0x05,
            0x05,
            0x04,
            0x04,
            0x00,
            0x00,
            0x01,
            0x7D,
            0x01,
            0x02,
            0x03,
            0x00,
            0x04,
            0x11,
            0x05,
            0x12,
            0x21,
            0x31,
            0x41,
            0x06,
            0x13,
            0x51,
            0x61,
            0x07,
            0x22,
            0x71,
            0x14,
            0x32,
            0x81,
            0x91,
            0xA1,
            0x08,
            0x23,
            0x42,
            0xB1,
            0xC1,
            0x15,
            0x52,
            0xD1,
            0xF0,
            0x24,
            0x33,
            0x62,
            0x72,
            0x82,
            0x09,
            0x0A,
            0x16,
            0x17,
            0x18,
            0x19,
            0x1A,
            0x25,
            0x26,
            0x27,
            0x28,
            0x29,
            0x2A,
            0x34,
            0x35,
            0x36,
            0x37,
            0x38,
            0x39,
            0x3A,
            0x43,
            0x44,
            0x45,
            0x46,
            0x47,
            0x48,
            0x49,
            0x4A,
            0x53,
            0x54,
            0x55,
            0x56,
            0x57,
            0x58,
            0x59,
            0x5A,
            0x63,
            0x64,
            0x65,
            0x66,
            0x67,
            0x68,
            0x69,
            0x6A,
            0x73,
            0x74,
            0x75,
            0x76,
            0x77,
            0x78,
            0x79,
            0x7A,
            0x83,
            0x84,
            0x85,
            0x86,
            0x87,
            0x88,
            0x89,
            0x8A,
            0x92,
            0x93,
            0x94,
            0x95,
            0x96,
            0x97,
            0x98,
            0x99,
            0x9A,
            0xA2,
            0xA3,
            0xA4,
            0xA5,
            0xA6,
            0xA7,
            0xA8,
            0xA9,
            0xAA,
            0xB2,
            0xB3,
            0xB4,
            0xB5,
            0xB6,
            0xB7,
            0xB8,
            0xB9,
            0xBA,
            0xC2,
            0xC3,
            0xC4,
            0xC5,
            0xC6,
            0xC7,
            0xC8,
            0xC9,
            0xCA,
            0xD2,
            0xD3,
            0xD4,
            0xD5,
            0xD6,
            0xD7,
            0xD8,
            0xD9,
            0xDA,
            0xE1,
            0xE2,
            0xE3,
            0xE4,
            0xE5,
            0xE6,
            0xE7,
            0xE8,
            0xE9,
            0xEA,
            0xF1,
            0xF2,
            0xF3,
            0xF4,
            0xF5,
            0xF6,
            0xF7,
            0xF8,
            0xF9,
            0xFA,
            0xFF,
            0xDA,
            0x00,
            0x08,
            0x01,
            0x01,
            0x00,
            0x00,
            0x3F,
            0x00,
            0xFB,
            0xD5,
            0xDB,
            0x20,
            0xA8,
            0xBA,
            0xAE,
            0xAF,
            0xDA,
            0xAD,
            0x28,
            0xA6,
            0x02,
            0x8A,
            0x28,
            0x03,
            0xFF,
            0xD9,
        ]
    )

    temp_dir = tempfile.mkdtemp(prefix="thrillwiki_test_")
    temp_path = Path(temp_dir)

    test_photo_path = temp_path / "test_photo.jpg"
    test_avatar_path = temp_path / "test_avatar.jpg"

    test_photo_path.write_bytes(jpeg_bytes)
    test_avatar_path.write_bytes(jpeg_bytes)

    yield {
        "test_photo": str(test_photo_path),
        "test_avatar": str(test_avatar_path),
    }

    # Cleanup
    import shutil

    shutil.rmtree(temp_dir, ignore_errors=True)


@pytest.fixture
def test_park(auth_page: Page, live_server, test_images):
    """Fixture for test park"""
    # Create test park using live_server URL
    auth_page.goto(f"{live_server.url}/parks/create/")
    auth_page.get_by_label("Name").fill("Test Park")
    auth_page.get_by_label("Location").fill("Orlando, FL")
    auth_page.get_by_label("Description").fill("A test theme park")
    auth_page.get_by_label("Photo").set_input_files(test_images["test_photo"])
    auth_page.get_by_role("button", name="Create Park").click()

    yield auth_page


@pytest.fixture
def test_ride(test_park: Page, live_server, test_images):
    """Fixture for test ride"""
    # Create test ride using live_server URL
    test_park.goto(f"{live_server.url}/rides/create/")
    test_park.get_by_label("Name").fill("Test Ride")
    test_park.get_by_label("Park").select_option("Test Park")
    test_park.get_by_label("Type").select_option("Roller Coaster")
    test_park.get_by_label("Description").fill("A test ride")
    test_park.get_by_label("Photo").set_input_files(test_images["test_photo"])
    test_park.get_by_role("button", name="Create Ride").click()

    yield test_park


@pytest.fixture
def test_review(test_park: Page, live_server):
    """Fixture for test review"""
    # Create test review using live_server URL
    test_park.goto(f"{live_server.url}/parks/test-park/")
    test_park.get_by_role("tab", name="Reviews").click()
    test_park.get_by_role("button", name="Write Review").click()
    test_park.get_by_label("Rating").select_option("5")
    test_park.get_by_label("Title").fill("Test Review")
    test_park.get_by_label("Review").fill("This is a test review")
    test_park.get_by_role("button", name="Submit Review").click()

    yield test_park


# =============================================================================
# FSM Testing Fixtures
# =============================================================================


@pytest.fixture
def admin_page(page: Page, live_server, setup_test_data):
    """Fixture for admin/superuser page"""
    # Login as admin using live_server URL
    page.goto(f"{live_server.url}/accounts/login/")
    page.get_by_label("Username").fill("admin")
    page.get_by_label("Password").fill("adminpass123")
    page.get_by_role("button", name="Sign In").click()

    yield page


@pytest.fixture
def submission_pending(db):
    """Create a pending EditSubmission for FSM testing."""
    from django.contrib.auth import get_user_model
    from django.contrib.contenttypes.models import ContentType

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

    User = get_user_model()

    # Get or create test user
    user, _ = User.objects.get_or_create(username="fsm_test_submitter", defaults={"email": "fsm_test@example.com"})
    user.set_password("testpass123")
    user.save()

    # Get a park
    park = Park.objects.first()
    if not park:
        pytest.skip("No parks available for testing")

    content_type = ContentType.objects.get_for_model(Park)

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

    yield submission

    # Cleanup
    with contextlib.suppress(Exception):
        submission.delete()


@pytest.fixture
def submission_approved(db):
    """Create an approved EditSubmission for FSM testing."""
    from django.contrib.auth import get_user_model
    from django.contrib.contenttypes.models import ContentType

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

    User = get_user_model()

    user, _ = User.objects.get_or_create(
        username="fsm_test_submitter_approved", defaults={"email": "fsm_approved@example.com"}
    )

    park = Park.objects.first()
    if not park:
        pytest.skip("No parks available for testing")

    content_type = ContentType.objects.get_for_model(Park)

    submission = EditSubmission.objects.create(
        user=user,
        content_type=content_type,
        object_id=park.pk,
        submission_type="EDIT",
        changes={"description": "Already approved"},
        reason="FSM approved test",
        status="APPROVED",
    )

    yield submission

    with contextlib.suppress(Exception):
        submission.delete()


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

    park = ParkFactory(name="FSM Test Park Operating", slug="fsm-test-park-operating", status="OPERATING")

    yield park


@pytest.fixture
def park_closed_temp(db):
    """Create a temporarily closed Park for FSM testing."""
    from tests.factories import ParkFactory

    park = ParkFactory(name="FSM Test Park Closed Temp", slug="fsm-test-park-closed-temp", status="CLOSED_TEMP")

    yield park


@pytest.fixture
def park_closed_perm(db):
    """Create a permanently closed Park for FSM testing."""
    from datetime import date, timedelta

    from tests.factories import ParkFactory

    park = ParkFactory(
        name="FSM Test Park Closed Perm",
        slug="fsm-test-park-closed-perm",
        status="CLOSED_PERM",
        closing_date=date.today() - timedelta(days=365),
    )

    yield park


@pytest.fixture
def ride_operating(db, park_operating):
    """Create an operating Ride for FSM testing."""
    from tests.factories import RideFactory

    ride = RideFactory(
        name="FSM Test Ride Operating", slug="fsm-test-ride-operating", park=park_operating, status="OPERATING"
    )

    yield ride


@pytest.fixture
def ride_sbno(db, park_operating):
    """Create an SBNO Ride for FSM testing."""
    from tests.factories import RideFactory

    ride = RideFactory(name="FSM Test Ride SBNO", slug="fsm-test-ride-sbno", park=park_operating, status="SBNO")

    yield ride


@pytest.fixture
def ride_closed_perm(db, park_operating):
    """Create a permanently closed Ride for FSM testing."""
    from datetime import date, timedelta

    from tests.factories import RideFactory

    ride = RideFactory(
        name="FSM Test Ride Closed Perm",
        slug="fsm-test-ride-closed-perm",
        park=park_operating,
        status="CLOSED_PERM",
        closing_date=date.today() - timedelta(days=365),
    )

    yield ride


@pytest.fixture
def queue_item_pending(db):
    """Create a pending ModerationQueue item for FSM testing."""
    from django.contrib.auth import get_user_model

    from apps.moderation.models import ModerationQueue

    User = get_user_model()

    user, _ = User.objects.get_or_create(username="fsm_queue_flagger", defaults={"email": "fsm_queue@example.com"})

    queue_item = ModerationQueue.objects.create(
        item_type="CONTENT_REVIEW",
        status="PENDING",
        priority="MEDIUM",
        title="FSM Test Queue Item",
        description="Queue item for FSM e2e testing",
        flagged_by=user,
    )

    yield queue_item

    with contextlib.suppress(Exception):
        queue_item.delete()


@pytest.fixture
def bulk_operation_pending(db):
    """Create a pending BulkOperation for FSM testing."""
    from django.contrib.auth import get_user_model

    from apps.moderation.models import BulkOperation

    User = get_user_model()

    user, _ = User.objects.get_or_create(
        username="fsm_bulk_creator", defaults={"email": "fsm_bulk@example.com", "is_staff": True}
    )

    operation = BulkOperation.objects.create(
        operation_type="IMPORT",
        status="PENDING",
        priority="MEDIUM",
        description="FSM Test Bulk Operation",
        parameters={"test": True},
        created_by=user,
        total_items=10,
    )

    yield operation

    with contextlib.suppress(Exception):
        operation.delete()


# =============================================================================
# Helper Fixtures
# =============================================================================


@pytest.fixture
def live_server(live_server_url):
    """Provide the live server URL for tests.

    Note: This fixture is provided by pytest-django. The live_server_url
    fixture provides the URL as a string.
    """

    class LiveServer:
        url = live_server_url

    return LiveServer()


@pytest.fixture
def moderator_user(db):
    """Get or create a moderator user for testing."""
    from django.contrib.auth import get_user_model

    User = get_user_model()

    user, _ = User.objects.get_or_create(
        username="moderator", defaults={"email": "moderator@example.com", "is_staff": True}
    )
    user.set_password("modpass123")
    user.save()

    return user


@pytest.fixture
def regular_user(db):
    """Get or create a regular user for testing."""
    from django.contrib.auth import get_user_model

    User = get_user_model()

    user, _ = User.objects.get_or_create(username="testuser", defaults={"email": "testuser@example.com"})
    user.set_password("testpass123")
    user.save()

    return user


@pytest.fixture
def parks_data(db):
    """Create test parks for E2E testing."""
    from tests.factories import ParkFactory

    parks = [ParkFactory(name=f"E2E Test Park {i}", slug=f"e2e-test-park-{i}", status="OPERATING") for i in range(3)]

    return parks


@pytest.fixture
def rides_data(db, parks_data):
    """Create test rides for E2E testing."""
    from tests.factories import RideFactory

    rides = []
    for park in parks_data:
        for i in range(2):
            ride = RideFactory(
                name=f"E2E Test Ride {park.name} {i}",
                slug=f"e2e-test-ride-{park.slug}-{i}",
                park=park,
                status="OPERATING",
            )
            rides.append(ride)

    return rides
