"""
Tests for RideService.

Following Django styleguide pattern: test__<context>__<action>__<expected_outcome>
"""

from unittest.mock import Mock, patch

import pytest
from django.test import TestCase

from apps.rides.models import Ride
from apps.rides.services import RideService
from tests.factories import (
    DesignerCompanyFactory,
    ManufacturerCompanyFactory,
    ParkAreaFactory,
    ParkFactory,
    RideFactory,
    RideModelFactory,
    UserFactory,
)


@pytest.mark.django_db
class TestRideServiceCreateRide(TestCase):
    """Tests for RideService.create_ride."""

    def test__create_ride__valid_data__creates_ride(self):
        """Test create_ride creates ride with valid data."""
        park = ParkFactory()
        user = UserFactory()

        ride = RideService.create_ride(
            name="Test Ride",
            park_id=park.pk,
            description="A test ride",
            status="OPERATING",
            category="TR",
            created_by=user,
        )

        assert ride.name == "Test Ride"
        assert ride.park == park
        assert ride.description == "A test ride"
        assert ride.status == "OPERATING"
        assert ride.category == "TR"

    def test__create_ride__with_manufacturer__sets_manufacturer(self):
        """Test create_ride sets manufacturer when provided."""
        park = ParkFactory()
        manufacturer = ManufacturerCompanyFactory()

        ride = RideService.create_ride(
            name="Test Ride",
            park_id=park.pk,
            category="RC",
            manufacturer_id=manufacturer.pk,
        )

        assert ride.manufacturer == manufacturer

    def test__create_ride__with_designer__sets_designer(self):
        """Test create_ride sets designer when provided."""
        park = ParkFactory()
        designer = DesignerCompanyFactory()

        ride = RideService.create_ride(
            name="Test Ride",
            park_id=park.pk,
            category="RC",
            designer_id=designer.pk,
        )

        assert ride.designer == designer

    def test__create_ride__with_ride_model__sets_ride_model(self):
        """Test create_ride sets ride model when provided."""
        park = ParkFactory()
        ride_model = RideModelFactory()

        ride = RideService.create_ride(
            name="Test Ride",
            park_id=park.pk,
            category="RC",
            ride_model_id=ride_model.pk,
        )

        assert ride.ride_model == ride_model

    def test__create_ride__with_park_area__sets_park_area(self):
        """Test create_ride sets park area when provided."""
        park = ParkFactory()
        area = ParkAreaFactory(park=park)

        ride = RideService.create_ride(
            name="Test Ride",
            park_id=park.pk,
            category="TR",
            park_area_id=area.pk,
        )

        assert ride.park_area == area

    def test__create_ride__invalid_park__raises_exception(self):
        """Test create_ride raises exception for invalid park."""
        from apps.parks.models import Park

        with pytest.raises(Park.DoesNotExist):
            RideService.create_ride(
                name="Test Ride",
                park_id=99999,  # Non-existent
                category="TR",
            )


@pytest.mark.django_db
class TestRideServiceUpdateRide(TestCase):
    """Tests for RideService.update_ride."""

    def test__update_ride__valid_updates__updates_ride(self):
        """Test update_ride updates ride with valid data."""
        ride = RideFactory(name="Original Name", description="Original desc")

        updated_ride = RideService.update_ride(
            ride_id=ride.pk,
            updates={"name": "Updated Name", "description": "Updated desc"},
        )

        assert updated_ride.name == "Updated Name"
        assert updated_ride.description == "Updated desc"

    def test__update_ride__partial_updates__updates_only_specified_fields(self):
        """Test update_ride only updates specified fields."""
        ride = RideFactory(name="Original", status="OPERATING")

        updated_ride = RideService.update_ride(
            ride_id=ride.pk,
            updates={"name": "New Name"},
        )

        assert updated_ride.name == "New Name"
        assert updated_ride.status == "OPERATING"  # Unchanged

    def test__update_ride__nonexistent_ride__raises_exception(self):
        """Test update_ride raises exception for non-existent ride."""
        with pytest.raises(Ride.DoesNotExist):
            RideService.update_ride(
                ride_id=99999,
                updates={"name": "New Name"},
            )


@pytest.mark.django_db
class TestRideServiceCloseRideTemporarily(TestCase):
    """Tests for RideService.close_ride_temporarily."""

    def test__close_ride_temporarily__operating_ride__changes_status(self):
        """Test close_ride_temporarily changes status to CLOSED_TEMP."""
        ride = RideFactory(status="OPERATING")
        user = UserFactory()

        result = RideService.close_ride_temporarily(ride_id=ride.pk, user=user)

        assert result.status == "CLOSED_TEMP"

    def test__close_ride_temporarily__nonexistent_ride__raises_exception(self):
        """Test close_ride_temporarily raises exception for non-existent ride."""
        with pytest.raises(Ride.DoesNotExist):
            RideService.close_ride_temporarily(ride_id=99999)


@pytest.mark.django_db
class TestRideServiceMarkRideSBNO(TestCase):
    """Tests for RideService.mark_ride_sbno."""

    def test__mark_ride_sbno__operating_ride__changes_status(self):
        """Test mark_ride_sbno changes status to SBNO."""
        ride = RideFactory(status="OPERATING")
        user = UserFactory()

        result = RideService.mark_ride_sbno(ride_id=ride.pk, user=user)

        assert result.status == "SBNO"


@pytest.mark.django_db
class TestRideServiceScheduleRideClosing(TestCase):
    """Tests for RideService.schedule_ride_closing."""

    def test__schedule_ride_closing__valid_data__schedules_closing(self):
        """Test schedule_ride_closing schedules ride closing."""
        from datetime import date, timedelta

        ride = RideFactory(status="OPERATING")
        user = UserFactory()
        closing_date = date.today() + timedelta(days=30)

        result = RideService.schedule_ride_closing(
            ride_id=ride.pk,
            closing_date=closing_date,
            post_closing_status="DEMOLISHED",
            user=user,
        )

        assert result.status == "CLOSING"
        assert result.closing_date == closing_date
        assert result.post_closing_status == "DEMOLISHED"


@pytest.mark.django_db
class TestRideServiceCloseRidePermanently(TestCase):
    """Tests for RideService.close_ride_permanently."""

    def test__close_ride_permanently__operating_ride__changes_status(self):
        """Test close_ride_permanently changes status to CLOSED_PERM."""
        ride = RideFactory(status="OPERATING")
        user = UserFactory()

        result = RideService.close_ride_permanently(ride_id=ride.pk, user=user)

        assert result.status == "CLOSED_PERM"


@pytest.mark.django_db
class TestRideServiceDemolishRide(TestCase):
    """Tests for RideService.demolish_ride."""

    def test__demolish_ride__closed_ride__changes_status(self):
        """Test demolish_ride changes status to DEMOLISHED."""
        ride = RideFactory(status="CLOSED_PERM")
        user = UserFactory()

        result = RideService.demolish_ride(ride_id=ride.pk, user=user)

        assert result.status == "DEMOLISHED"


@pytest.mark.django_db
class TestRideServiceRelocateRide(TestCase):
    """Tests for RideService.relocate_ride."""

    def test__relocate_ride__valid_data__relocates_ride(self):
        """Test relocate_ride moves ride to new park."""
        old_park = ParkFactory()
        new_park = ParkFactory()
        ride = RideFactory(park=old_park, status="OPERATING")
        user = UserFactory()

        result = RideService.relocate_ride(
            ride_id=ride.pk,
            new_park_id=new_park.pk,
            user=user,
        )

        assert result.park == new_park
        assert result.status == "RELOCATED"


@pytest.mark.django_db
class TestRideServiceReopenRide(TestCase):
    """Tests for RideService.reopen_ride."""

    def test__reopen_ride__closed_temp_ride__changes_status(self):
        """Test reopen_ride changes status to OPERATING."""
        ride = RideFactory(status="CLOSED_TEMP")
        user = UserFactory()

        result = RideService.reopen_ride(ride_id=ride.pk, user=user)

        assert result.status == "OPERATING"


@pytest.mark.django_db
class TestRideServiceHandleNewEntitySuggestions(TestCase):
    """Tests for RideService.handle_new_entity_suggestions."""

    @patch("apps.rides.services.ModerationService.create_edit_submission_with_queue")
    def test__handle_new_entity_suggestions__new_manufacturer__creates_submission(self, mock_create_submission):
        """Test handle_new_entity_suggestions creates submission for new manufacturer."""
        mock_submission = Mock()
        mock_submission.id = 1
        mock_create_submission.return_value = mock_submission

        user = UserFactory()
        form_data = {
            "manufacturer_search": "New Manufacturer",
            "manufacturer": None,
            "designer_search": "",
            "designer": None,
            "ride_model_search": "",
            "ride_model": None,
        }

        result = RideService.handle_new_entity_suggestions(
            form_data=form_data,
            submitter=user,
        )

        assert result["total_submissions"] == 1
        assert 1 in result["manufacturers"]
        mock_create_submission.assert_called_once()

    @patch("apps.rides.services.ModerationService.create_edit_submission_with_queue")
    def test__handle_new_entity_suggestions__new_designer__creates_submission(self, mock_create_submission):
        """Test handle_new_entity_suggestions creates submission for new designer."""
        mock_submission = Mock()
        mock_submission.id = 2
        mock_create_submission.return_value = mock_submission

        user = UserFactory()
        form_data = {
            "manufacturer_search": "",
            "manufacturer": None,
            "designer_search": "New Designer",
            "designer": None,
            "ride_model_search": "",
            "ride_model": None,
        }

        result = RideService.handle_new_entity_suggestions(
            form_data=form_data,
            submitter=user,
        )

        assert result["total_submissions"] == 1
        assert 2 in result["designers"]

    @patch("apps.rides.services.ModerationService.create_edit_submission_with_queue")
    def test__handle_new_entity_suggestions__new_ride_model__creates_submission(self, mock_create_submission):
        """Test handle_new_entity_suggestions creates submission for new ride model."""
        mock_submission = Mock()
        mock_submission.id = 3
        mock_create_submission.return_value = mock_submission

        user = UserFactory()
        manufacturer = ManufacturerCompanyFactory()
        form_data = {
            "manufacturer_search": "",
            "manufacturer": manufacturer,
            "designer_search": "",
            "designer": None,
            "ride_model_search": "New Model",
            "ride_model": None,
        }

        result = RideService.handle_new_entity_suggestions(
            form_data=form_data,
            submitter=user,
        )

        assert result["total_submissions"] == 1
        assert 3 in result["ride_models"]

    def test__handle_new_entity_suggestions__no_new_entities__returns_empty(self):
        """Test handle_new_entity_suggestions with no new entities returns empty result."""
        user = UserFactory()
        manufacturer = ManufacturerCompanyFactory()
        form_data = {
            "manufacturer_search": "Existing Mfr",
            "manufacturer": manufacturer,  # Already selected
            "designer_search": "",
            "designer": None,
            "ride_model_search": "",
            "ride_model": None,
        }

        result = RideService.handle_new_entity_suggestions(
            form_data=form_data,
            submitter=user,
        )

        assert result["total_submissions"] == 0
        assert len(result["manufacturers"]) == 0
        assert len(result["designers"]) == 0
        assert len(result["ride_models"]) == 0
