from datetime import timedelta

from django.conf import settings
from django.contrib import messages
from django.contrib.auth import login, logout, authenticate
from django.contrib.auth.decorators import login_required
from django.contrib.auth.views import LoginView
from django.core.paginator import Paginator
from django.db.models import Q, Sum
from django.http import JsonResponse, HttpResponseForbidden
from django.shortcuts import render, redirect, get_object_or_404
from django.urls import reverse
from django.utils import timezone
from django.views.decorators.http import require_POST

from core.forms import (
    SignupForm, ProfileForm, BecomeListerForm, IndividualKYCForm, BusinessKYCForm,
    ListingForm, ListingPhotoFormSet, ListingSearchForm, ReviewForm, ReviewReplyForm,
    InquiryForm, MessageForm, SavedSearchForm, ReportForm,
    EventForm, EventTicketTypeFormSet, EventSearchForm, EventTicketBookingForm,
)
from core.models import (
    Category, Listing, ListingPhoto, Profile, Favorite, SavedSearch,
    Conversation, Message, InquiryRequest, Review, Notification, Report,
    EventCategory, Event, EventPhoto, EventTicketType, EventTicketOrder, EventFavorite,
)
from core.utils import haversine_km, scan_for_duplicates


# =========================================================================
# Public / Home / Discovery
# =========================================================================

def home(request):
    """
    Landing page. First-time discoverers pick an intent (e.g. 'Houses for
    rent'); returning users see a feed filtered to their saved intent.
    """
    top_categories = Category.objects.filter(parent__isnull=True, is_active=True).order_by("order", "name")
    featured = Listing.objects.filter(
        status=Listing.STATUS_ACTIVE, 
        is_featured=True
    ).select_related("category")[:8]

    intent = None
    if request.user.is_authenticated:
        intent = getattr(request.user.profile, "preferred_intent", None)

    recent_qs = Listing.objects.filter(status=Listing.STATUS_ACTIVE).select_related("category")
    if intent:
        recent_qs = recent_qs.filter(category=intent)
    recent = recent_qs.order_by("-created_at")[:8]

    context = {
        "top_categories": top_categories,
        "featured": featured,
        "recent": recent,
        "intent": intent,
    }
    return render(request, "core/home.html", context)


def category_browse(request, slug):
    category = get_object_or_404(Category, slug=slug, is_active=True)
    category_ids = [category.pk] + list(category.children.values_list("pk", flat=True))
    return _listing_search(request, base_queryset=Listing.objects.filter(category_id__in=category_ids), category=category)


def listing_search(request):
    return _listing_search(request, base_queryset=Listing.objects.all())


def _listing_search(request, base_queryset, category=None):
    form = ListingSearchForm(request.GET or None)
    qs = base_queryset.filter(status=Listing.STATUS_ACTIVE).select_related("category", "lister", "lister__profile")

    if form.is_valid():
        data = form.cleaned_data
        if data.get("q"):
            qs = qs.filter(
                Q(title__icontains=data["q"]) | 
                Q(description__icontains=data["q"]) | 
                Q(address__icontains=data["q"])
            )
        if data.get("category"):
            qs = qs.filter(category=data["category"])
        if data.get("min_price") is not None:
            qs = qs.filter(price__gte=data["min_price"])
        if data.get("max_price") is not None:
            qs = qs.filter(price__lte=data["max_price"])
        if data.get("purpose"):
            qs = qs.filter(purpose=data["purpose"])
        if data.get("verified_only"):
            qs = qs.filter(is_verified_listing=True)
        qs = qs.order_by(data.get("sort") or "-created_at")
    else:
        qs = qs.order_by("-created_at")

    # Radius filter (post-query — SQLite has no geo functions built in)
    lat, lng, radius = None, None, None
    if form.is_valid():
        lat, lng, radius = form.cleaned_data.get("lat"), form.cleaned_data.get("lng"), form.cleaned_data.get("radius_km")

    listings = list(qs[:500])  # cap for the in-memory distance pass
    if lat and lng:
        radius = radius or 10
        filtered = []
        for listing in listings:
            d = haversine_km(lat, lng, listing.latitude, listing.longitude)
            if d is not None and d <= radius:
                listing.distance_km = round(d, 1)
                filtered.append(listing)
        listings = filtered

    paginator = Paginator(listings, getattr(settings, "ACCO_LISTINGS_PER_PAGE", 12))
    page_obj = paginator.get_page(request.GET.get("page"))

    # Get favorited IDs for the current user
    favorited_ids = []
    if request.user.is_authenticated:
        favorited_ids = Favorite.objects.filter(
            user=request.user,
            listing__in=[l.id for l in page_obj.object_list]
        ).values_list("listing_id", flat=True)

    return render(request, "core/listing_search.html", {
        "form": form, 
        "page_obj": page_obj, 
        "category": category,
        "favorited_ids": list(favorited_ids),
    })


def listing_detail(request, slug):
    listing = get_object_or_404(
        Listing.objects.select_related("category", "lister", "lister__profile")
        .prefetch_related("photos", "reviews__reviewer", "reviews__reviewer__profile"),
        slug=slug,
    )
    
    # Check if listing is accessible
    if listing.status != Listing.STATUS_ACTIVE and listing.lister != request.user and not request.user.is_staff:
        return HttpResponseForbidden("This listing is not currently available.")

    # Track view
    if not request.session.get(f"viewed_{listing.pk}"):
        listing.register_view()
        request.session[f"viewed_{listing.pk}"] = True

    # Check if favorited
    is_favorited = request.user.is_authenticated and Favorite.objects.filter(user=request.user, listing=listing).exists()
    
    # Get dynamic fields
    dynamic_fields = listing.category.fields.all()
    
    # Get reviews
    reviews = listing.reviews.filter(is_approved=True).select_related("reviewer", "reviewer__profile")
    
    # Check if user has reviewed
    user_has_reviewed = request.user.is_authenticated and listing.reviews.filter(reviewer=request.user).exists()
    
    # Similar listings
    similar_listings = Listing.objects.filter(
        category=listing.category,
        status=Listing.STATUS_ACTIVE
    ).exclude(pk=listing.pk)[:6]

    context = {
        "listing": listing,
        "is_favorited": is_favorited,
        "dynamic_fields": dynamic_fields,
        "reviews": reviews,
        "user_has_reviewed": user_has_reviewed,
        "similar_listings": similar_listings,
    }
    return render(request, "core/listing_detail.html", context)


@require_POST
def register_contact_click(request, slug):
    listing = get_object_or_404(Listing, slug=slug)
    listing.register_contact_click()
    return JsonResponse({"ok": True})


# =========================================================================
# Auth
# =========================================================================

def signup(request):
    if request.user.is_authenticated:
        return redirect("core:home")
    
    if request.method == "POST":
        form = SignupForm(request.POST)
        if form.is_valid():
            user = form.save()
            login(request, user)
            messages.success(request, f"Welcome to Acco, {user.first_name or user.username}!")
            return redirect("core:home")
    else:
        form = SignupForm()
    
    # Get active categories for the intent dropdown
    categories = Category.objects.filter(is_active=True, children__isnull=True).distinct().order_by("name")
    
    return render(request, "core/auth/signup.html", {
        "form": form,
        "categories": categories,
    })


class AccoLoginView(LoginView):
    template_name = "core/auth/login.html"
    redirect_authenticated_user = True


def logout_view(request):
    logout(request)
    messages.info(request, "You have been logged out.")
    return redirect("core:home")


# =========================================================================
# Profile & KYC
# =========================================================================

@login_required
def profile_view(request, username=None):
    from django.contrib.auth.models import User
    from core.models import Favorite
    
    user = get_object_or_404(User, username=username) if username else request.user
    profile = user.profile
    
    # Get user's listings
    listings = Listing.objects.filter(lister=user).select_related("category")
    active_listings = listings.filter(status=Listing.STATUS_ACTIVE).count()
    
    # Get favorites count
    favorites_count = Favorite.objects.filter(user=user).count()
    
    # Get review stats
    avg_rating, review_count = profile.average_rating_as_lister
    
    # Get total views from listings
    total_views = sum(l.views_count for l in listings)
    
    # Get first listing for message button
    first_listing = listings.filter(status=Listing.STATUS_ACTIVE).first()
    
    context = {
        "profile_user": user,
        "listings": listings,
        "active_listings": active_listings,
        "favorites_count": favorites_count,
        "avg_rating": avg_rating,
        "review_count": review_count,
        "total_views": total_views,
        "first_listing": first_listing,
    }
    return render(request, "core/profile/profile_detail.html", context)


@login_required
def profile_edit(request):
    if request.method == "POST":
        form = ProfileForm(request.POST, request.FILES, instance=request.user.profile)
        if form.is_valid():
            form.save()
            messages.success(request, "Profile updated.")
            return redirect("core:profile_view")
    else:
        form = ProfileForm(instance=request.user.profile)
    return render(request, "core/profile/profile_edit.html", {"form": form})


@login_required
def become_lister(request):
    profile = request.user.profile
    if request.method == "POST":
        form = BecomeListerForm(request.POST, instance=profile)
        if form.is_valid():
            profile = form.save(commit=False)
            profile.is_lister = True
            profile.save()
            messages.success(request, "Great! Now let's verify your account.")
            return redirect("core:kyc_submit")
    else:
        form = BecomeListerForm(instance=profile)
    return render(request, "core/profile/become_lister.html", {"form": form})


@login_required
def kyc_submit(request):
    profile = request.user.profile
    if not profile.account_type:
        return redirect("core:become_lister")

    form_class = IndividualKYCForm if profile.account_type == Profile.INDIVIDUAL else BusinessKYCForm

    if request.method == "POST":
        form = form_class(request.POST, request.FILES, instance=profile)
        if form.is_valid():
            profile = form.save(commit=False)
            profile.verification_status = Profile.PENDING
            profile.save()
            messages.success(request, "Your documents were submitted. We'll review them shortly.")
            return redirect("core:dashboard")
    else:
        form = form_class(instance=profile)
    return render(request, "core/profile/kyc_submit.html", {"form": form, "account_type": profile.account_type})


# =========================================================================
# Lister dashboard & listing CRUD
# =========================================================================

@login_required
def dashboard(request):
    profile = request.user.profile
    
    # Get all listings with pagination
    my_listings = Listing.objects.filter(lister=request.user).select_related("category")
    
    # Apply filter if present
    filter_status = request.GET.get('filter')
    if filter_status and filter_status != 'all':
        my_listings = my_listings.filter(status=filter_status)
    
    # Paginate
    paginator = Paginator(my_listings, 10)
    page_number = request.GET.get('page')
    page_obj = paginator.get_page(page_number)
    
    # Stats
    all_listings = Listing.objects.filter(lister=request.user)
    stats = {
        "total": all_listings.count(),
        "active": all_listings.filter(status=Listing.STATUS_ACTIVE).count(),
        "pending": all_listings.filter(status=Listing.STATUS_PENDING).count(),
        "views": sum(l.views_count for l in all_listings),
        "contact_clicks": sum(l.contact_clicks_count for l in all_listings),
    }
    
    # Recent inquiries
    inquiries = InquiryRequest.objects.filter(
        listing__lister=request.user
    ).select_related("listing", "requester").order_by("-created_at")[:10]
    
    return render(request, "core/dashboard/dashboard.html", {
        "profile": profile, 
        "my_listings": page_obj, 
        "stats": stats, 
        "inquiries": inquiries,
    })


@login_required
def listing_create(request):
    profile = request.user.profile
    if not profile.is_lister:
        messages.warning(request, "You need to set up a lister account first.")
        return redirect("core:become_lister")

    category = None
    if request.method == "GET" and request.GET.get("category"):
        category = Category.objects.filter(pk=request.GET.get("category")).first()

    if request.method == "POST":
        form = ListingForm(request.POST, category=category)
        if form.is_valid():
            listing = form.save(commit=False)
            listing.lister = request.user
            listing.status = Listing.STATUS_PENDING
            listing.save()

            # Handle photos
            photos = request.FILES.getlist("photos")
            for i, image in enumerate(photos[: settings.ACCO_MAX_LISTING_PHOTOS]):
                ListingPhoto.objects.create(listing=listing, image=image, is_cover=(i == 0))

            # Check for duplicates
            scan_for_duplicates(listing)

            if listing.photos.count() < settings.ACCO_MIN_LISTING_PHOTOS:
                messages.warning(
                    request,
                    f"Listing created, but we recommend at least {settings.ACCO_MIN_LISTING_PHOTOS} photos "
                    "for better visibility. You can add more from 'My Listings'.",
                )
            messages.success(request, "Listing submitted for review!")
            return redirect("core:listing_detail", slug=listing.slug)
    else:
        form = ListingForm(category=category)

    categories = Category.objects.filter(is_active=True, children__isnull=True).distinct()
    
    return render(request, "core/listings/listing_form.html", {
        "form": form, 
        "categories": categories, 
        "is_create": True,
        "min_photos": settings.ACCO_MIN_LISTING_PHOTOS, 
        "max_photos": settings.ACCO_MAX_LISTING_PHOTOS,
        "ACCO_DEFAULT_CURRENCY": getattr(settings, "ACCO_DEFAULT_CURRENCY", "MWK"),
    })


@login_required
def listing_edit(request, slug):
    listing = get_object_or_404(Listing, slug=slug)
    if listing.lister != request.user and not request.user.is_staff:
        return HttpResponseForbidden("You cannot edit this listing.")

    if request.method == "POST":
        form = ListingForm(request.POST, request.FILES, instance=listing, category=listing.category)
        if form.is_valid():
            listing = form.save()
            new_photos = request.FILES.getlist("photos")
            for image in new_photos[: settings.ACCO_MAX_LISTING_PHOTOS]:
                ListingPhoto.objects.create(listing=listing, image=image)
            messages.success(request, "Listing updated.")
            return redirect("core:listing_detail", slug=listing.slug)
    else:
        form = ListingForm(instance=listing, category=listing.category)

    return render(request, "core/listings/listing_form.html", {
        "form": form, "categories": Category.objects.filter(is_active=True), "is_create": False,
        "listing": listing, "min_photos": settings.ACCO_MIN_LISTING_PHOTOS, "max_photos": settings.ACCO_MAX_LISTING_PHOTOS,
    })


@login_required
@require_POST
def listing_delete(request, slug):
    listing = get_object_or_404(Listing, slug=slug)
    if listing.lister != request.user and not request.user.is_staff:
        return HttpResponseForbidden()
    listing.delete()
    messages.info(request, "Listing removed.")
    return redirect("core:dashboard")


@login_required
@require_POST
def listing_photo_delete(request, photo_id):
    photo = get_object_or_404(ListingPhoto, pk=photo_id)
    if photo.listing.lister != request.user and not request.user.is_staff:
        return HttpResponseForbidden()
    photo.delete()
    return JsonResponse({"ok": True})


@login_required
@require_POST
def listing_status_update(request, slug):
    listing = get_object_or_404(Listing, slug=slug)
    if listing.lister != request.user and not request.user.is_staff:
        return HttpResponseForbidden()
    new_status = request.POST.get("status")
    valid_statuses = dict(Listing.STATUS_CHOICES)
    if new_status in valid_statuses:
        listing.status = new_status
        listing.save()
        messages.success(request, f"Listing marked as {valid_statuses[new_status]}.")
    return redirect("core:dashboard")


def category_fields_json(request, category_id):
    """Powers the dynamic-form JS: returns the field schema for a category as JSON."""
    category = get_object_or_404(Category, pk=category_id)
    fields = [
        {
            "name": f.name, "label": f.label, "field_type": f.field_type,
            "choices": f.choice_list(), "unit": f.unit, "required": f.required,
            "help_text": f.help_text,
        }
        for f in category.fields.all()
    ]
    return JsonResponse({"fields": fields})


# =========================================================================
# Favorites & saved searches
# =========================================================================

@login_required
@require_POST
def toggle_favorite(request, slug):
    listing = get_object_or_404(Listing, slug=slug)
    favorite, created = Favorite.objects.get_or_create(user=request.user, listing=listing)
    if not created:
        favorite.delete()
        return JsonResponse({"favorited": False})
    return JsonResponse({"favorited": True})


@login_required
def favorites_list(request):
    favorites = Favorite.objects.filter(user=request.user).select_related("listing", "listing__category")
    return render(request, "core/discoverer/favorites.html", {"favorites": favorites})


@login_required
def saved_search_list(request):
    searches = SavedSearch.objects.filter(user=request.user)
    if request.method == "POST":
        form = SavedSearchForm(request.POST)
        if form.is_valid():
            search = form.save(commit=False)
            search.user = request.user
            search.save()
            messages.success(request, "Saved search created — we'll notify you of new matches.")
            return redirect("core:saved_search_list")
    else:
        form = SavedSearchForm()
    return render(request, "core/discoverer/saved_searches.html", {"searches": searches, "form": form})


@login_required
@require_POST
def saved_search_delete(request, pk):
    search = get_object_or_404(SavedSearch, pk=pk, user=request.user)
    search.delete()
    return redirect("core:saved_search_list")


# =========================================================================
# Reviews
# =========================================================================

@login_required
@require_POST
def review_create(request, slug):
    listing = get_object_or_404(Listing, slug=slug)
    if Review.objects.filter(listing=listing, reviewer=request.user).exists():
        messages.warning(request, "You've already reviewed this listing.")
        return redirect("core:listing_detail", slug=slug)

    form = ReviewForm(request.POST)
    if form.is_valid():
        review = form.save(commit=False)
        review.listing = listing
        review.reviewer = request.user
        review.save()
        messages.success(request, "Thanks for your review!")
    else:
        messages.error(request, "Please provide a valid rating.")
    return redirect("core:listing_detail", slug=slug)


@login_required
@require_POST
def review_flag(request, pk):
    review = get_object_or_404(Review, pk=pk)
    review.is_flagged = True
    review.flag_reason = request.POST.get("reason", "")
    review.save()
    messages.info(request, "Review reported to moderators.")
    return redirect("core:listing_detail", slug=review.listing.slug)


@login_required
@require_POST
def review_reply(request, pk):
    review = get_object_or_404(Review, pk=pk)
    if review.listing.lister != request.user:
        return HttpResponseForbidden()
    form = ReviewReplyForm(request.POST, instance=review)
    if form.is_valid():
        form.save()
    return redirect("core:listing_detail", slug=review.listing.slug)


# =========================================================================
# Inquiries
# =========================================================================

@login_required
@require_POST
def inquiry_create(request, slug):
    listing = get_object_or_404(Listing, slug=slug)
    form = InquiryForm(request.POST)
    if form.is_valid():
        inquiry = form.save(commit=False)
        inquiry.listing = listing
        inquiry.requester = request.user
        inquiry.save()
        messages.success(request, "Your inquiry was sent to the lister.")
    return redirect("core:listing_detail", slug=slug)


# =========================================================================
# Messaging
# =========================================================================

@login_required
def conversation_list(request):
    # Get all conversations for the user
    conversations = Conversation.objects.filter(participants=request.user).prefetch_related(
        "messages", "participants", "participants__profile", "listing"
    )
    
    # Search filter
    q = request.GET.get('q')
    if q:
        # Filter by participant name or listing title
        from django.db.models import Q
        # This is a simplified search - you may need to implement more complex search
        conversations = conversations.filter(
            Q(participants__username__icontains=q) |
            Q(participants__first_name__icontains=q) |
            Q(participants__last_name__icontains=q) |
            Q(listing__title__icontains=q) |
            Q(messages__body__icontains=q)
        ).distinct()
    
    # Filter by unread or properties
    filter_type = request.GET.get('filter')
    if filter_type == 'unread':
        # Filter conversations where the user has unread messages
        # This is a simplified filter - might need optimization for production
        conversation_ids = []
        for conv in conversations:
            if conv.unread_count_for(request.user) > 0:
                conversation_ids.append(conv.id)
        conversations = conversations.filter(id__in=conversation_ids)
    
    # Order by latest message
    conversations = conversations.order_by("-updated_at")
    
    # Paginate
    paginator = Paginator(conversations, 20)
    page_number = request.GET.get('page')
    page_obj = paginator.get_page(page_number)
    
    return render(request, "core/messaging/conversation_list.html", {
        "conversations": page_obj,
    })


@login_required
def conversation_detail(request, pk):
    conversation = get_object_or_404(Conversation, pk=pk, participants=request.user)
    if request.method == "POST":
        form = MessageForm(request.POST)
        if form.is_valid():
            msg = form.save(commit=False)
            msg.conversation = conversation
            msg.sender = request.user
            msg.save()
            conversation.save()  # bump updated_at
            return redirect("core:conversation_detail", pk=pk)
    else:
        form = MessageForm()

    conversation.messages.exclude(sender=request.user).update(is_read=True)
    return render(request, "core/messaging/conversation_detail.html", {
        "conversation": conversation, "form": form,
    })


@login_required
def conversation_start(request, slug):
    listing = get_object_or_404(Listing, slug=slug)
    if listing.lister == request.user:
        messages.warning(request, "You can't message yourself about your own listing.")
        return redirect("core:listing_detail", slug=slug)

    conversation = Conversation.objects.filter(listing=listing, participants=request.user).filter(
        participants=listing.lister
    ).first()
    if not conversation:
        conversation = Conversation.objects.create(listing=listing)
        conversation.participants.add(request.user, listing.lister)

    initial_message = request.POST.get("body", "").strip()
    if initial_message:
        Message.objects.create(conversation=conversation, sender=request.user, body=initial_message)

    return redirect("core:conversation_detail", pk=conversation.pk)


# =========================================================================
# Notifications
# =========================================================================

@login_required
def notification_list(request):
    notifications = Notification.objects.filter(user=request.user)
    notifications.filter(is_read=False).update(is_read=True)
    return render(request, "core/notifications/notification_list.html", {"notifications": notifications})


# =========================================================================
# Trust & safety
# =========================================================================

@login_required
def report_create(request, slug=None, username=None, event_slug=None):
    listing = get_object_or_404(Listing, slug=slug) if slug else None
    event = get_object_or_404(Event, slug=event_slug) if event_slug else None
    from django.contrib.auth.models import User
    reported_user = get_object_or_404(User, username=username) if username else None

    if request.method == "POST":
        form = ReportForm(request.POST)
        if form.is_valid():
            report = form.save(commit=False)
            report.reporter = request.user
            report.listing = listing
            report.event = event
            report.reported_user = reported_user
            report.save()
            messages.success(request, "Thanks — our moderation team will look into this.")
            if event:
                return redirect(event.get_absolute_url())
            return redirect(listing.get_absolute_url() if listing else "core:home")
    else:
        form = ReportForm()

    return render(request, "core/trust/report_form.html", {
        "form": form, "listing": listing, "event": event, "reported_user": reported_user,
    })


# =========================================================================
# Simple category picker (first-run intent selection)
# =========================================================================

@login_required
@require_POST
def set_intent(request):
    category_id = request.POST.get("category_id")
    profile = request.user.profile
    profile.preferred_intent = Category.objects.filter(pk=category_id).first()
    profile.save()
    return redirect("core:home")


# =========================================================================
# Events — dedicated content type with its own discovery page, CRUD,
# ticket types, bookings, and favorites. Deliberately kept separate from
# Listing / listing_search so events never get mixed in with properties
# and places.
# =========================================================================

def event_list(request):
    """The dedicated Events discovery page."""
    form = EventSearchForm(request.GET or None)
    now = timezone.now()
    qs = Event.objects.filter(status=Event.STATUS_PUBLISHED).select_related(
        "event_type", "organizer", "organizer__profile"
    ).prefetch_related("photos", "ticket_types")

    when = "upcoming"
    lat = lng = radius = None

    if form.is_valid():
        data = form.cleaned_data
        when = data.get("when") or "upcoming"
        lat, lng, radius = data.get("lat"), data.get("lng"), data.get("radius_km")

        if data.get("q"):
            qs = qs.filter(
                Q(title__icontains=data["q"]) | Q(description__icontains=data["q"]) |
                Q(address__icontains=data["q"]) | Q(venue_name__icontains=data["q"])
            )
        if data.get("event_type"):
            qs = qs.filter(event_type=data["event_type"])
        if data.get("price") == "free":
            qs = qs.filter(is_free=True)
        elif data.get("price") == "paid":
            qs = qs.filter(is_free=False)

    if when == "past":
        qs = qs.filter(end_datetime__lt=now).order_by("-start_datetime")
    else:
        qs = qs.filter(end_datetime__gte=now).order_by("start_datetime")
        if when == "today":
            qs = qs.filter(start_datetime__date=now.date())
        elif when == "week":
            qs = qs.filter(start_datetime__lte=now + timedelta(days=7))
        elif when == "month":
            qs = qs.filter(start_datetime__lte=now + timedelta(days=30))

    events = list(qs[:300])  # cap for the in-memory distance pass

    if lat and lng:
        radius = radius or 25
        filtered = []
        for event in events:
            d = haversine_km(lat, lng, event.latitude, event.longitude)
            if d is not None and d <= radius:
                event.distance_km = round(d, 1)
                filtered.append(event)
        events = filtered

    paginator = Paginator(events, getattr(settings, "ACCO_EVENTS_PER_PAGE", 12))
    page_obj = paginator.get_page(request.GET.get("page"))

    favorited_ids = []
    if request.user.is_authenticated:
        favorited_ids = list(EventFavorite.objects.filter(
            user=request.user, event__in=[e.id for e in page_obj.object_list]
        ).values_list("event_id", flat=True))

    return render(request, "core/events/event_list.html", {
        "form": form,
        "page_obj": page_obj,
        "when": when,
        "favorited_ids": favorited_ids,
        "event_types": EventCategory.objects.filter(is_active=True),
    })


def event_detail(request, slug):
    event = get_object_or_404(
        Event.objects.select_related("event_type", "organizer", "organizer__profile")
        .prefetch_related("photos", "ticket_types"),
        slug=slug,
    )
    if event.status != Event.STATUS_PUBLISHED and event.organizer != request.user and not request.user.is_staff:
        return HttpResponseForbidden("This event is not currently available.")

    if not request.session.get(f"viewed_event_{event.pk}"):
        event.register_view()
        request.session[f"viewed_event_{event.pk}"] = True

    is_favorited = request.user.is_authenticated and EventFavorite.objects.filter(
        user=request.user, event=event
    ).exists()

    user_orders = []
    if request.user.is_authenticated:
        user_orders = event.ticket_orders.filter(
            buyer=request.user, status=EventTicketOrder.STATUS_CONFIRMED
        ).select_related("ticket_type")

    return render(request, "core/events/event_detail.html", {
        "event": event,
        "is_favorited": is_favorited,
        "ticket_types": event.ticket_types.all(),
        "user_orders": user_orders,
        "booking_form": EventTicketBookingForm(event=event),
    })


@login_required
def event_create(request):
    profile = request.user.profile
    if not profile.is_lister:
        messages.warning(request, "You need to set up a lister account first to create events.")
        return redirect("core:become_lister")

    if request.method == "POST":
        form = EventForm(request.POST)
        unsaved_event = Event(organizer=request.user)
        formset = EventTicketTypeFormSet(request.POST, instance=unsaved_event, prefix="tickets")
        if form.is_valid() and formset.is_valid():
            event = form.save(commit=False)
            event.organizer = request.user
            event.status = Event.STATUS_PENDING
            event.save()

            formset.instance = event
            formset.save()
            event.refresh_price_summary()

            photos = request.FILES.getlist("photos")
            for i, image in enumerate(photos[: settings.ACCO_MAX_EVENT_PHOTOS]):
                EventPhoto.objects.create(event=event, image=image, is_cover=(i == 0))

            messages.success(request, "Event submitted for review!")
            return redirect("core:event_detail", slug=event.slug)
    else:
        form = EventForm()
        formset = EventTicketTypeFormSet(instance=Event(), prefix="tickets")

    return render(request, "core/events/event_form.html", {
        "form": form, "formset": formset, "is_create": True,
        "min_photos": settings.ACCO_MIN_EVENT_PHOTOS, "max_photos": settings.ACCO_MAX_EVENT_PHOTOS,
    })


@login_required
def event_edit(request, slug):
    event = get_object_or_404(Event, slug=slug)
    if event.organizer != request.user and not request.user.is_staff:
        return HttpResponseForbidden("You cannot edit this event.")

    if request.method == "POST":
        form = EventForm(request.POST, instance=event)
        formset = EventTicketTypeFormSet(request.POST, instance=event, prefix="tickets")
        if form.is_valid() and formset.is_valid():
            event = form.save()
            formset.save()
            event.refresh_price_summary()

            new_photos = request.FILES.getlist("photos")
            for image in new_photos[: settings.ACCO_MAX_EVENT_PHOTOS]:
                EventPhoto.objects.create(event=event, image=image)

            messages.success(request, "Event updated.")
            return redirect("core:event_detail", slug=event.slug)
    else:
        form = EventForm(instance=event)
        formset = EventTicketTypeFormSet(instance=event, prefix="tickets")

    return render(request, "core/events/event_form.html", {
        "form": form, "formset": formset, "is_create": False, "event": event,
        "min_photos": settings.ACCO_MIN_EVENT_PHOTOS, "max_photos": settings.ACCO_MAX_EVENT_PHOTOS,
    })


@login_required
@require_POST
def event_delete(request, slug):
    event = get_object_or_404(Event, slug=slug)
    if event.organizer != request.user and not request.user.is_staff:
        return HttpResponseForbidden()
    event.delete()
    messages.info(request, "Event removed.")
    return redirect("core:my_events")


@login_required
@require_POST
def event_photo_delete(request, photo_id):
    photo = get_object_or_404(EventPhoto, pk=photo_id)
    if photo.event.organizer != request.user and not request.user.is_staff:
        return HttpResponseForbidden()
    photo.delete()
    return JsonResponse({"ok": True})


@login_required
@require_POST
def event_status_update(request, slug):
    event = get_object_or_404(Event, slug=slug)
    if event.organizer != request.user and not request.user.is_staff:
        return HttpResponseForbidden()
    new_status = request.POST.get("status")
    valid_statuses = dict(Event.STATUS_CHOICES)
    if new_status in valid_statuses:
        event.status = new_status
        event.save()
        messages.success(request, f"Event marked as {valid_statuses[new_status]}.")
    return redirect("core:my_events")


@login_required
@require_POST
def toggle_event_favorite(request, slug):
    event = get_object_or_404(Event, slug=slug)
    favorite, created = EventFavorite.objects.get_or_create(user=request.user, event=event)
    if not created:
        favorite.delete()
        return JsonResponse({"favorited": False})
    return JsonResponse({"favorited": True})


@login_required
def event_favorites_list(request):
    favorites = EventFavorite.objects.filter(user=request.user).select_related("event", "event__event_type")
    return render(request, "core/events/event_favorites.html", {"favorites": favorites})


@login_required
def my_events(request):
    """Organizer dashboard for events — separate from the listings dashboard."""
    profile = request.user.profile
    events = Event.objects.filter(organizer=request.user).select_related("event_type").prefetch_related(
        "ticket_types", "photos"
    )
    tickets_sold = EventTicketOrder.objects.filter(
        event__organizer=request.user, status=EventTicketOrder.STATUS_CONFIRMED
    ).aggregate(total=Sum("quantity"))["total"] or 0

    stats = {
        "total": events.count(),
        "published": events.filter(status=Event.STATUS_PUBLISHED).count(),
        "views": sum(e.views_count for e in events),
        "tickets_sold": tickets_sold,
    }
    return render(request, "core/events/my_events.html", {
        "profile": profile, "my_events": events, "stats": stats,
    })


@login_required
def my_tickets(request):
    """A discoverer's booked event tickets."""
    orders = EventTicketOrder.objects.filter(buyer=request.user).select_related(
        "event", "ticket_type"
    ).order_by("-created_at")
    return render(request, "core/events/my_tickets.html", {"orders": orders})


@login_required
@require_POST
def event_book_tickets(request, slug):
    event = get_object_or_404(Event, slug=slug)
    if event.status != Event.STATUS_PUBLISHED:
        messages.error(request, "This event isn't open for bookings right now.")
        return redirect("core:event_detail", slug=slug)

    form = EventTicketBookingForm(request.POST, event=event)
    if form.is_valid():
        ticket_type = form.cleaned_data["ticket_type"]
        quantity = form.cleaned_data["quantity"]
        order = EventTicketOrder.objects.create(
            event=event,
            ticket_type=ticket_type,
            buyer=request.user,
            quantity=quantity,
            attendee_name=form.cleaned_data.get("attendee_name") or request.user.get_full_name() or request.user.username,
            attendee_phone=form.cleaned_data.get("attendee_phone") or request.user.profile.phone,
        )
        ticket_type.mark_sold(quantity)
        messages.success(request, f"You're booked! Reference code: {order.reference_code}")
    else:
        for field, errors in form.errors.items():
            for error in errors:
                messages.error(request, error)
    return redirect("core:event_detail", slug=slug)


@login_required
@require_POST
def event_ticket_cancel(request, order_id):
    order = get_object_or_404(EventTicketOrder, pk=order_id, buyer=request.user)
    if order.status != EventTicketOrder.STATUS_CANCELLED:
        order.status = EventTicketOrder.STATUS_CANCELLED
        order.save()
        order.ticket_type.unmark_sold(order.quantity)
        messages.info(request, "Booking cancelled.")
    return redirect("core:my_tickets")