"""Seller dashboard analytics (Premium Seller Analytics).

A dedicated, read-only service that assembles the "Premium Insights" panel on the
Seller Dashboard. Everything is computed here (never in templates), reusing the
existing batched aggregations (``get_seller_overview``, ``get_offers_by_listing``)
and the **cached** ``UserReputation`` for completed sales / average rating — no
duplicated calculations, no per-listing N+1.

The return shape is a plain dict with clearly-named sections so future additions
(revenue, repeat buyers, response-time trends, profile views, charts, CSV export)
can be added as new keys without touching callers.
"""

from __future__ import annotations

from datetime import timedelta

from sqlalchemy import func
from sqlalchemy.orm import selectinload

from app.extensions import db
from app.models import (
    CompletedTransaction,
    Conversation,
    Listing,
    Offer,
    Review,
    User,
)
from app.models.base import utcnow

# Performance score weights — deeper engagement counts for more, mirroring the
# discovery engine's weighting (favorite < message < offer < sale). Documented so
# the "best performing listing" ranking is transparent and deterministic.
W_VIEW, W_FAVORITE, W_MESSAGE, W_OFFER, W_SALE = 1, 4, 5, 8, 50

# Cap the performance table so a prolific seller's dashboard stays lightweight.
PERFORMANCE_TABLE_LIMIT = 20


def _percentage(part: int, whole: int) -> float:
    """Safe percentage (guards divide-by-zero), rounded to one decimal."""
    return round(part / whole * 100, 1) if whole else 0.0


def _messages_by_listing(listing_ids) -> dict[int, int]:
    if not listing_ids:
        return {}
    return {
        lid: int(c) for lid, c in db.session.execute(
            db.select(Conversation.listing_id, func.count())
            .where(Conversation.listing_id.in_(listing_ids))
            .group_by(Conversation.listing_id)
        ).all()
    }


def _completed_by_listing(listing_ids) -> dict[int, int]:
    """``{listing_id: transaction_id}`` for the seller's finalised sales (unique)."""
    if not listing_ids:
        return {}
    return {
        lid: tid for lid, tid in db.session.execute(
            db.select(CompletedTransaction.listing_id, CompletedTransaction.id)
            .where(CompletedTransaction.listing_id.in_(listing_ids))
        ).all()
    }


def _seller_ratings_by_transaction(seller_id: int, transaction_ids) -> dict[int, int]:
    """The rating the seller received per completed transaction (buyer's review)."""
    ids = [t for t in transaction_ids if t is not None]
    if not ids:
        return {}
    return {
        tid: int(rating) for tid, rating in db.session.execute(
            db.select(Review.transaction_id, Review.rating)
            .where(Review.reviewee_id == seller_id, Review.transaction_id.in_(ids))
        ).all()
    }


def _recommendations(listings, rows, overview, rep) -> list[str]:
    """Up to three deterministic, data-driven insights (ordered by usefulness)."""
    recs: list[str] = []
    total_views = overview["total_views"]
    total_listings = overview["total_listings"]

    # 1) Category that over-performs the seller's own average views/listing.
    cat_stats: dict[str, list[int]] = {}
    for listing in listings:
        name = listing.category.name if listing.category else "Other"
        acc = cat_stats.setdefault(name, [0, 0])
        acc[0] += listing.view_count or 0
        acc[1] += 1
    if len(cat_stats) >= 2 and total_listings and total_views:
        overall_avg = total_views / total_listings
        best_name, (best_views, best_count) = max(
            cat_stats.items(), key=lambda kv: (kv[1][0] / kv[1][1]) if kv[1][1] else 0
        )
        cat_avg = best_views / best_count if best_count else 0
        if overall_avg and cat_avg > overall_avg:
            pct = round((cat_avg / overall_avg - 1) * 100)
            if pct >= 10:
                recs.append(f"Your {best_name} listings receive {pct}% more views than your average.")

    # 2) Highest-performing listing.
    if rows and rows[0]["score"] > 0:
        recs.append(f"Your highest-performing listing is “{rows[0]['title']}”.")

    # 3) Strong rating (cached reputation).
    if rep and rep.review_count > 0 and rep.rating >= 4.5:
        recs.append(f"Buyers rate you {rep.rating:.1f}★ — excellent for building trust.")

    # 4) Excellent response time (only when response tracking has data).
    if rep and rep.average_response_minutes is not None and rep.average_response_minutes <= 30:
        recs.append("Your average response time is excellent.")

    # 5) High save-rate signals strong interest.
    save_rate = _percentage(overview["total_favorites"], total_views)
    if save_rate >= 15:
        recs.append(f"{round(save_rate)}% of viewers save your listings — strong buyer interest.")

    return recs[:3]


def get_dashboard_analytics(user: User) -> dict:
    """Assemble the seller's Premium Insights.

    Reuses ``get_seller_overview`` (totals + weekly) and the cached
    ``UserReputation`` (completed sales, average rating), then adds per-listing
    performance via a handful of batched GROUP BY queries — never one per listing.
    Always returns a dict; ``has_data`` is False for a seller with no listings so
    the UI can show the new-seller empty state.
    """
    from app.marketplace.services import get_offers_by_listing, get_seller_overview
    from app.reviews.reputation import get_reputation

    overview = get_seller_overview(user)
    rep = get_reputation(user.id)  # cached; may be None
    completed_sales = rep.completed_sales if rep else 0
    average_rating = rep.rating if rep else 0.0
    total_views = overview["total_views"]

    summary = {
        "listing_views": total_views,
        "favorites": overview["total_favorites"],
        "messages": overview["total_conversations"],
        "offers": overview["total_offers"],
        "completed_sales": completed_sales,
        "average_rating": average_rating,
        "review_count": rep.review_count if rep else 0,
        # Conversion Rate = completed sales ÷ listing views (guarded).
        "conversion_rate": _percentage(completed_sales, total_views),
    }

    now = utcnow()
    week_ago = now - timedelta(days=7)
    offers_7d = db.session.scalar(
        db.select(func.count(Offer.id)).where(Offer.seller_id == user.id, Offer.created_at >= week_ago)
    ) or 0
    weekly = {
        "views": overview["views_7d"],
        "favorites": overview["favorites_7d"],
        "messages": overview["conversations_7d"],
        "offers": int(offers_7d),
    }

    if overview["total_listings"] == 0:
        return {"has_data": False, "summary": summary, "weekly": weekly,
                "best_listing": None, "listings": [], "recommendations": []}

    listings = db.session.scalars(
        db.select(Listing)
        .options(selectinload(Listing.images), selectinload(Listing.category))
        .where(Listing.seller_id == user.id, Listing.deleted_at.is_(None))
    ).all()
    ids = [listing.id for listing in listings]

    offers_map = get_offers_by_listing(ids)
    messages_map = _messages_by_listing(ids)
    completed_map = _completed_by_listing(ids)
    ratings_map = _seller_ratings_by_transaction(user.id, completed_map.values())

    rows = []
    for listing in listings:
        views = listing.view_count or 0
        favourites = listing.favorite_count or 0
        messages = messages_map.get(listing.id, 0)
        offers = offers_map.get(listing.id, 0)
        transaction_id = completed_map.get(listing.id)
        completed = transaction_id is not None
        score = views * W_VIEW + favourites * W_FAVORITE + messages * W_MESSAGE \
            + offers * W_OFFER + (W_SALE if completed else 0)
        rows.append({
            "public_id": listing.public_id,
            "title": listing.title,
            "cover": listing.images[0].image_path if listing.images else None,
            "views": views,
            "favorites": favourites,
            "messages": messages,
            "offers": offers,
            "status": listing.status.value,
            "completed_sale": completed,
            "rating": ratings_map.get(transaction_id) if completed else None,
            # Per-listing conversion: offers per view (interest conversion), guarded.
            "conversion": _percentage(offers, views),
            "score": score,
        })
    rows.sort(key=lambda r: (r["score"], r["views"]), reverse=True)

    best_listing = rows[0] if rows and rows[0]["score"] > 0 else None

    return {
        "has_data": True,
        "summary": summary,
        "weekly": weekly,
        "best_listing": best_listing,
        "listings": rows[:PERFORMANCE_TABLE_LIMIT],
        "recommendations": _recommendations(listings, rows, overview, rep),
    }
