"""Admin advertisement forms.

Server-side validation for creating and editing advertisements. Image *format*
is validated here (Flask-WTF ``FileAllowed``); image *size* is validated in the
service layer against ``MAX_AD_IMAGE_SIZE`` so the limit stays configurable.
"""

from __future__ import annotations

from flask_wtf import FlaskForm
from flask_wtf.file import FileAllowed, FileField
from wtforms import (
    BooleanField,
    IntegerField,
    SelectField,
    StringField,
    SubmitField,
    TextAreaField,
)
from wtforms.fields import DateTimeLocalField
from wtforms.validators import URL, Length, NumberRange, Optional, ValidationError

from app.models.enums import (
    AdvertisementMediaType,
    AdvertisementPlacement,
    AdvertisementType,
)

# Curated fallback icons (Bootstrap Icons) offered in the form.
ICON_CHOICES = [
    ("", "Default (stars)"),
    ("bi-stars", "Stars"),
    ("bi-mortarboard-fill", "Graduation"),
    ("bi-laptop", "Laptop"),
    ("bi-book-fill", "Book"),
    ("bi-megaphone-fill", "Megaphone"),
    ("bi-gift-fill", "Gift"),
    ("bi-lightning-charge-fill", "Lightning"),
    ("bi-rocket-takeoff-fill", "Rocket"),
    ("bi-tag-fill", "Tag"),
]

_IMAGE_EXTS = ["png", "jpg", "jpeg", "webp"]
_VIDEO_EXTS = ["mp4", "webm", "ogg"]


class AdvertisementForm(FlaskForm):
    """Create/edit an advertisement."""

    title = StringField("Title", validators=[Length(min=2, max=150)])
    description = TextAreaField("Description", validators=[Optional(), Length(max=500)])
    redirect_url = StringField(
        "Destination URL",
        validators=[Optional(), URL(message="Enter a valid URL (including https://)."), Length(max=500)],
    )
    advertisement_type = SelectField(
        "Advertisement type",
        choices=[(t.value, t.value.title()) for t in AdvertisementType],
    )
    # Only banner placements are admin-selectable. CHAT is reserved for rewarded
    # advertisements and assigned automatically (the form hides this field and shows
    # a read-only "Chat Unlock" card for Rewarded), so it is intentionally excluded
    # from the choices — a crafted banner+chat request fails validation here.
    placement = SelectField(
        "Placement",
        choices=[
            (p.value, p.display_name)
            for p in AdvertisementPlacement
            if p is not AdvertisementPlacement.CHAT
        ],
    )
    # Stored in the ``priority`` column (unchanged) but presented as "Display
    # Weight": it sets how likely this advertisement is to be shown, not its order.
    # Range/negative are validated in ``validate_priority`` for plain-language
    # messages (avoids WTForms' technical NumberRange wording).
    priority = IntegerField(
        "Display Weight",
        validators=[Optional()],
        default=100,
    )
    icon = SelectField("Fallback icon", choices=ICON_CHOICES, validators=[Optional()])
    # Labelled "Reward Format" in the UI because that is the choice an admin is
    # actually making (Rewarded only); the stored column stays ``media_type``.
    media_type = SelectField(
        "Reward Format",
        choices=[
            (AdvertisementMediaType.IMAGE.value, "Image Advertisement"),
            (AdvertisementMediaType.VIDEO.value, "Video Advertisement"),
        ],
        default=AdvertisementMediaType.IMAGE.value,
    )
    image = FileField(
        "Image (optional)",
        validators=[FileAllowed(_IMAGE_EXTS, "Images only: PNG, JPG, JPEG, WEBP.")],
    )
    video = FileField(
        "Video File",
        validators=[FileAllowed(_VIDEO_EXTS, "Videos only: MP4, WebM or Ogg.")],
    )
    remove_video = BooleanField("Remove current video")
    video_duration = IntegerField(
        "Video duration (seconds)",
        validators=[Optional(), NumberRange(min=5, max=600)],
        default=20,
    )
    start_at = DateTimeLocalField(
        "Start date", format="%Y-%m-%dT%H:%M", validators=[Optional()]
    )
    end_at = DateTimeLocalField(
        "End date", format="%Y-%m-%dT%H:%M", validators=[Optional()]
    )
    is_active = BooleanField("Enable advertisement", default=True)
    submit = SubmitField("Save advertisement")

    def validate_title(self, field) -> None:
        if not (field.data or "").strip():
            raise ValidationError("Title is required.")

    def validate_priority(self, field) -> None:
        """Plain-language Display Weight validation (0–1000, no negatives)."""
        if field.data is None:
            return
        if field.data < 0:
            raise ValidationError("Weight cannot be negative.")
        if field.data > 1000:
            raise ValidationError("Display Weight must be between 0 and 1000.")

    def validate_end_at(self, field) -> None:
        """End must be strictly after start when both are provided."""
        if field.data and self.start_at.data and field.data <= self.start_at.data:
            raise ValidationError("End date must be after the start date.")
