"""Stats and post-details views."""

from __future__ import annotations

from datetime import UTC
from datetime import date
from datetime import datetime
from datetime import timedelta

from sqlalchemy import func
from sqlalchemy import select
from sqlalchemy import text
from sqlalchemy.orm import joinedload
from starlette.requests import Request
from starlette.responses import JSONResponse
from starlette.responses import Response

from fenliu.config import settings
from fenliu.database import get_db
from fenliu.models import ErrorHistory
from fenliu.models import HashtagStream
from fenliu.models import Post
from fenliu.models import QueueStats
from fenliu.models import QueueStatus
from fenliu.templates_env import templates
from fenliu.utils.display import process_post_for_display
from fenliu.utils.display import process_stream_for_display


async def stats_page(request: Request) -> Response:
    """Render the statistics page."""
    with get_db() as db:
        # Get basic statistics
        total_streams: int = db.scalar(select(func.count()).select_from(HashtagStream))
        active_streams: int = db.scalar(select(func.count()).select_from(HashtagStream).where(HashtagStream.active))
        total_posts: int = db.scalar(select(func.count()).select_from(Post))
        processed_posts: int = db.scalar(select(func.count()).select_from(Post).where(Post.reviewed))

        # Count posts with errors (active)
        posts_with_errors: int = db.scalar(
            select(func.count()).select_from(Post).where(Post.queue_status == QueueStatus.ERROR)
        )

        # Count historical errors from ErrorHistory table
        historical_errors: int = db.scalar(select(func.count()).select_from(ErrorHistory))
        all_time_errors = posts_with_errors + historical_errors

        # Load queue stats for all-time counts
        stats = db.get(QueueStats, 1)

        # Count delivered posts
        delivered_posts: int = db.scalar(
            select(func.count()).select_from(Post).where(Post.queue_status == QueueStatus.DELIVERED)
        )
        all_time_delivered = delivered_posts + (stats.total_deleted_delivered if stats else 0)

        # Get most frequent error reason (combining active + historical)
        most_frequent_error: str | None = None
        if all_time_errors > 0:
            # UNION active errors with historical errors, then find most common reason
            error_query = text("""
                SELECT error_reason, COUNT(*) as count FROM (
                    SELECT error_reason FROM posts WHERE queue_status = 'error' AND error_reason IS NOT NULL
                    UNION ALL
                    SELECT error_reason FROM error_history WHERE error_reason IS NOT NULL
                ) combined
                GROUP BY error_reason
                ORDER BY count DESC
                LIMIT 1
            """)
            error_result = db.execute(error_query).first()
            if error_result:
                most_frequent_error = error_result[0]

        # Calculate average spam score across all posts
        average_spam_score: float | None = db.scalar(select(func.avg(Post.spam_score)))

        # Get top hashtags by post count (including all hashtags from posts)
        top_hashtags_query = text("""
            SELECT
                MIN(json_each.value) as hashtag,
                COUNT(*) as post_count,
                MAX(hs.active) as active
            FROM posts, json_each(posts.hashtags)
            LEFT JOIN hashtag_streams hs ON hs.hashtag = LTRIM(json_each.value, '#') COLLATE NOCASE
            GROUP BY json_each.value COLLATE NOCASE
            ORDER BY post_count DESC
            LIMIT 10
        """)
        result = db.execute(top_hashtags_query)
        top_hashtags = []
        for row in result:
            # Strip the # prefix from hashtag if present
            hashtag = str(row[0]).lstrip("#")
            top_hashtags.append({"hashtag": hashtag, "post_count": row[1], "active": row[2]})

        # Generate posts over time data for last 7 days
        today: date = date.today()
        week_start = datetime.combine(today - timedelta(days=6), datetime.min.time()).replace(tzinfo=UTC)

        day_counts: dict[str, int] = {
            str(row[0]): int(row[1])
            for row in db.execute(
                select(func.date(Post.fetched_at), func.count())
                .where(Post.fetched_at >= week_start)
                .group_by(func.date(Post.fetched_at))
            ).all()
        }

        posts_over_time_data: list[int] = []
        posts_over_time_labels: list[str] = []
        for i in range(6, -1, -1):
            day_date = today - timedelta(days=i)
            posts_over_time_data.append(day_counts.get(str(day_date), 0))
            posts_over_time_labels.append(day_date.strftime("%a"))

        # Generate top hashtags chart data (top 5)
        top_hashtags_chart_data = []
        top_hashtags_chart_labels = []

        for hashtag in top_hashtags[:5]:
            top_hashtags_chart_labels.append(f"#{hashtag['hashtag']}")
            top_hashtags_chart_data.append(hashtag["post_count"])

        context = {
            "total_streams": total_streams,
            "active_streams": active_streams,
            "total_posts": total_posts,
            "processed_posts": processed_posts,
            "average_spam_score": average_spam_score,
            "top_hashtags": top_hashtags,
            "posts_over_time_data": posts_over_time_data,
            "posts_over_time_labels": posts_over_time_labels,
            "top_hashtags_chart_data": top_hashtags_chart_data,
            "top_hashtags_chart_labels": top_hashtags_chart_labels,
            "posts_with_errors": posts_with_errors,
            "all_time_errors": all_time_errors,
            "delivered_posts": delivered_posts,
            "all_time_delivered": all_time_delivered,
            "most_frequent_error": most_frequent_error,
            "app_name": settings.app_name,
            "app_version": settings.app_version,
        }
        return templates.TemplateResponse(request, "stats.html", context)


async def post_details(request: Request) -> Response:
    """Render the post details page."""
    with get_db() as db:
        try:
            post_id: int = int(request.path_params["post_id"])
        except (KeyError, ValueError):
            return JSONResponse({"detail": "Invalid post ID"}, status_code=400)

        stmt = select(Post).options(joinedload(Post.stream)).where(Post.id == post_id)
        result = db.execute(stmt)
        post: Post | None = result.scalar_one_or_none()

        if not post:
            return JSONResponse({"detail": f"Post {post_id} not found"}, status_code=404)

        # Process post using helper function
        post_dict: dict = process_post_for_display(post)

        # Get stream info if available
        if post.stream:
            post_dict["stream"] = process_stream_for_display(post.stream)

        context = {
            "post": post_dict,
            "app_name": settings.app_name,
            "app_version": settings.app_version,
        }
        return templates.TemplateResponse(request, "post_details.html", context)
