from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy import func
from sqlalchemy.orm import Session, joinedload

from app.database import get_db
from app.dependencies import require_user_token
from app.models import User
from app.models_admin import CommunityPost, CommunityPostComment, CommunityPostLike, CommunityPostShare
from app.schemas import (
    CommunityPostCommentIdRequest,
    CommunityPostCommentRequest,
    CommunityPostIdRequest,
    CommunityPostListResponse,
    CommunityPostOut,
    CommunityPostShareRequest,
)
from app.services.admin_service import (
    list_community_posts as fetch_community_posts,
    serialize_community_comment,
    serialize_community_post,
)
from app.services.admin_notification_service import (
    notify_admin_community_comment,
    notify_admin_community_like,
    notify_admin_community_share,
)

router = APIRouter(tags=["user-community"])


def _post_with_engagement(db: Session, post: CommunityPost, user_id: int) -> dict:
    data = serialize_community_post(post)
    likes_count = (
        db.query(func.count(CommunityPostLike.id))
        .filter(CommunityPostLike.post_id == post.id)
        .scalar()
        or 0
    )
    comments_count = (
        db.query(func.count(CommunityPostComment.id))
        .filter(CommunityPostComment.post_id == post.id)
        .scalar()
        or 0
    )
    shares_count = (
        db.query(func.count(CommunityPostShare.id))
        .filter(CommunityPostShare.post_id == post.id)
        .scalar()
        or 0
    )
    share_rows = (
        db.query(CommunityPostShare.platform, func.count(CommunityPostShare.id))
        .filter(CommunityPostShare.post_id == post.id)
        .group_by(CommunityPostShare.platform)
        .all()
    )
    liked_by_me = (
        db.query(CommunityPostLike.id)
        .filter(CommunityPostLike.post_id == post.id, CommunityPostLike.user_id == user_id)
        .first()
        is not None
    )
    comments = (
        db.query(CommunityPostComment)
        .options(joinedload(CommunityPostComment.user))
        .filter(CommunityPostComment.post_id == post.id)
        .order_by(CommunityPostComment.created_at.asc(), CommunityPostComment.id.asc())
        .all()
    )
    data["likes_count"] = int(likes_count)
    data["comments_count"] = int(comments_count)
    data["shares_count"] = int(shares_count)
    data["share_counts"] = {str(platform): int(count) for platform, count in share_rows}
    data["liked_by_me"] = liked_by_me
    data["comments"] = [serialize_community_comment(comment) for comment in comments]
    return data


def _posts_with_engagement(db: Session, posts: list[CommunityPost], user_id: int) -> list[dict]:
    if not posts:
        return []

    post_ids = [post.id for post in posts]
    likes_by_post = dict(
        db.query(CommunityPostLike.post_id, func.count(CommunityPostLike.id))
        .filter(CommunityPostLike.post_id.in_(post_ids))
        .group_by(CommunityPostLike.post_id)
        .all()
    )
    comments_by_post = dict(
        db.query(CommunityPostComment.post_id, func.count(CommunityPostComment.id))
        .filter(CommunityPostComment.post_id.in_(post_ids))
        .group_by(CommunityPostComment.post_id)
        .all()
    )
    shares_by_post = dict(
        db.query(CommunityPostShare.post_id, func.count(CommunityPostShare.id))
        .filter(CommunityPostShare.post_id.in_(post_ids))
        .group_by(CommunityPostShare.post_id)
        .all()
    )
    liked_post_ids = {
        post_id
        for (post_id,) in db.query(CommunityPostLike.post_id)
        .filter(
            CommunityPostLike.post_id.in_(post_ids),
            CommunityPostLike.user_id == user_id,
        )
        .all()
    }
    share_counts: dict[int, dict[str, int]] = {post_id: {} for post_id in post_ids}
    for post_id, platform, count in (
        db.query(
            CommunityPostShare.post_id,
            CommunityPostShare.platform,
            func.count(CommunityPostShare.id),
        )
        .filter(CommunityPostShare.post_id.in_(post_ids))
        .group_by(CommunityPostShare.post_id, CommunityPostShare.platform)
        .all()
    ):
        share_counts.setdefault(post_id, {})[str(platform)] = int(count)

    comments_by_post_id: dict[int, list[CommunityPostComment]] = {post_id: [] for post_id in post_ids}
    all_comments = (
        db.query(CommunityPostComment)
        .options(joinedload(CommunityPostComment.user))
        .filter(CommunityPostComment.post_id.in_(post_ids))
        .order_by(
            CommunityPostComment.post_id.asc(),
            CommunityPostComment.created_at.asc(),
            CommunityPostComment.id.asc(),
        )
        .all()
    )
    for comment in all_comments:
        comments_by_post_id.setdefault(comment.post_id, []).append(comment)

    serialized: list[dict] = []
    for post in posts:
        data = serialize_community_post(post)
        data["likes_count"] = int(likes_by_post.get(post.id, 0))
        data["comments_count"] = int(comments_by_post.get(post.id, 0))
        data["shares_count"] = int(shares_by_post.get(post.id, 0))
        data["share_counts"] = share_counts.get(post.id, {})
        data["liked_by_me"] = post.id in liked_post_ids
        data["comments"] = [
            serialize_community_comment(comment)
            for comment in comments_by_post_id.get(post.id, [])
        ]
        serialized.append(data)
    return serialized


@router.get(
    "/list-community-posts",
    response_model=CommunityPostListResponse,
    summary="List community posts for the user app (newest first)",
)
def list_community_posts(
    db: Session = Depends(get_db),
    user: User = Depends(require_user_token),
):
    posts = fetch_community_posts(db)
    serialized = _posts_with_engagement(db, posts, user.id)
    return CommunityPostListResponse(total=len(serialized), posts=serialized)


@router.post(
    "/toggle-community-post-like",
    response_model=CommunityPostOut,
    summary="Like or unlike a community post",
)
def toggle_community_post_like(
    body: CommunityPostIdRequest,
    db: Session = Depends(get_db),
    user: User = Depends(require_user_token),
):
    post = db.get(CommunityPost, body.post_id)
    if post is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Community post not found")

    existing = (
        db.query(CommunityPostLike)
        .filter(CommunityPostLike.post_id == body.post_id, CommunityPostLike.user_id == user.id)
        .first()
    )
    if existing is not None:
        db.delete(existing)
    else:
        db.add(CommunityPostLike(post_id=body.post_id, user_id=user.id))
        db.flush()
        notify_admin_community_like(
            db,
            user,
            post_id=post.id,
            post_title=post.title or "",
        )

    db.commit()
    db.refresh(post)
    return _post_with_engagement(db, post, user.id)


@router.post(
    "/add-community-post-comment",
    response_model=CommunityPostOut,
    summary="Add a comment to a community post",
)
def add_community_post_comment(
    body: CommunityPostCommentRequest,
    db: Session = Depends(get_db),
    user: User = Depends(require_user_token),
):
    post = db.get(CommunityPost, body.post_id)
    if post is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Community post not found")

    comment_text = body.comment.strip()
    if not comment_text:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Comment is required")

    db.add(CommunityPostComment(post_id=body.post_id, user_id=user.id, comment=comment_text))
    db.flush()
    notify_admin_community_comment(
        db,
        user,
        post_id=post.id,
        post_title=post.title or "",
        comment_preview=comment_text,
    )
    db.commit()
    db.refresh(post)
    return _post_with_engagement(db, post, user.id)


@router.post(
    "/delete-community-comment",
    response_model=CommunityPostOut,
    summary="Delete your own comment on a community post",
    name="delete_own_community_post_comment",
)
@router.post(
    "/delete-community-post-comment",
    response_model=CommunityPostOut,
    summary="Delete your own comment on a community post (alias)",
    include_in_schema=False,
)
def delete_own_community_post_comment(
    body: CommunityPostCommentIdRequest,
    db: Session = Depends(get_db),
    user: User = Depends(require_user_token),
):
    row = db.get(CommunityPostComment, body.comment_id)
    if row is None and body.post_id is not None:
        row = (
            db.query(CommunityPostComment)
            .filter(
                CommunityPostComment.id == body.comment_id,
                CommunityPostComment.post_id == body.post_id,
            )
            .first()
        )
    if row is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Comment not found")
    if body.post_id is not None and row.post_id != body.post_id:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Comment not found")
    if row.user_id != user.id:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="You can only delete your own comments",
        )

    post = db.get(CommunityPost, row.post_id)
    if post is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Community post not found")

    db.delete(row)
    db.commit()
    db.refresh(post)
    return _post_with_engagement(db, post, user.id)


@router.post(
    "/share-community-post",
    response_model=CommunityPostOut,
    summary="Record a community post share by platform",
)
def share_community_post(
    body: CommunityPostShareRequest,
    db: Session = Depends(get_db),
    user: User = Depends(require_user_token),
):
    post = db.get(CommunityPost, body.post_id)
    if post is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Community post not found")

    db.add(CommunityPostShare(post_id=body.post_id, user_id=user.id, platform=body.platform))
    db.flush()
    notify_admin_community_share(
        db,
        user,
        post_id=post.id,
        post_title=post.title or "",
        platform=body.platform,
    )
    db.commit()
    db.refresh(post)
    return _post_with_engagement(db, post, user.id)
