"""
Test factories for ThrillWiki models.
Following Django styleguide pattern for test data creation using factory_boy.
"""

import factory
from django.contrib.auth import get_user_model
from django.contrib.gis.geos import Point
from django.utils.text import slugify
from factory import fuzzy
from factory.django import DjangoModelFactory

User = get_user_model()


class UserFactory(DjangoModelFactory):
    """Factory for creating User instances."""

    class Meta:
        model = User
        django_get_or_create = ("username",)

    username = factory.Sequence(lambda n: f"testuser{n}")
    email = factory.LazyAttribute(lambda obj: f"{obj.username}@example.com")
    first_name = factory.Faker("first_name")
    last_name = factory.Faker("last_name")
    is_active = True
    is_staff = False
    is_superuser = False

    @factory.post_generation
    def set_password(obj, create, extracted, **kwargs):
        if create:
            password = extracted or "testpass123"
            obj.set_password(password)
            obj.save()


class StaffUserFactory(UserFactory):
    """Factory for creating staff User instances."""

    is_staff = True


class SuperUserFactory(UserFactory):
    """Factory for creating superuser instances."""

    is_staff = True
    is_superuser = True


class CompanyFactory(DjangoModelFactory):
    """Factory for creating Company instances."""

    class Meta:
        model = "parks.Company"
        django_get_or_create = ("name",)

    name = factory.Faker("company")
    slug = factory.LazyAttribute(lambda obj: slugify(obj.name))
    description = factory.Faker("text", max_nb_chars=500)
    website = factory.Faker("url")
    founded_year = fuzzy.FuzzyInteger(1800, 2024)
    roles = factory.LazyFunction(lambda: ["OPERATOR"])

    @factory.post_generation
    def multiple_roles(obj, create, extracted, **kwargs):
        """Optionally add multiple roles."""
        if create and extracted:
            obj.roles = extracted
            obj.save()


class OperatorCompanyFactory(CompanyFactory):
    """Factory for companies that operate parks."""

    roles = factory.LazyFunction(lambda: ["OPERATOR"])


class ManufacturerCompanyFactory(CompanyFactory):
    """Factory for companies that manufacture rides."""

    roles = factory.LazyFunction(lambda: ["MANUFACTURER"])


class DesignerCompanyFactory(CompanyFactory):
    """Factory for companies that design rides."""

    roles = factory.LazyFunction(lambda: ["DESIGNER"])


class LocationFactory(DjangoModelFactory):
    """Factory for creating Location instances."""

    class Meta:
        model = "location.Location"

    name = factory.Faker("city")
    location_type = "park"
    latitude = fuzzy.FuzzyFloat(-90, 90)
    longitude = fuzzy.FuzzyFloat(-180, 180)
    street_address = factory.Faker("street_address")
    city = factory.Faker("city")
    state = factory.Faker("state")
    country = factory.Faker("country")
    postal_code = factory.Faker("postcode")

    @factory.lazy_attribute
    def point(self):
        return Point(float(self.longitude), float(self.latitude))


class ParkFactory(DjangoModelFactory):
    """Factory for creating Park instances."""

    class Meta:
        model = "parks.Park"
        django_get_or_create = ("slug",)

    name = factory.Sequence(lambda n: f"Test Park {n}")
    slug = factory.LazyAttribute(lambda obj: slugify(obj.name))
    description = factory.Faker("text", max_nb_chars=1000)
    status = "OPERATING"
    opening_date = factory.Faker("date_between", start_date="-50y", end_date="today")
    closing_date = None
    operating_season = factory.Faker("sentence", nb_words=4)
    size_acres = fuzzy.FuzzyDecimal(1, 1000, precision=2)
    website = factory.Faker("url")
    average_rating = fuzzy.FuzzyDecimal(1, 10, precision=2)
    ride_count = fuzzy.FuzzyInteger(5, 100)
    coaster_count = fuzzy.FuzzyInteger(1, 20)

    # Relationships
    operator = factory.SubFactory(OperatorCompanyFactory)
    property_owner = factory.SubFactory(OperatorCompanyFactory)

    @factory.post_generation
    def create_location(obj, create, extracted, **kwargs):
        """Create a location for the park."""
        if create:
            LocationFactory(content_object=obj, name=obj.name, location_type="park")


class ClosedParkFactory(ParkFactory):
    """Factory for creating closed parks."""

    status = "CLOSED_PERM"
    closing_date = factory.Faker("date_between", start_date="-10y", end_date="today")


class ParkAreaFactory(DjangoModelFactory):
    """Factory for creating ParkArea instances."""

    class Meta:
        model = "parks.ParkArea"
        django_get_or_create = ("park", "slug")

    name = factory.Faker("word")
    slug = factory.LazyAttribute(lambda obj: slugify(obj.name))
    description = factory.Faker("text", max_nb_chars=500)

    # Relationships
    park = factory.SubFactory(ParkFactory)


class RideModelFactory(DjangoModelFactory):
    """Factory for creating RideModel instances."""

    class Meta:
        model = "rides.RideModel"
        django_get_or_create = ("name", "manufacturer")

    name = factory.Faker("word")
    description = factory.Faker("text", max_nb_chars=500)

    # Relationships
    manufacturer = factory.SubFactory(ManufacturerCompanyFactory)


class RideFactory(DjangoModelFactory):
    """Factory for creating Ride instances."""

    class Meta:
        model = "rides.Ride"
        django_get_or_create = ("park", "slug")

    name = factory.Sequence(lambda n: f"Test Ride {n}")
    slug = factory.LazyAttribute(lambda obj: slugify(obj.name))
    description = factory.Faker("text", max_nb_chars=1000)
    category = fuzzy.FuzzyChoice(["RC", "WC", "TR", "WR", "DR", "CR", "FR", "SP"])
    status = "OPERATING"
    opening_date = factory.Faker("date_between", start_date="-30y", end_date="today")
    closing_date = None
    min_height_in = fuzzy.FuzzyInteger(36, 48)
    max_height_in = None
    capacity_per_hour = fuzzy.FuzzyInteger(500, 3000)
    ride_duration_seconds = fuzzy.FuzzyInteger(60, 300)
    average_rating = fuzzy.FuzzyDecimal(1, 10, precision=2)

    # Relationships
    park = factory.SubFactory(ParkFactory)
    manufacturer = factory.SubFactory(ManufacturerCompanyFactory)
    designer = factory.SubFactory(DesignerCompanyFactory)
    ride_model = factory.SubFactory(RideModelFactory)
    park_area = factory.SubFactory(ParkAreaFactory, park=factory.SelfAttribute("..park"))

    @factory.post_generation
    def create_location(obj, create, extracted, **kwargs):
        """Create a location for the ride."""
        if create:
            LocationFactory(content_object=obj, name=obj.name, location_type="ride")


class CoasterFactory(RideFactory):
    """Factory for creating roller coaster rides."""

    category = fuzzy.FuzzyChoice(["RC", "WC"])
    min_height_in = fuzzy.FuzzyInteger(42, 54)
    ride_duration_seconds = fuzzy.FuzzyInteger(90, 240)


class ParkReviewFactory(DjangoModelFactory):
    """Factory for creating ParkReview instances."""

    class Meta:
        model = "parks.ParkReview"
        django_get_or_create = ("park", "user")

    rating = fuzzy.FuzzyInteger(1, 10)
    title = factory.Faker("sentence", nb_words=6)
    content = factory.Faker("text", max_nb_chars=2000)
    visit_date = factory.Faker("date_between", start_date="-2y", end_date="today")
    is_published = True
    moderation_notes = ""

    # Relationships
    park = factory.SubFactory(ParkFactory)
    user = factory.SubFactory(UserFactory)


class RideReviewFactory(DjangoModelFactory):
    """Factory for creating RideReview instances."""

    class Meta:
        model = "rides.RideReview"
        django_get_or_create = ("ride", "user")

    rating = fuzzy.FuzzyInteger(1, 10)
    title = factory.Faker("sentence", nb_words=6)
    content = factory.Faker("text", max_nb_chars=2000)
    visit_date = factory.Faker("date_between", start_date="-2y", end_date="today")
    is_published = True
    moderation_notes = ""

    # Relationships
    ride = factory.SubFactory(RideFactory)
    user = factory.SubFactory(UserFactory)


class ModeratedReviewFactory(ParkReviewFactory):
    """Factory for creating moderated reviews."""

    moderation_notes = factory.Faker("sentence")
    moderated_by = factory.SubFactory(StaffUserFactory)
    moderated_at = factory.Faker("date_time_between", start_date="-1y", end_date="now")


class EditSubmissionFactory(DjangoModelFactory):
    """Factory for creating EditSubmission instances."""

    class Meta:
        model = "moderation.EditSubmission"

    submission_type = "UPDATE"
    changes = factory.LazyFunction(lambda: {"name": "Updated Name"})
    status = "PENDING"
    notes = factory.Faker("sentence")

    # Relationships
    submitted_by = factory.SubFactory(UserFactory)
    content_object = factory.SubFactory(ParkFactory)


# Trait mixins for common scenarios
class Traits:
    """Common trait mixins for factories."""

    @staticmethod
    def operating_park():
        """Trait for operating parks."""
        return {"status": "OPERATING", "closing_date": None}

    @staticmethod
    def closed_park():
        """Trait for closed parks."""
        return {
            "status": "CLOSED_PERM",
            "closing_date": factory.Faker("date_between", start_date="-10y", end_date="today"),
        }

    @staticmethod
    def high_rated():
        """Trait for highly rated items."""
        return {"average_rating": fuzzy.FuzzyDecimal(8, 10, precision=2)}

    @staticmethod
    def recent_submission():
        """Trait for recent submissions."""
        return {"submitted_at": factory.Faker("date_time_between", start_date="-7d", end_date="now")}


# Specialized factories for testing scenarios
class TestScenarios:
    """Pre-configured factory combinations for common test scenarios."""

    @staticmethod
    def complete_park_with_rides(num_rides=5):
        """Create a complete park with rides and reviews."""
        park = ParkFactory()
        rides = [RideFactory(park=park) for _ in range(num_rides)]
        park_review = ParkReviewFactory(park=park)
        ride_reviews = [RideReviewFactory(ride=ride) for ride in rides[:2]]

        return {
            "park": park,
            "rides": rides,
            "park_review": park_review,
            "ride_reviews": ride_reviews,
        }

    @staticmethod
    def moderation_workflow():
        """Create a complete moderation workflow scenario."""
        user = UserFactory()
        moderator = StaffUserFactory()
        park = ParkFactory()

        submission = EditSubmissionFactory(submitted_by=user, content_object=park)

        return {
            "user": user,
            "moderator": moderator,
            "park": park,
            "submission": submission,
        }

    @staticmethod
    def review_scenario():
        """Create a scenario with multiple reviews and ratings."""
        park = ParkFactory()
        users = [UserFactory() for _ in range(5)]
        reviews = [ParkReviewFactory(park=park, user=user) for user in users]

        return {"park": park, "users": users, "reviews": reviews}


class CloudflareImageFactory(DjangoModelFactory):
    """Factory for creating CloudflareImage instances."""

    class Meta:
        model = "django_cloudflareimages_toolkit.CloudflareImage"

    cloudflare_id = factory.Sequence(lambda n: f"cf-image-{n}")
    status = "uploaded"
    upload_url = factory.Faker("url")
    width = fuzzy.FuzzyInteger(100, 1920)
    height = fuzzy.FuzzyInteger(100, 1080)
    format = "jpeg"

    @factory.lazy_attribute
    def expires_at(self):
        from django.utils import timezone

        return timezone.now() + timezone.timedelta(days=365)

    @factory.lazy_attribute
    def uploaded_at(self):
        from django.utils import timezone

        return timezone.now()


class ParkPhotoFactory(DjangoModelFactory):
    """Factory for creating ParkPhoto instances."""

    class Meta:
        model = "parks.ParkPhoto"

    park = factory.SubFactory(ParkFactory)
    image = factory.SubFactory(CloudflareImageFactory)
    caption = factory.Faker("sentence", nb_words=6)
    alt_text = factory.Faker("sentence", nb_words=8)
    is_primary = False
    is_approved = True
    uploaded_by = factory.SubFactory(UserFactory)
    date_taken = factory.Faker("date_time_between", start_date="-2y", end_date="now")


class RidePhotoFactory(DjangoModelFactory):
    """Factory for creating RidePhoto instances."""

    class Meta:
        model = "rides.RidePhoto"

    ride = factory.SubFactory(RideFactory)
    image = factory.SubFactory(CloudflareImageFactory)
    caption = factory.Faker("sentence", nb_words=6)
    alt_text = factory.Faker("sentence", nb_words=8)
    is_primary = False
    is_approved = True
    uploaded_by = factory.SubFactory(UserFactory)
