"""Top-level admin routes (attached to ``admin_bp``, prefix ``/admin``).

Thin handlers only; analytics computation lives in :mod:`app.admin.analytics`
and user-management queries in :mod:`app.admin.user_management`. Every route
requires an authenticated Super Admin.
"""

from __future__ import annotations

from datetime import datetime, time

from flask import abort, flash, redirect, render_template, request, url_for
from flask_login import current_user, login_required
from markupsafe import Markup, escape

from app.admin import admin_bp
from app.admin import campus as campus_service
from app.admin.analytics import get_admin_dashboard
from app.admin.audit import (
    ACTION_FILTER_OPTIONS,
    AUDIT_SORT_OPTIONS,
    ENTITY_TYPE_FILTER_OPTIONS,
    get_admin_filter_options,
    get_audit_logs,
    get_audit_summary,
    record_admin_action,
)
from app.admin.listing_management import (
    FEATURED_FILTER_OPTIONS,
    LISTING_ACTIONS,
    LISTING_SORT_OPTIONS,
    LISTING_STATUS_FILTERS,
    apply_listing_action,
    bulk_update_listings,
    get_category_filter_options,
    get_listing_detail,
    get_listing_summary,
    get_listings,
)
from app.admin.user_management import (
    PREMIUM_FILTER_OPTIONS,
    ROLE_FILTER_OPTIONS,
    STATUS_FILTER_OPTIONS,
    USER_SORT_OPTIONS,
    get_user_detail,
    get_user_summary,
    get_users,
    set_account_status,
)
from app.admin.utils import admin_required
from app.auth import verification as verification_service
from app.extensions import db
from app.models import Listing, User
from app.models.enums import AccountStatus, UserRole
from app.moderation.services import (
    REPORT_ASSIGNED_FILTERS,
    REPORT_COMPLETION_ACTIONS,
    REPORT_ENTITY_TYPE_FILTERS,
    REPORT_REASON_FILTERS,
    REPORT_REASON_OPTIONS,
    REPORT_SORT_OPTIONS,
    REPORT_STATUS_FILTERS,
    assign_report,
    dismiss_report,
    get_report_detail,
    get_report_or_404,
    get_report_summary,
    get_reports,
    mark_under_review,
    notify_report_parties,
    remove_reported_listing,
    resolve_report,
    suspend_reported_seller,
    warn_reported_seller,
)
from app.premium.services import activate_premium, expire_premium


@admin_bp.route("/analytics")
@login_required
@admin_required
def analytics():
    """Platform-wide admin analytics dashboard."""
    return render_template("admin/analytics.html", dashboard=get_admin_dashboard())


def _get_user_or_404(public_id: str) -> User:
    return db.first_or_404(db.select(User).where(User.public_id == public_id))


def _selected(value, options) -> str:
    value = (value or "").strip().lower()
    return value if value in {key for key, _ in options} else "all"


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)


@admin_bp.route("/users")
@login_required
@admin_required
def users():
    """User management: searchable, filterable, sortable, paginated user table."""
    page = request.args.get("page", 1, type=int)
    q = (request.args.get("q") or "").strip()
    premium = _selected(request.args.get("premium"), PREMIUM_FILTER_OPTIONS)
    status = _selected(request.args.get("status"), STATUS_FILTER_OPTIONS)
    role = _selected(request.args.get("role"), ROLE_FILTER_OPTIONS)
    sort = request.args.get("sort", "newest")
    if sort not in dict(USER_SORT_OPTIONS):
        sort = "newest"

    pagination, rows = get_users(page, search=q, premium=premium, status=status, role=role, sort=sort)
    filter_args = {k: v for k, v in {
        "q": q or None, "premium": premium if premium != "all" else None,
        "status": status if status != "all" else None, "role": role if role != "all" else None,
        "sort": sort if sort != "newest" else None,
    }.items() if v is not None}

    return render_template(
        "admin/users/list.html",
        pagination=pagination, rows=rows, summary=get_user_summary(),
        q=q, premium=premium, status=status, role=role, sort=sort,
        premium_options=PREMIUM_FILTER_OPTIONS, status_options=STATUS_FILTER_OPTIONS,
        role_options=ROLE_FILTER_OPTIONS, sort_options=USER_SORT_OPTIONS,
        filter_args=filter_args,
    )


@admin_bp.route("/users/<public_id>")
@login_required
@admin_required
def user_detail(public_id: str):
    user = _get_user_or_404(public_id)
    return render_template(
        "admin/users/detail.html",
        account=user, detail=get_user_detail(user),
        is_self=(user.id == current_user.id),
        is_super_admin=(user.role == UserRole.SUPER_ADMIN),
    )


@admin_bp.route("/users/<public_id>/grant-premium", methods=["POST"])
@login_required
@admin_required
def grant_premium(public_id: str):
    user = _get_user_or_404(public_id)
    subscription = activate_premium(user, activated_by=current_user.id)
    record_admin_action(current_user, "premium_granted", user, request=request,
                        details={"plan": subscription.plan_name, "until": subscription.end_date.isoformat()})
    flash(f"Premium granted to {user.email} until {subscription.end_date.strftime('%d %b %Y')}.", "success")
    return redirect(url_for("admin.user_detail", public_id=public_id))


@admin_bp.route("/users/<public_id>/remove-premium", methods=["POST"])
@login_required
@admin_required
def remove_premium(public_id: str):
    user = _get_user_or_404(public_id)
    expired = expire_premium(user)
    if expired:
        record_admin_action(current_user, "premium_removed", user, request=request,
                            details={"expired_subscriptions": expired})
        flash(f"Premium removed from {user.email}.", "info")
    else:
        flash(f"{user.email} has no active premium subscription.", "warning")
    return redirect(url_for("admin.user_detail", public_id=public_id))


@admin_bp.route("/users/<public_id>/suspend", methods=["POST"])
@login_required
@admin_required
def suspend_user(public_id: str):
    user = _get_user_or_404(public_id)
    if user.id == current_user.id:
        flash("You cannot suspend your own account.", "warning")
    elif user.role == UserRole.SUPER_ADMIN:
        flash("Administrator accounts cannot be suspended here.", "warning")
    else:
        set_account_status(user, AccountStatus.SUSPENDED)
        record_admin_action(current_user, "user_suspended", user, request=request)
        flash(f"{user.email} has been suspended.", "success")
    return redirect(url_for("admin.user_detail", public_id=public_id))


@admin_bp.route("/users/<public_id>/reactivate", methods=["POST"])
@login_required
@admin_required
def reactivate_user(public_id: str):
    user = _get_user_or_404(public_id)
    set_account_status(user, AccountStatus.ACTIVE)
    record_admin_action(current_user, "user_reactivated", user, request=request)
    flash(f"{user.email} has been reactivated.", "success")
    return redirect(url_for("admin.user_detail", public_id=public_id))


# --- Student verification moderation ---------------------------------------

@admin_bp.route("/verifications")
@login_required
@admin_required
def verifications():
    """Queue of pending Verified-Student requests, with dashboard statistics.

    An optional ``?university=`` narrows the pending queue to one campus (from the
    clickable university names in University Analytics)."""
    university = (request.args.get("university") or "").strip() or None
    return render_template(
        "admin/verifications/list.html",
        pending=verification_service.get_pending_verifications(university=university),
        dashboard=verification_service.get_verification_dashboard(),
        universities=verification_service.get_university_statistics(),
        university_filter=university,
    )


@admin_bp.route("/verifications/university/<path:university_name>")
@login_required
@admin_required
def campus_overview(university_name: str):
    """Campus Overview: verification and live marketplace insights for one
    university. Unknown university → 404."""
    name = (university_name or "").strip()
    if not verification_service.university_exists(name):
        abort(404)
    page = request.args.get("page", 1, type=int)
    overview = verification_service.get_university_overview(name)
    return render_template(
        "admin/verifications/campus.html",
        university=name,
        stats=overview["stats"],
        recent_activity=overview["recent_activity"],
        students=verification_service.get_university_students(name, page=page),
        marketplace=campus_service.get_campus_overview(name),
    )


@admin_bp.route("/verifications/<public_id>")
@login_required
@admin_required
def verification_detail(public_id: str):
    user = _get_user_or_404(public_id)
    return render_template(
        "admin/verifications/detail.html",
        account=user, profile=user.profile,
        history=verification_service.get_history_timeline(user, include_moderation=True),
    )


@admin_bp.route("/verifications/<public_id>/approve", methods=["POST"])
@login_required
@admin_required
def approve_verification(public_id: str):
    user = _get_user_or_404(public_id)
    try:
        verification_service.approve_verification(user, current_user)
    except verification_service.VerificationError as exc:
        flash(str(exc), "warning")
    else:
        record_admin_action(current_user, "verification_approved", user, request=request)
        flash(f"{user.email} is now a Verified Student.", "success")
    return redirect(url_for("admin.verifications"))


@admin_bp.route("/verifications/<public_id>/reject", methods=["POST"])
@login_required
@admin_required
def reject_verification(public_id: str):
    user = _get_user_or_404(public_id)
    reason = (request.form.get("reason") or "").strip()
    try:
        verification_service.reject_verification(user, current_user, reason=reason or None)
    except verification_service.VerificationError as exc:
        flash(str(exc), "warning")
    else:
        record_admin_action(current_user, "verification_rejected", user, request=request,
                            details={"reason": reason} if reason else None)
        flash("Verification rejected. The student has been notified.", "info")
    return redirect(url_for("admin.verifications"))


# --- Listing management ----------------------------------------------------

_LISTING_ACTION_FLASH = {
    "feature": "featured",
    "unfeature": "unfeatured",
    "available": "marked available",
    "reserved": "marked reserved",
    "sold": "marked sold",
    "delete": "deleted",
    "restore": "restored",
}

# Map a listing quick/bulk action to its audit-log action key (available has no
# dedicated audit event).
_LISTING_ACTION_AUDIT = {
    "feature": "listing_featured",
    "unfeature": "listing_unfeatured",
    "reserved": "listing_marked_reserved",
    "sold": "listing_marked_sold",
    "delete": "listing_deleted",
    "restore": "listing_restored",
}


def _get_listing_or_404(public_id: str) -> Listing:
    return db.first_or_404(db.select(Listing).where(Listing.public_id == public_id))


@admin_bp.route("/listings")
@login_required
@admin_required
def listings():
    """Listing management: searchable, filterable, sortable, paginated table."""
    page = request.args.get("page", 1, type=int)
    q = (request.args.get("q") or "").strip()
    status = _selected(request.args.get("status"), LISTING_STATUS_FILTERS)
    featured = _selected(request.args.get("featured"), FEATURED_FILTER_OPTIONS)
    category = (request.args.get("category") or "all").strip() or "all"
    seller = (request.args.get("seller") or "").strip() or None
    sort = request.args.get("sort", "newest")
    if sort not in dict(LISTING_SORT_OPTIONS):
        sort = "newest"

    pagination, rows = get_listings(
        page, search=q, status=status, category=category, featured=featured, seller=seller, sort=sort
    )
    filter_args = {k: v for k, v in {
        "q": q or None, "status": status if status != "all" else None,
        "featured": featured if featured != "all" else None,
        "category": category if category != "all" else None,
        "seller": seller, "sort": sort if sort != "newest" else None,
    }.items() if v is not None}

    return render_template(
        "admin/listings/list.html",
        pagination=pagination, rows=rows, summary=get_listing_summary(),
        q=q, status=status, featured=featured, category=category, seller=seller, sort=sort,
        status_options=LISTING_STATUS_FILTERS, featured_options=FEATURED_FILTER_OPTIONS,
        category_options=get_category_filter_options(), sort_options=LISTING_SORT_OPTIONS,
        filter_args=filter_args,
    )


@admin_bp.route("/listings/<public_id>")
@login_required
@admin_required
def listing_detail(public_id: str):
    listing = _get_listing_or_404(public_id)
    return render_template(
        "admin/listings/detail.html",
        listing=listing, detail=get_listing_detail(listing),
    )


@admin_bp.route("/listings/<public_id>/action", methods=["POST"])
@login_required
@admin_required
def listing_action(public_id: str):
    """Apply a single quick action (feature/unfeature/status/delete/restore)."""
    listing = _get_listing_or_404(public_id)
    action = (request.form.get("action") or "").strip().lower()
    if action not in LISTING_ACTIONS:
        flash("Unknown action.", "warning")
        return redirect(url_for("admin.listing_detail", public_id=public_id))
    before_status = listing.status.value
    if apply_listing_action(listing, action, editor=current_user):
        audit_action = _LISTING_ACTION_AUDIT.get(action)
        if audit_action:
            record_admin_action(current_user, audit_action, listing, request=request,
                                details={"from_status": before_status, "to_status": listing.status.value})
        flash(f"“{listing.title}” {_LISTING_ACTION_FLASH[action]}.", "success")
    else:
        flash("That action isn't valid for this listing.", "warning")
    return redirect(url_for("admin.listing_detail", public_id=public_id))


@admin_bp.route("/listings/bulk", methods=["POST"])
@login_required
@admin_required
def listing_bulk_action():
    """Apply one action to many selected listings (loop lives in the service)."""
    action = (request.form.get("action") or "").strip().lower()
    public_ids = request.form.getlist("selected")
    if action not in LISTING_ACTIONS:
        flash("Unknown bulk action.", "warning")
    elif not public_ids:
        flash("No listings selected.", "warning")
    else:
        changed = bulk_update_listings(action, public_ids, editor=current_user)
        if changed:
            record_admin_action(
                current_user, "bulk_listing", f"{changed} listing{'s' if changed != 1 else ''}",
                request=request, details={"action": action, "count": changed, "selected": len(public_ids)},
            )
        flash(f"{changed} listing{'s' if changed != 1 else ''} {_LISTING_ACTION_FLASH[action]}.", "success")
    return redirect(request.referrer or url_for("admin.listings"))


# --- Audit logs ------------------------------------------------------------

@admin_bp.route("/audit-logs")
@login_required
@admin_required
def audit_logs():
    """Read-only, append-only administrative audit trail."""
    page = request.args.get("page", 1, type=int)
    q = (request.args.get("q") or "").strip()
    admin = (request.args.get("admin") or "all").strip() or "all"
    action = _selected(request.args.get("action"), ACTION_FILTER_OPTIONS)
    entity_type = _selected(request.args.get("entity_type"), ENTITY_TYPE_FILTER_OPTIONS)
    sort = request.args.get("sort", "newest")
    if sort not in dict(AUDIT_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, rows = get_audit_logs(
        page, search=q, admin=admin, action=action, entity_type=entity_type,
        date_from=_parse_date(date_from_raw), date_to=_parse_date(date_to_raw, end_of_day=True),
        sort=sort,
    )
    highlight = (request.args.get("highlight") or "").strip()
    filter_args = {k: v for k, v in {
        "q": q or None, "admin": admin if admin != "all" else None,
        "action": action if action != "all" else None,
        "entity_type": entity_type if entity_type != "all" else None,
        "date_from": date_from_raw or None, "date_to": date_to_raw or None,
        "sort": sort if sort != "newest" else None,
    }.items() if v is not None}

    return render_template(
        "admin/audit/list.html",
        pagination=pagination, rows=rows, summary=get_audit_summary(),
        q=q, admin=admin, action=action, entity_type=entity_type, sort=sort,
        date_from=date_from_raw, date_to=date_to_raw, highlight=highlight,
        admin_options=get_admin_filter_options(), action_options=ACTION_FILTER_OPTIONS,
        entity_type_options=ENTITY_TYPE_FILTER_OPTIONS, sort_options=AUDIT_SORT_OPTIONS,
        filter_args=filter_args,
    )


# --- Reports & moderation --------------------------------------------------

@admin_bp.route("/reports")
@login_required
@admin_required
def reports():
    """Moderation queue: searchable, filterable, sortable, paginated reports."""
    page = request.args.get("page", 1, type=int)
    q = (request.args.get("q") or "").strip()
    status = _selected(request.args.get("status"), REPORT_STATUS_FILTERS)
    reason = _selected(request.args.get("reason"), REPORT_REASON_FILTERS)
    entity_type = _selected(request.args.get("entity_type"), REPORT_ENTITY_TYPE_FILTERS)
    assigned = _selected(request.args.get("assigned"), REPORT_ASSIGNED_FILTERS)
    seller = (request.args.get("seller") or "").strip() or None
    sort = request.args.get("sort", "newest")
    if sort not in dict(REPORT_SORT_OPTIONS):
        sort = "newest"

    pagination, rows = get_reports(
        page, search=q, status=status, reason=reason, entity_type=entity_type,
        assigned=assigned, seller=seller, sort=sort,
    )
    filter_args = {k: v for k, v in {
        "q": q or None, "status": status if status != "all" else None,
        "reason": reason if reason != "all" else None,
        "entity_type": entity_type if entity_type != "all" else None,
        "assigned": assigned if assigned != "all" else None,
        "seller": seller, "sort": sort if sort != "newest" else None,
    }.items() if v is not None}

    return render_template(
        "admin/reports/list.html",
        pagination=pagination, rows=rows, summary=get_report_summary(),
        q=q, status=status, reason=reason, entity_type=entity_type, assigned=assigned,
        seller=seller, sort=sort,
        status_options=REPORT_STATUS_FILTERS, reason_options=REPORT_REASON_FILTERS,
        entity_type_options=REPORT_ENTITY_TYPE_FILTERS, assigned_options=REPORT_ASSIGNED_FILTERS,
        sort_options=REPORT_SORT_OPTIONS, filter_args=filter_args,
    )


@admin_bp.route("/reports/<public_id>")
@login_required
@admin_required
def report_detail(public_id: str):
    report = get_report_or_404(public_id)
    return render_template(
        "admin/reports/detail.html",
        report=report, detail=get_report_detail(report),
        warn_reasons=REPORT_REASON_OPTIONS,
    )


# action -> (service callable(report, moderator[, notes]), audit key, past-tense verb)
_REPORT_ACTIONS = {
    "assign": (lambda r, m, n: assign_report(r, m), "report_assigned", "assigned to you"),
    "under_review": (lambda r, m, n: mark_under_review(r, m), "report_under_review", "marked under review"),
    "resolve": (lambda r, m, n: resolve_report(r, m, n), "report_resolved", "resolved"),
    "dismiss": (lambda r, m, n: dismiss_report(r, m, n), "report_dismissed", "dismissed"),
    "remove_listing": (lambda r, m, n: remove_reported_listing(r, m), "report_listing_removed", "listing removed"),
    "suspend_seller": (lambda r, m, n: suspend_reported_seller(r, m), "report_seller_suspended", "seller suspended"),
}


@admin_bp.route("/reports/<public_id>/action", methods=["POST"])
@login_required
@admin_required
def report_action(public_id: str):
    """Apply one moderator action to a report. Every action writes exactly one
    audit entry here — the service layer never duplicates audit code."""
    report = get_report_or_404(public_id)
    action = (request.form.get("action") or "").strip().lower()
    notes = (request.form.get("notes") or "").strip() or None

    if action == "warn_seller":
        reason = (request.form.get("warn_reason") or "").strip() or None
        message = (request.form.get("warn_message") or "").strip() or None
        notification = warn_reported_seller(report, current_user, reason=reason, message=message)
        if notification is None:
            flash("There is no seller to warn for this report.", "warning")
            return redirect(url_for("admin.report_detail", public_id=public_id))
        record_admin_action(current_user, "seller_warned", report, request=request,
                            details={"action": action, "reason": reason, "message": message,
                                     "seller_public_id": report.reported_user.public_id if report.reported_user else None})
        flash("Warning sent to the seller.", "success")
        return redirect(url_for("admin.report_detail", public_id=public_id))

    handler = _REPORT_ACTIONS.get(action)
    if handler is None:
        flash("Unknown moderator action.", "warning")
        return redirect(url_for("admin.report_detail", public_id=public_id))

    service_fn, audit_key, verb = handler
    # 1) apply action  2) save status (inside the service)  3) audit
    service_fn(report, current_user, notes)
    record_admin_action(current_user, audit_key, report, request=request,
                        details={"action": action, "notes": notes,
                                 "seller_public_id": report.reported_user.public_id if report.reported_user else None})

    # 4) notify reporter  5) notify reported user (only when an action was taken).
    # Runs after the moderation commit, so a delivery failure can't undo it.
    if action in REPORT_COMPLETION_ACTIONS:
        summary = notify_report_parties(report, action)
        flash(_review_banner(report, verb, summary), "success")
    else:
        flash(f"Report {report.public_id[:8]} {verb}.", "success")
    return redirect(url_for("admin.report_detail", public_id=public_id))


def _review_banner(report, verb, summary) -> Markup:
    """Build the moderator confirmation banner (reporter/reported notify status).

    Static, server-controlled copy — the only dynamic value is the report's short
    id — but every line is escaped before being marked safe for the flash body.
    """
    lines = [f"Report {report.public_id[:8]} {verb}."]
    if summary["reporter_notified"]:
        lines.append("✓ The reporter has been notified.")
    if summary["reported_action"]:
        if summary["reported_notified"]:
            lines.append("✓ The reported user has been notified.")
    else:
        lines.append("No notification sent to the reported user — no moderation action was taken.")
    return Markup("<br>").join(escape(line) for line in lines)
