from django import forms
from django.conf import settings
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.forms import inlineformset_factory

from core.models import (
    Category, CategoryField, Listing, ListingPhoto, Profile,
    Review, InquiryRequest, SavedSearch, Report, Message,
    EventCategory, Event, EventPhoto, EventTicketType,
)


BOOTSTRAP_TEXT = "form-control"
BOOTSTRAP_SELECT = "form-select"
BOOTSTRAP_CHECK = "form-check-input"


# =========================================================================
# Auth / onboarding
# =========================================================================

class SignupForm(UserCreationForm):
    first_name = forms.CharField(max_length=30, required=True, widget=forms.TextInput(attrs={"class": BOOTSTRAP_TEXT}))
    last_name = forms.CharField(max_length=30, required=False, widget=forms.TextInput(attrs={"class": BOOTSTRAP_TEXT}))
    email = forms.EmailField(required=True, widget=forms.EmailInput(attrs={"class": BOOTSTRAP_TEXT}))
    phone = forms.CharField(max_length=20, required=True, widget=forms.TextInput(attrs={"class": BOOTSTRAP_TEXT}))

    intent = forms.ModelChoiceField(
        queryset=Category.objects.filter(is_active=True),
        required=False,
        empty_label="I'm just browsing for now",
        label="What are you discovering today?",
        widget=forms.Select(attrs={"class": BOOTSTRAP_SELECT}),
    )

    class Meta:
        model = User
        fields = ["first_name", "last_name", "username", "email", "phone", "password1", "password2"]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields["username"].widget.attrs.update({"class": BOOTSTRAP_TEXT})
        self.fields["password1"].widget.attrs.update({"class": BOOTSTRAP_TEXT})
        self.fields["password2"].widget.attrs.update({"class": BOOTSTRAP_TEXT})

    def clean_email(self):
        email = self.cleaned_data["email"]
        if User.objects.filter(email__iexact=email).exists():
            raise ValidationError("An account with this email already exists.")
        return email

    def save(self, commit=True):
        user = super().save(commit=False)
        user.email = self.cleaned_data["email"]
        user.first_name = self.cleaned_data["first_name"]
        user.last_name = self.cleaned_data.get("last_name", "")
        if commit:
            user.save()
            profile = user.profile
            profile.phone = self.cleaned_data["phone"]
            profile.preferred_intent = self.cleaned_data.get("intent")
            profile.save()
        return user


class LoginForm(forms.Form):
    username = forms.CharField(widget=forms.TextInput(attrs={"class": BOOTSTRAP_TEXT, "autofocus": True}))
    password = forms.CharField(widget=forms.PasswordInput(attrs={"class": BOOTSTRAP_TEXT}))


# =========================================================================
# Profile / KYC
# =========================================================================

class ProfileForm(forms.ModelForm):
    class Meta:
        model = Profile
        fields = ["phone", "whatsapp", "avatar", "bio", "preferred_language", "preferred_currency"]
        widgets = {
            "phone": forms.TextInput(attrs={"class": BOOTSTRAP_TEXT}),
            "whatsapp": forms.TextInput(attrs={"class": BOOTSTRAP_TEXT}),
            "avatar": forms.ClearableFileInput(attrs={"class": "form-control"}),
            "bio": forms.Textarea(attrs={"class": BOOTSTRAP_TEXT, "rows": 3, "maxlength": 500}),
            "preferred_language": forms.Select(attrs={"class": BOOTSTRAP_SELECT}),
            "preferred_currency": forms.Select(attrs={"class": BOOTSTRAP_SELECT}),
        }


class BecomeListerForm(forms.ModelForm):
    """Step 1 of becoming a lister: choose Individual vs Business."""
    class Meta:
        model = Profile
        fields = ["account_type"]
        widgets = {"account_type": forms.RadioSelect}


class IndividualKYCForm(forms.ModelForm):
    class Meta:
        model = Profile
        fields = ["id_number", "id_photo"]
        widgets = {
            "id_number": forms.TextInput(attrs={"class": BOOTSTRAP_TEXT, "placeholder": "National ID number"}),
            "id_photo": forms.ClearableFileInput(attrs={"class": "form-control"}),
        }

    def clean(self):
        cleaned = super().clean()
        if not cleaned.get("id_number") or not cleaned.get("id_photo"):
            raise ValidationError("Both ID number and a photo of your ID are required for verification.")
        return cleaned


class BusinessKYCForm(forms.ModelForm):
    class Meta:
        model = Profile
        fields = [
            "business_name", "business_registration_number", "business_address",
            "representative_id_photo", "proof_of_ownership",
        ]
        widgets = {
            "business_name": forms.TextInput(attrs={"class": BOOTSTRAP_TEXT}),
            "business_registration_number": forms.TextInput(attrs={"class": BOOTSTRAP_TEXT}),
            "business_address": forms.TextInput(attrs={"class": BOOTSTRAP_TEXT}),
            "representative_id_photo": forms.ClearableFileInput(attrs={"class": "form-control"}),
            "proof_of_ownership": forms.ClearableFileInput(attrs={"class": "form-control"}),
        }


# =========================================================================
# Listings — schema-driven dynamic form
# =========================================================================

class MultipleFileField(forms.FileField):
    def clean(self, data, initial=None):
        if not data:
            if self.required:
                raise ValidationError("No file was submitted.")
            return []

        if isinstance(data, (list, tuple)):
            cleaned_files = []
            for item in data:
                if item in [None, ""]:
                    continue
                cleaned_files.append(super().clean(item, initial))
            return cleaned_files

        return super().clean(data, initial)


class ListingForm(forms.ModelForm):
    """
    Core listing fields. Category-specific fields are injected dynamically
    in __init__ based on the CategoryField rows for the chosen category —
    this is what makes the form adapt without any code changes when new
    categories/fields are added via the admin.
    """

    class Meta:
        model = Listing
        fields = [
            "category", "title", "description", "purpose",
            "address", "latitude", "longitude",
            "price", "price_period", "currency", "application_fee",
            "contact_phone", "contact_email", "contact_whatsapp",
        ]
        widgets = {
            "category": forms.Select(attrs={"class": BOOTSTRAP_SELECT, "id": "id_category"}),
            "title": forms.TextInput(attrs={"class": BOOTSTRAP_TEXT, "maxlength": 150}),
            "description": forms.Textarea(attrs={"class": BOOTSTRAP_TEXT, "rows": 5}),
            "purpose": forms.Select(attrs={"class": BOOTSTRAP_SELECT}),
            "address": forms.TextInput(attrs={"class": BOOTSTRAP_TEXT, "placeholder": "Street / area, city"}),
            "latitude": forms.HiddenInput(attrs={"id": "id_latitude"}),
            "longitude": forms.HiddenInput(attrs={"id": "id_longitude"}),
            "price": forms.NumberInput(attrs={"class": BOOTSTRAP_TEXT, "step": "0.01", "min": "0"}),
            "price_period": forms.Select(attrs={"class": BOOTSTRAP_SELECT}),
            "currency": forms.Select(attrs={"class": BOOTSTRAP_SELECT}),
            "application_fee": forms.NumberInput(attrs={"class": BOOTSTRAP_TEXT, "step": "0.01", "min": "0"}),
            "contact_phone": forms.TextInput(attrs={"class": BOOTSTRAP_TEXT}),
            "contact_email": forms.EmailInput(attrs={"class": BOOTSTRAP_TEXT}),
            "contact_whatsapp": forms.TextInput(attrs={"class": BOOTSTRAP_TEXT}),
        }

    def __init__(self, *args, category=None, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields["category"].queryset = Category.objects.filter(is_active=True, children__isnull=True).distinct()
        self.fields["category"].empty_label = "Select a category…"

        # Add photos field for multiple file uploads
        self.fields["photos"] = MultipleFileField(
            widget=MultipleFileInput(attrs={
                "multiple": True,
                "accept": "image/*",
                "class": "form-control",
                "id": "id_photos",
            }),
            required=False,
            label="Photos",
            help_text=f"Upload {settings.ACCO_MIN_LISTING_PHOTOS}–{settings.ACCO_MAX_LISTING_PHOTOS} photos. JPG or PNG.",
        )

        # Determine which category's dynamic fields to render:
        # explicit `category` kwarg > bound data > instance's current category
        resolved_category = category
        if resolved_category is None and self.data.get("category"):
            resolved_category = Category.objects.filter(pk=self.data.get("category")).first()
        if resolved_category is None and self.instance and self.instance.pk:
            resolved_category = self.instance.category

        self.dynamic_field_defs = []
        if resolved_category:
            self.dynamic_field_defs = list(resolved_category.fields.all())
            existing_values = self.instance.attributes if (self.instance and self.instance.pk) else {}

            for field_def in self.dynamic_field_defs:
                field_name = f"dyn__{field_def.name}"
                initial = existing_values.get(field_def.name)
                self.fields[field_name] = self._build_dynamic_field(field_def, initial)

    @staticmethod
    def _build_dynamic_field(field_def, initial):
        common = {"required": field_def.required, "label": field_def.label, "help_text": field_def.help_text}

        if field_def.field_type == CategoryField.NUMBER:
            f = forms.IntegerField(widget=forms.NumberInput(attrs={"class": BOOTSTRAP_TEXT}), **common)
        elif field_def.field_type == CategoryField.DECIMAL:
            f = forms.DecimalField(widget=forms.NumberInput(attrs={"class": BOOTSTRAP_TEXT, "step": "0.01"}), **common)
        elif field_def.field_type == CategoryField.BOOLEAN:
            f = forms.BooleanField(widget=forms.CheckboxInput(attrs={"class": BOOTSTRAP_CHECK}), **{**common, "required": False})
        elif field_def.field_type == CategoryField.CHOICE:
            choices = [("", "---------")] + [(c, c) for c in field_def.choice_list()]
            f = forms.ChoiceField(choices=choices, widget=forms.Select(attrs={"class": BOOTSTRAP_SELECT}), **common)
        elif field_def.field_type == CategoryField.MULTICHOICE:
            choices = [(c, c) for c in field_def.choice_list()]
            f = forms.MultipleChoiceField(
                choices=choices, widget=forms.CheckboxSelectMultiple(attrs={"class": BOOTSTRAP_CHECK}), **common,
            )
        elif field_def.field_type == CategoryField.DATE:
            f = forms.DateField(widget=forms.DateInput(attrs={"class": BOOTSTRAP_TEXT, "type": "date"}), **common)
        elif field_def.field_type == CategoryField.TIME:
            f = forms.TimeField(widget=forms.TimeInput(attrs={"class": BOOTSTRAP_TEXT, "type": "time"}), **common)
        elif field_def.field_type == CategoryField.DATETIME:
            f = forms.DateTimeField(widget=forms.DateTimeInput(attrs={"class": BOOTSTRAP_TEXT, "type": "datetime-local"}), **common)
        elif field_def.field_type == CategoryField.URL:
            f = forms.URLField(widget=forms.URLInput(attrs={"class": BOOTSTRAP_TEXT}), **common)
        elif field_def.field_type == CategoryField.TEXTAREA:
            f = forms.CharField(widget=forms.Textarea(attrs={"class": BOOTSTRAP_TEXT, "rows": 3}), **common)
        else:
            f = forms.CharField(widget=forms.TextInput(attrs={"class": BOOTSTRAP_TEXT}), **common)

        if initial is not None:
            f.initial = initial
        return f

    def _get_uploaded_photos(self):
        if not self.files:
            return []
        if hasattr(self.files, "getlist"):
            return self.files.getlist("photos")
        uploaded = self.files.get("photos")
        if isinstance(uploaded, (list, tuple)):
            return [file for file in uploaded if file not in [None, ""]]
        return [uploaded] if uploaded not in [None, ""] else []

    def clean(self):
        cleaned = super().clean()
        if cleaned.get("purpose") in (Listing.PURPOSE_SALE, Listing.PURPOSE_RENT) and not cleaned.get("price"):
            self.add_error("price", "Price is required when the listing is for sale or rent.")

        # Validate photos
        photos = self._get_uploaded_photos()
        existing_photos = self.instance.photos.count() if self.instance and self.instance.pk else 0
        total_photos = len(photos) + existing_photos
        
        # For new listings (create), we need at least ACCO_MIN_LISTING_PHOTOS
        if not self.instance or not self.instance.pk:
            if len(photos) < settings.ACCO_MIN_LISTING_PHOTOS:
                self.add_error(
                    "photos",
                    f"Please upload at least {settings.ACCO_MIN_LISTING_PHOTOS} photos."
                )
        
        # Check max limit
        if total_photos > settings.ACCO_MAX_LISTING_PHOTOS:
            self.add_error(
                "photos",
                f"You can upload a maximum of {settings.ACCO_MAX_LISTING_PHOTOS} photos."
            )
        
        return cleaned

    def collect_attributes(self):
        """Pull the dynamically-added dyn__* fields into a plain dict for Listing.attributes."""
        attributes = {}
        for field_def in self.dynamic_field_defs:
            key = f"dyn__{field_def.name}"
            if key in self.cleaned_data:
                value = self.cleaned_data[key]
                attributes[field_def.name] = value
        return attributes

    def save(self, commit=True):
        instance = super().save(commit=False)
        instance.attributes = self.collect_attributes()
        if commit:
            instance.save()
        return instance


class ListingPhotoForm(forms.ModelForm):
    class Meta:
        model = ListingPhoto
        fields = ["image", "caption", "is_cover"]
        widgets = {
            "image": forms.ClearableFileInput(attrs={"class": "form-control"}),
            "caption": forms.TextInput(attrs={"class": BOOTSTRAP_TEXT}),
            "is_cover": forms.CheckboxInput(attrs={"class": BOOTSTRAP_CHECK}),
        }


ListingPhotoFormSet = inlineformset_factory(
    Listing, ListingPhoto, form=ListingPhotoForm, extra=6, can_delete=True, max_num=15, validate_max=True,
)


class MultipleFileInput(forms.ClearableFileInput):
    allow_multiple_selected = True


class MultiPhotoUploadForm(forms.Form):
    """Simple multi-file uploader used alongside the formset for convenience."""
    images = forms.FileField(
        widget=MultipleFileInput(attrs={"multiple": True, "class": "form-control"}),
        required=False,
        help_text="Upload 4–15 photos. JPG or PNG.",
    )


# =========================================================================
# Search / filters
# =========================================================================

class ListingSearchForm(forms.Form):
    q = forms.CharField(required=False, label="Keyword",
                         widget=forms.TextInput(attrs={"class": BOOTSTRAP_TEXT, "placeholder": "Search listings…"}))
    category = forms.ModelChoiceField(
        queryset=Category.objects.filter(is_active=True), required=False,
        widget=forms.Select(attrs={"class": BOOTSTRAP_SELECT}),
    )
    min_price = forms.DecimalField(required=False, widget=forms.NumberInput(attrs={"class": BOOTSTRAP_TEXT}))
    max_price = forms.DecimalField(required=False, widget=forms.NumberInput(attrs={"class": BOOTSTRAP_TEXT}))
    purpose = forms.ChoiceField(
        required=False,
        choices=[("", "Any")] + Listing.PURPOSE_CHOICES,
        widget=forms.Select(attrs={"class": BOOTSTRAP_SELECT}),
    )
    verified_only = forms.BooleanField(required=False, widget=forms.CheckboxInput(attrs={"class": BOOTSTRAP_CHECK}))
    lat = forms.DecimalField(required=False, widget=forms.HiddenInput())
    lng = forms.DecimalField(required=False, widget=forms.HiddenInput())
    radius_km = forms.IntegerField(required=False, initial=10, widget=forms.NumberInput(attrs={"class": BOOTSTRAP_TEXT}))
    sort = forms.ChoiceField(
        required=False,
        choices=[
            ("-created_at", "Newest first"),
            ("price", "Price: Low to High"),
            ("-price", "Price: High to Low"),
            ("-views_count", "Most popular"),
        ],
        widget=forms.Select(attrs={"class": BOOTSTRAP_SELECT}),
    )


# =========================================================================
# Reviews, inquiries, messaging, saved searches, reports
# =========================================================================

class ReviewForm(forms.ModelForm):
    class Meta:
        model = Review
        fields = ["rating", "comment"]
        widgets = {
            "rating": forms.Select(choices=[(i, f"{i} ★") for i in range(1, 6)], attrs={"class": BOOTSTRAP_SELECT}),
            "comment": forms.Textarea(attrs={"class": BOOTSTRAP_TEXT, "rows": 3, "maxlength": 1500}),
        }


class ReviewReplyForm(forms.ModelForm):
    class Meta:
        model = Review
        fields = ["lister_reply"]
        widgets = {"lister_reply": forms.Textarea(attrs={"class": BOOTSTRAP_TEXT, "rows": 2})}


class InquiryForm(forms.ModelForm):
    class Meta:
        model = InquiryRequest
        fields = ["move_in_date", "budget", "message"]
        widgets = {
            "move_in_date": forms.DateInput(attrs={"class": BOOTSTRAP_TEXT, "type": "date"}),
            "budget": forms.NumberInput(attrs={"class": BOOTSTRAP_TEXT}),
            "message": forms.Textarea(attrs={"class": BOOTSTRAP_TEXT, "rows": 3}),
        }


class MessageForm(forms.ModelForm):
    class Meta:
        model = Message
        fields = ["body"]
        widgets = {"body": forms.Textarea(attrs={"class": BOOTSTRAP_TEXT, "rows": 2, "placeholder": "Type a message…"})}


class SavedSearchForm(forms.ModelForm):
    class Meta:
        model = SavedSearch
        fields = ["label", "category", "keyword", "min_price", "max_price", "location_text", "radius_km", "verified_only"]
        widgets = {
            "label": forms.TextInput(attrs={"class": BOOTSTRAP_TEXT}),
            "category": forms.Select(attrs={"class": BOOTSTRAP_SELECT}),
            "keyword": forms.TextInput(attrs={"class": BOOTSTRAP_TEXT}),
            "min_price": forms.NumberInput(attrs={"class": BOOTSTRAP_TEXT}),
            "max_price": forms.NumberInput(attrs={"class": BOOTSTRAP_TEXT}),
            "location_text": forms.TextInput(attrs={"class": BOOTSTRAP_TEXT}),
            "radius_km": forms.NumberInput(attrs={"class": BOOTSTRAP_TEXT}),
            "verified_only": forms.CheckboxInput(attrs={"class": BOOTSTRAP_CHECK}),
        }


class ReportForm(forms.ModelForm):
    class Meta:
        model = Report
        fields = ["reason", "details"]
        widgets = {
            "reason": forms.Select(attrs={"class": BOOTSTRAP_SELECT}),
            "details": forms.Textarea(attrs={"class": BOOTSTRAP_TEXT, "rows": 3}),
        }


# =========================================================================
# Events — dedicated content type (venue/geo, schedule, tickets)
# =========================================================================

class EventForm(forms.ModelForm):
    """
    Core event fields. Every event is created together with at least one
    EventTicketType (see EventTicketTypeFormSet below) — even free events
    get a free "General Admission" tier so RSVPs/capacity can be tracked
    the same way as paid tickets.
    """

    class Meta:
        model = Event
        fields = [
            "event_type", "title", "description",
            "venue_name", "address", "latitude", "longitude", "is_online", "online_url",
            "start_datetime", "end_datetime", "doors_open_time",
            "currency", "total_capacity", "age_restriction", "tags",
            "contact_phone", "contact_email", "contact_whatsapp",
        ]
        widgets = {
            "event_type": forms.Select(attrs={"class": BOOTSTRAP_SELECT, "id": "id_event_type"}),
            "title": forms.TextInput(attrs={"class": BOOTSTRAP_TEXT, "maxlength": 150, "placeholder": "e.g. Lake of Stars Festival"}),
            "description": forms.Textarea(attrs={"class": BOOTSTRAP_TEXT, "rows": 5}),
            "venue_name": forms.TextInput(attrs={"class": BOOTSTRAP_TEXT, "placeholder": "e.g. Sunbird Nkopola Lodge"}),
            "address": forms.TextInput(attrs={"class": BOOTSTRAP_TEXT, "placeholder": "Street / area, city", "id": "id_event_address"}),
            "latitude": forms.HiddenInput(attrs={"id": "id_event_latitude"}),
            "longitude": forms.HiddenInput(attrs={"id": "id_event_longitude"}),
            "is_online": forms.CheckboxInput(attrs={"class": BOOTSTRAP_CHECK, "id": "id_is_online"}),
            "online_url": forms.URLInput(attrs={"class": BOOTSTRAP_TEXT, "placeholder": "https://…"}),
            "start_datetime": forms.DateTimeInput(attrs={"class": BOOTSTRAP_TEXT, "type": "datetime-local"}),
            "end_datetime": forms.DateTimeInput(attrs={"class": BOOTSTRAP_TEXT, "type": "datetime-local"}),
            "doors_open_time": forms.TimeInput(attrs={"class": BOOTSTRAP_TEXT, "type": "time"}),
            "currency": forms.Select(attrs={"class": BOOTSTRAP_SELECT}),
            "total_capacity": forms.NumberInput(attrs={"class": BOOTSTRAP_TEXT, "min": "1", "placeholder": "Leave blank for unlimited"}),
            "age_restriction": forms.TextInput(attrs={"class": BOOTSTRAP_TEXT, "placeholder": "e.g. 18+, All ages"}),
            "tags": forms.TextInput(attrs={"class": BOOTSTRAP_TEXT, "placeholder": "live band, outdoor, food"}),
            "contact_phone": forms.TextInput(attrs={"class": BOOTSTRAP_TEXT}),
            "contact_email": forms.EmailInput(attrs={"class": BOOTSTRAP_TEXT}),
            "contact_whatsapp": forms.TextInput(attrs={"class": BOOTSTRAP_TEXT}),
        }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields["event_type"].queryset = EventCategory.objects.filter(is_active=True)
        self.fields["event_type"].empty_label = "Select an event type…"
        for optional in ["venue_name", "address", "online_url", "doors_open_time",
                         "total_capacity", "age_restriction", "tags",
                         "contact_phone", "contact_email", "contact_whatsapp"]:
            self.fields[optional].required = False

        self.fields["photos"] = MultipleFileField(
            widget=MultipleFileInput(attrs={
                "multiple": True, "accept": "image/*", "class": "form-control", "id": "id_event_photos",
            }),
            required=False,
            label="Photos",
            help_text=f"Upload {settings.ACCO_MIN_EVENT_PHOTOS}–{settings.ACCO_MAX_EVENT_PHOTOS} photos "
                      f"(poster, venue, past editions).",
        )

    def _get_uploaded_photos(self):
        if not self.files:
            return []
        if hasattr(self.files, "getlist"):
            return self.files.getlist("photos")
        uploaded = self.files.get("photos")
        if isinstance(uploaded, (list, tuple)):
            return [f for f in uploaded if f not in [None, ""]]
        return [uploaded] if uploaded not in [None, ""] else []

    def clean(self):
        cleaned = super().clean()
        start = cleaned.get("start_datetime")
        end = cleaned.get("end_datetime")
        if start and end and end <= start:
            self.add_error("end_datetime", "End time must be after the start time.")

        if cleaned.get("is_online"):
            if not cleaned.get("online_url"):
                self.add_error("online_url", "Please provide a link for people to join your online event.")
        elif not cleaned.get("address"):
            self.add_error("address", "Please provide a venue address, or mark this as an online event.")

        photos = self._get_uploaded_photos()
        existing_photos = self.instance.photos.count() if self.instance and self.instance.pk else 0
        total_photos = len(photos) + existing_photos

        if not self.instance or not self.instance.pk:
            if len(photos) < settings.ACCO_MIN_EVENT_PHOTOS:
                self.add_error("photos", f"Please upload at least {settings.ACCO_MIN_EVENT_PHOTOS} photo(s).")
        if total_photos > settings.ACCO_MAX_EVENT_PHOTOS:
            self.add_error("photos", f"You can upload a maximum of {settings.ACCO_MAX_EVENT_PHOTOS} photos.")

        return cleaned


class EventTicketTypeForm(forms.ModelForm):
    class Meta:
        model = EventTicketType
        fields = ["name", "description", "price", "quantity_available", "sales_end"]
        widgets = {
            "name": forms.TextInput(attrs={"class": BOOTSTRAP_TEXT, "placeholder": "e.g. Regular, VIP, Early Bird"}),
            "description": forms.TextInput(attrs={"class": BOOTSTRAP_TEXT, "placeholder": "What's included? (optional)"}),
            "price": forms.NumberInput(attrs={"class": BOOTSTRAP_TEXT, "step": "0.01", "min": "0"}),
            "quantity_available": forms.NumberInput(attrs={"class": BOOTSTRAP_TEXT, "min": "1", "placeholder": "Unlimited"}),
            "sales_end": forms.DateTimeInput(attrs={"class": BOOTSTRAP_TEXT, "type": "datetime-local"}),
        }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields["name"].required = True
        self.fields["description"].required = False
        self.fields["quantity_available"].required = False
        self.fields["sales_end"].required = False
        self.fields["price"].required = False


EventTicketTypeFormSet = inlineformset_factory(
    Event, EventTicketType, form=EventTicketTypeForm,
    extra=1, can_delete=True, min_num=1, validate_min=True, max_num=10, validate_max=True,
)


class EventPhotoForm(forms.ModelForm):
    class Meta:
        model = EventPhoto
        fields = ["image", "caption", "is_cover"]
        widgets = {
            "image": forms.ClearableFileInput(attrs={"class": "form-control"}),
            "caption": forms.TextInput(attrs={"class": BOOTSTRAP_TEXT}),
            "is_cover": forms.CheckboxInput(attrs={"class": BOOTSTRAP_CHECK}),
        }


class EventSearchForm(forms.Form):
    q = forms.CharField(
        required=False, label="Keyword",
        widget=forms.TextInput(attrs={"class": BOOTSTRAP_TEXT, "placeholder": "Search events, venues, cities…"}),
    )
    event_type = forms.ModelChoiceField(
        queryset=EventCategory.objects.filter(is_active=True), required=False,
        widget=forms.Select(attrs={"class": BOOTSTRAP_SELECT}),
    )
    when = forms.ChoiceField(
        required=False,
        choices=[
            ("upcoming", "All Upcoming"), ("today", "Today"), ("week", "This Week"),
            ("month", "This Month"), ("past", "Past Events"),
        ],
        widget=forms.Select(attrs={"class": BOOTSTRAP_SELECT}),
    )
    price = forms.ChoiceField(
        required=False, choices=[("", "Any"), ("free", "Free"), ("paid", "Paid")],
        widget=forms.Select(attrs={"class": BOOTSTRAP_SELECT}),
    )
    lat = forms.DecimalField(required=False, widget=forms.HiddenInput())
    lng = forms.DecimalField(required=False, widget=forms.HiddenInput())
    radius_km = forms.IntegerField(required=False, initial=25, widget=forms.NumberInput(attrs={"class": BOOTSTRAP_TEXT}))


class EventTicketBookingForm(forms.Form):
    ticket_type = forms.ModelChoiceField(
        queryset=EventTicketType.objects.none(),
        widget=forms.Select(attrs={"class": BOOTSTRAP_SELECT}),
    )
    quantity = forms.IntegerField(
        min_value=1, max_value=20, initial=1,
        widget=forms.NumberInput(attrs={"class": BOOTSTRAP_TEXT, "min": "1", "max": "20"}),
    )
    attendee_name = forms.CharField(
        required=False, widget=forms.TextInput(attrs={"class": BOOTSTRAP_TEXT, "placeholder": "Full name"}),
    )
    attendee_phone = forms.CharField(
        required=False, widget=forms.TextInput(attrs={"class": BOOTSTRAP_TEXT, "placeholder": "Phone number"}),
    )

    def __init__(self, *args, event=None, **kwargs):
        super().__init__(*args, **kwargs)
        if event is not None:
            self.fields["ticket_type"].queryset = event.ticket_types.all()

    def clean(self):
        cleaned = super().clean()
        ticket_type = cleaned.get("ticket_type")
        quantity = cleaned.get("quantity")
        if ticket_type and quantity:
            if not ticket_type.is_available:
                self.add_error("ticket_type", "This ticket type is no longer available.")
            elif ticket_type.remaining is not None and quantity > ticket_type.remaining:
                self.add_error("quantity", f"Only {ticket_type.remaining} ticket(s) left for this type.")
        return cleaned