"""Admin advertisement dashboard routes.

Thin request handlers attached to ``admin_bp`` (prefix ``/admin``). All routes
require an authenticated Super Admin. Business logic lives in
:mod:`app.admin.advertisements.services`; single-record lookups reuse
``get_advertisement_or_404`` from the existing advertisement service so fetching
logic is never duplicated.
"""

from __future__ import annotations

from datetime import datetime, time

from flask import current_app, flash, redirect, render_template, request, url_for
from flask_login import current_user, login_required

from app.admin import admin_bp
from app.admin.advertisements import services as ad_admin
from app.admin.advertisements.forms import AdvertisementForm
from app.admin.audit import record_admin_action
from app.admin.utils import admin_required
from app.advertisements.services import get_advertisement_or_404
from app.models.enums import (
    AdvertisementMediaType,
    AdvertisementPlacement,
    AdvertisementType,
)

# Filter option lists shared by the list + analytics templates. CHAT is included
# so admins can filter the rewarded (chat-unlock) advertisements.
PLACEMENT_OPTIONS = [(p.value, p.display_name) for p in AdvertisementPlacement]
TYPE_OPTIONS = [(t.value, t.value.title()) for t in AdvertisementType]
MEDIA_TYPE_OPTIONS = [(m.value, m.value.title()) for m in AdvertisementMediaType]
STATUS_OPTIONS = [
    ("active", "Active"),
    ("scheduled", "Scheduled"),
    ("expired", "Expired"),
    ("disabled", "Disabled"),
]
SORT_OPTIONS = [
    ("newest", "Newest"),
    ("oldest", "Oldest"),
    ("priority_high", "Display Weight: High to Low"),
    ("priority_low", "Display Weight: Low to High"),
    ("title", "Title A–Z"),
    ("impressions", "Most impressions"),
    ("clicks", "Most clicks"),
]

# Placeholder used to render the live-preview partials on the empty create form
# (the ad partials only render when given an object). JS then syncs it to the
# live form values. On edit the real advertisement is used instead.
_PREVIEW_PLACEHOLDER = {
    "title": "Advertisement title",
    "description": "Your supporting copy appears here.",
    "image_path": None,
    "icon": "bi-stars",
    "redirect_url": "#",
    "public_id": "preview",
}


def _parse_date(raw: str | None, *, end_of_day: bool = False):
    """Parse a ``YYYY-MM-DD`` string into a datetime, or None."""
    if not raw:
        return None
    try:
        day = datetime.strptime(raw.strip(), "%Y-%m-%d").date()
    except ValueError:
        return None
    return datetime.combine(day, time.max if end_of_day else time.min)


def _selected(value, options) -> str:
    """Return value if it is a valid option key, else ''."""
    return value if value in {key for key, _ in options} else ""


@admin_bp.route("/advertisements")
@login_required
@admin_required
def ad_list():
    page = request.args.get("page", 1, type=int)
    q = (request.args.get("q") or "").strip()
    placement = _selected((request.args.get("placement") or "").strip(), PLACEMENT_OPTIONS)
    ad_type = _selected((request.args.get("type") or "").strip(), TYPE_OPTIONS)
    media_type = _selected((request.args.get("media_type") or "").strip(), MEDIA_TYPE_OPTIONS)
    status = _selected((request.args.get("status") or "").strip(), STATUS_OPTIONS)
    priority = request.args.get("priority", type=int)
    sort = request.args.get("sort", "newest")
    if sort not in dict(SORT_OPTIONS):
        sort = "newest"
    date_from_raw = (request.args.get("date_from") or "").strip()
    date_to_raw = (request.args.get("date_to") or "").strip()

    pagination = ad_admin.list_advertisements(
        page,
        q=q or None,
        placement=placement or None,
        ad_type=ad_type or None,
        media_type=media_type or None,
        status=status or None,
        priority=priority,
        date_from=_parse_date(date_from_raw),
        date_to=_parse_date(date_to_raw, end_of_day=True),
        sort=sort,
    )
    # Batched per-ad rewarded metrics for this page only — one query, no N+1.
    rewarded_stats = ad_admin.rewarded_stats_map([ad.id for ad in pagination.items])
    rows = [
        {
            "ad": ad,
            "status": ad_admin.advertisement_status(ad),
            "ctr": ad_admin.compute_ctr(ad.impression_count, ad.click_count),
            "rewarded": rewarded_stats.get(ad.id),
        }
        for ad in pagination.items
    ]
    # Preserve active filters across pagination links.
    filter_args = {
        k: v
        for k, v in {
            "q": q or None,
            "placement": placement or None,
            "type": ad_type or None,
            "media_type": media_type or None,
            "status": status or None,
            "priority": priority,
            "date_from": date_from_raw or None,
            "date_to": date_to_raw or None,
            "sort": sort,
        }.items()
        if v is not None
    }
    return render_template(
        "admin/advertisements/list.html",
        rows=rows,
        pagination=pagination,
        status_badge_class=ad_admin.STATUS_BADGE_CLASS,
        # Dashboard analytics (reuse the existing, already-optimised aggregates).
        overview=ad_admin.analytics_overview(),
        charts=ad_admin.analytics_chart_data(top_n=10),
        placement_summary=ad_admin.placement_summary(),
        storage=ad_admin.advertisement_storage_stats(),
        q=q,
        placement=placement,
        ad_type=ad_type,
        media_type=media_type,
        status=status,
        priority=priority or "",
        date_from=date_from_raw,
        date_to=date_to_raw,
        sort=sort,
        placement_options=PLACEMENT_OPTIONS,
        type_options=TYPE_OPTIONS,
        media_type_options=MEDIA_TYPE_OPTIONS,
        status_options=STATUS_OPTIONS,
        sort_options=SORT_OPTIONS,
        filter_args=filter_args,
    )


@admin_bp.route("/advertisements/analytics")
@login_required
@admin_required
def ad_analytics():
    return render_template(
        "admin/advertisements/analytics.html",
        overview=ad_admin.analytics_overview(),
        charts=ad_admin.analytics_chart_data(),
        status_badge_class=ad_admin.STATUS_BADGE_CLASS,
        rewarded=ad_admin.rewarded_analytics(),
        rewarded_charts=ad_admin.rewarded_chart_data(),
        rewarded_breakdown=ad_admin.rewarded_ad_breakdown(),
    )


@admin_bp.route("/advertisements/<public_id>/stats")
@login_required
@admin_required
def ad_stats(public_id: str):
    """Per-advertisement analytics page — reuses the rewarded event aggregates and
    the shared Chart.js helper. Read-only; never records analytics."""
    ad = get_advertisement_or_404(public_id)
    return render_template(
        "admin/advertisements/stats.html",
        ad=ad,
        status=ad_admin.advertisement_status(ad),
        ctr=ad_admin.compute_ctr(ad.impression_count, ad.click_count),
        status_badge_class=ad_admin.STATUS_BADGE_CLASS,
        rewarded=ad_admin.rewarded_stats_for_ad(ad.id),
    )


@admin_bp.route("/advertisements/new", methods=["GET", "POST"])
@login_required
@admin_required
def ad_create():
    form = AdvertisementForm()
    if request.method == "GET":
        form.priority.data = current_app.config.get("DEFAULT_AD_PRIORITY", 100)

    if form.validate_on_submit():
        error = ad_admin.image_validation_error(form.image.data) or ad_admin.video_upload_error(form, None)
        if error:
            flash(error, "danger")
        else:
            ad = ad_admin.create_advertisement(form)
            record_admin_action(current_user, "ad_created", ad, request=request,
                                details={"placement": ad.placement.value, "type": ad.advertisement_type.value})
            flash(f"Advertisement “{ad.title}” created.", "success")
            return redirect(url_for("admin.ad_list"))

    return render_template(
        "admin/advertisements/create.html", form=form, ad=None,
        preview_ad=_PREVIEW_PLACEHOLDER,
    )


@admin_bp.route("/advertisements/<public_id>/edit", methods=["GET", "POST"])
@login_required
@admin_required
def ad_edit(public_id: str):
    ad = get_advertisement_or_404(public_id)
    form = AdvertisementForm(obj=ad)

    if request.method == "GET":
        # SelectFields and enum/datetime fields need their stored values.
        form.advertisement_type.data = ad.advertisement_type.value
        form.placement.data = ad.placement.value
        form.media_type.data = ad.media_type.value
        form.video_duration.data = ad.video_duration
        form.icon.data = ad.icon or ""
        form.start_at.data = ad.start_at
        form.end_at.data = ad.end_at
        form.is_active.data = ad.is_active

    if form.validate_on_submit():
        error = ad_admin.image_validation_error(form.image.data) or ad_admin.video_upload_error(form, ad)
        if error:
            flash(error, "danger")
        else:
            ad_admin.update_advertisement(ad, form)
            record_admin_action(current_user, "ad_updated", ad, request=request)
            flash(f"Advertisement “{ad.title}” updated.", "success")
            return redirect(url_for("admin.ad_list"))

    return render_template("admin/advertisements/edit.html", form=form, ad=ad, preview_ad=ad)


@admin_bp.route("/advertisements/<public_id>/preview")
@login_required
@admin_required
def ad_preview(public_id: str):
    ad = get_advertisement_or_404(public_id)
    return render_template(
        "admin/advertisements/preview.html",
        ad=ad,
        status=ad_admin.advertisement_status(ad),
        ctr=ad_admin.compute_ctr(ad.impression_count, ad.click_count),
        status_badge_class=ad_admin.STATUS_BADGE_CLASS,
    )


@admin_bp.route("/advertisements/<public_id>/duplicate", methods=["POST"])
@login_required
@admin_required
def ad_duplicate(public_id: str):
    ad = get_advertisement_or_404(public_id)
    copy = ad_admin.duplicate_advertisement(ad)
    flash(f"Duplicated as “{copy.title}” (disabled).", "success")
    return redirect(url_for("admin.ad_edit", public_id=copy.public_id))


@admin_bp.route("/advertisements/<public_id>/enable", methods=["POST"])
@login_required
@admin_required
def ad_enable(public_id: str):
    ad = get_advertisement_or_404(public_id)
    ad_admin.set_active(ad, True)
    record_admin_action(current_user, "ad_activated", ad, request=request)
    flash(f"“{ad.title}” enabled.", "success")
    return redirect(url_for("admin.ad_list"))


@admin_bp.route("/advertisements/<public_id>/disable", methods=["POST"])
@login_required
@admin_required
def ad_disable(public_id: str):
    ad = get_advertisement_or_404(public_id)
    ad_admin.set_active(ad, False)
    record_admin_action(current_user, "ad_deactivated", ad, request=request)
    flash(f"“{ad.title}” disabled.", "info")
    return redirect(url_for("admin.ad_list"))


@admin_bp.route("/advertisements/<public_id>/delete", methods=["POST"])
@login_required
@admin_required
def ad_delete(public_id: str):
    ad = get_advertisement_or_404(public_id)
    title = ad.title
    # Record before deletion so the entity name / public id are still available.
    record_admin_action(current_user, "ad_deleted", ad, request=request, details={"title": title})
    ad_admin.delete_advertisement(ad)
    flash(f"Advertisement “{title}” deleted.", "success")
    return redirect(url_for("admin.ad_list"))


@admin_bp.route("/advertisements/bulk", methods=["POST"])
@login_required
@admin_required
def ad_bulk():
    action = (request.form.get("action") or "").strip()
    ids = [int(x) for x in request.form.getlist("ad_ids") if x.isdigit()]
    if not ids:
        flash("Select at least one advertisement.", "warning")
        return redirect(url_for("admin.ad_list"))

    def _noun(count: int) -> str:
        return "advertisement" if count == 1 else "advertisements"

    n = 0
    if action == "enable":
        n = ad_admin.bulk_set_active(ids, True)
        flash(f"✅ {n} {_noun(n)} activated.", "success")
    elif action == "disable":
        n = ad_admin.bulk_set_active(ids, False)
        flash(f"✅ {n} {_noun(n)} deactivated.", "success")
    elif action == "delete":
        n = ad_admin.bulk_delete(ids)
        flash(f"✅ {n} {_noun(n)} deleted.", "success")
    elif action == "duplicate":
        n = ad_admin.bulk_duplicate(ids)
        flash(f"✅ {n} {_noun(n)} duplicated.", "success")
    elif action == "priority":
        priority = request.form.get("bulk_priority", type=int)
        if priority is None or priority < 0 or priority > 1000:
            flash("Enter a valid Display Weight (0–1000).", "warning")
        else:
            n = ad_admin.bulk_set_priority(ids, priority)
            flash(f"✅ Display Weight set to {priority} on {n} {_noun(n)}.", "success")
    elif action == "placement":
        placement = (request.form.get("bulk_placement") or "").strip()
        n = ad_admin.bulk_set_placement(ids, placement)
        if n:
            flash(f"✅ {n} {_noun(n)} moved to {placement.title()}.", "success")
        else:
            flash("Choose a valid placement.", "warning")
    else:
        flash("Unknown bulk action.", "warning")

    if n:
        record_admin_action(current_user, "bulk_advertisement", f"{n} advertisement(s)",
                            request=request, details={"action": action, "count": n})

    return redirect(url_for("admin.ad_list"))
