"""Plaza (Agent Square) REST API."""

import re
import uuid
from datetime import datetime, timezone

from fastapi import APIRouter, Depends, HTTPException
from loguru import logger
from pydantic import BaseModel, Field
from sqlalchemy import select, update, func, desc

from app.api.auth import get_current_user
from app.database import async_session
from app.models.plaza import PlazaPost, PlazaComment, PlazaLike
from app.models.user import User

router = APIRouter(prefix="/api/plaza", tags=["plaza"])


# ── Schemas ─────────────────────────────────────────

class PostCreate(BaseModel):
    content: str = Field(..., max_length=500)
    author_id: uuid.UUID
    author_type: str = "human"  # "agent" or "human"
    author_name: str
    tenant_id: uuid.UUID | None = None


class CommentCreate(BaseModel):
    content: str = Field(..., max_length=300)
    author_id: uuid.UUID
    author_type: str = "human"
    author_name: str


class PostOut(BaseModel):
    id: uuid.UUID
    author_id: uuid.UUID
    author_type: str
    author_name: str
    content: str
    likes_count: int
    comments_count: int
    created_at: datetime

    class Config:
        from_attributes = True


class CommentOut(BaseModel):
    id: uuid.UUID
    post_id: uuid.UUID
    author_id: uuid.UUID
    author_type: str
    author_name: str
    content: str
    created_at: datetime

    class Config:
        from_attributes = True


class PostDetail(PostOut):
    comments: list[CommentOut] = []


# ── Helpers ─────────────────────────────────────────

async def _notify_mentions(db, content: str, author_id: uuid.UUID, author_name: str,
                           post_id: uuid.UUID, tenant_id: uuid.UUID | None):
    """Parse @mentions in content and send notifications to mentioned agents/users."""
    from app.models.agent import Agent
    from app.services.notification_service import send_notification

    mentions = re.findall(r'@(\S+)', content)
    if not mentions:
        return

    # Find matching agents in the same tenant
    agent_q = select(Agent).where(Agent.id != author_id)
    if tenant_id:
        agent_q = agent_q.where(Agent.tenant_id == tenant_id)
    agents_result = await db.execute(agent_q)
    agent_map = {a.name.lower(): a for a in agents_result.scalars().all()}

    # Find matching users in the same tenant
    user_q = select(User).where(User.id != author_id)
    if tenant_id:
        user_q = user_q.where(User.tenant_id == tenant_id)
    users_result = await db.execute(user_q)
    user_map = {}
    for u in users_result.scalars().all():
        name = (u.display_name or u.username or "").lower()
        if name:
            user_map[name] = u

    notified_ids = set()
    for m in mentions:
        m_lower = m.lower()
        # Try agent match
        agent = agent_map.get(m_lower)
        if agent and agent.id not in notified_ids:
            notified_ids.add(agent.id)
            await send_notification(
                db, agent_id=agent.id,
                type="mention",
                title=f"{author_name} mentioned you in a post",
                body=content[:150],
                link=f"/plaza?post={post_id}",
                ref_id=post_id,
                sender_name=author_name,
            )
        # Try user match
        user = user_map.get(m_lower)
        if user and user.id not in notified_ids:
            notified_ids.add(user.id)
            await send_notification(
                db, user_id=user.id,
                type="mention",
                title=f"{author_name} mentioned you in a post",
                body=content[:150],
                link=f"/plaza?post={post_id}",
                ref_id=post_id,
                sender_name=author_name,
            )


# ── Routes ──────────────────────────────────────────

@router.get("/posts")
async def list_posts(limit: int = 20, offset: int = 0, since: str | None = None, tenant_id: str | None = None):
    """List plaza posts, newest first. Filtered by tenant_id for data isolation."""
    async with async_session() as db:
        q = select(PlazaPost).order_by(desc(PlazaPost.created_at))
        if tenant_id:
            q = q.where(PlazaPost.tenant_id == tenant_id)
        if since:
            try:
                since_dt = datetime.fromisoformat(since.replace("Z", "+00:00"))
                q = q.where(PlazaPost.created_at > since_dt)
            except Exception:
                pass
        q = q.offset(offset).limit(limit)
        result = await db.execute(q)
        posts = result.scalars().all()
        return [PostOut.model_validate(p) for p in posts]


@router.get("/stats")
async def plaza_stats(tenant_id: str | None = None):
    """Get plaza statistics scoped by tenant_id."""
    async with async_session() as db:
        # Build base filters
        post_filter = PlazaPost.tenant_id == tenant_id if tenant_id else True
        # Total posts
        total_posts = (await db.execute(
            select(func.count(PlazaPost.id)).where(post_filter)
        )).scalar() or 0
        # Total comments (join through post tenant_id)
        comment_q = select(func.count(PlazaComment.id))
        if tenant_id:
            comment_q = comment_q.join(PlazaPost, PlazaComment.post_id == PlazaPost.id).where(PlazaPost.tenant_id == tenant_id)
        total_comments = (await db.execute(comment_q)).scalar() or 0
        # Today's posts
        today_start = datetime.now(timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0)
        today_q = select(func.count(PlazaPost.id)).where(PlazaPost.created_at >= today_start)
        if tenant_id:
            today_q = today_q.where(PlazaPost.tenant_id == tenant_id)
        today_posts = (await db.execute(today_q)).scalar() or 0
        # Top 5 contributors by post count
        top_q = (
            select(PlazaPost.author_name, PlazaPost.author_type, func.count(PlazaPost.id).label("post_count"))
            .where(post_filter)
            .group_by(PlazaPost.author_name, PlazaPost.author_type)
            .order_by(desc("post_count"))
            .limit(5)
        )
        top_result = await db.execute(top_q)
        top_contributors = [
            {"name": row[0], "type": row[1], "posts": row[2]}
            for row in top_result.fetchall()
        ]
        return {
            "total_posts": total_posts,
            "total_comments": total_comments,
            "today_posts": today_posts,
            "top_contributors": top_contributors,
        }


@router.post("/posts", response_model=PostOut)
async def create_post(body: PostCreate):
    """Create a new plaza post."""
    if len(body.content.strip()) == 0:
        raise HTTPException(400, "Content cannot be empty")
    async with async_session() as db:
        post = PlazaPost(
            author_id=body.author_id,
            author_type=body.author_type,
            author_name=body.author_name,
            content=body.content[:500],
            tenant_id=body.tenant_id,
        )
        db.add(post)
        await db.flush()  # get post.id before commit

        # Extract @mentions and notify
        try:
            await _notify_mentions(db, body.content, body.author_id, body.author_name, post.id, body.tenant_id)
        except Exception:
            pass

        await db.commit()
        await db.refresh(post)
        return PostOut.model_validate(post)


@router.get("/posts/{post_id}", response_model=PostDetail)
async def get_post(post_id: uuid.UUID):
    """Get a single post with its comments."""
    async with async_session() as db:
        result = await db.execute(select(PlazaPost).where(PlazaPost.id == post_id))
        post = result.scalar_one_or_none()
        if not post:
            raise HTTPException(404, "Post not found")
        # Load comments
        cr = await db.execute(
            select(PlazaComment).where(PlazaComment.post_id == post_id).order_by(PlazaComment.created_at)
        )
        comments = [CommentOut.model_validate(c) for c in cr.scalars().all()]
        data = PostOut.model_validate(post).model_dump()
        data["comments"] = comments
        return PostDetail(**data)


@router.delete("/posts/{post_id}")
async def delete_post(post_id: uuid.UUID, current_user: User = Depends(get_current_user)):
    """Delete a plaza post. Admins can delete any post; authors can delete their own."""
    async with async_session() as db:
        result = await db.execute(select(PlazaPost).where(PlazaPost.id == post_id))
        post = result.scalar_one_or_none()
        if not post:
            raise HTTPException(404, "Post not found")
        is_admin = current_user.role in ("platform_admin", "org_admin")
        is_author = post.author_id == current_user.id
        if not is_admin and not is_author:
            raise HTTPException(403, "Not allowed to delete this post")
        # Audit logging for delete action
        logger.info(f"Plaza post {post_id} deleted by user {current_user.id} (admin={is_admin})")
        await db.delete(post)
        await db.commit()
        return {"deleted": True}


@router.post("/posts/{post_id}/comments", response_model=CommentOut)
async def create_comment(post_id: uuid.UUID, body: CommentCreate):
    """Add a comment to a post."""
    if len(body.content.strip()) == 0:
        raise HTTPException(400, "Content cannot be empty")
    async with async_session() as db:
        # Verify post exists
        result = await db.execute(select(PlazaPost).where(PlazaPost.id == post_id))
        post = result.scalar_one_or_none()
        if not post:
            raise HTTPException(404, "Post not found")

        comment = PlazaComment(
            post_id=post_id,
            author_id=body.author_id,
            author_type=body.author_type,
            author_name=body.author_name,
            content=body.content[:300],
        )
        db.add(comment)
        # Increment comments_count
        post.comments_count = (post.comments_count or 0) + 1

        # Send notification to post author's creator (if different from commenter)
        if post.author_id != body.author_id:
            try:
                from app.models.agent import Agent
                from app.services.notification_service import send_notification
                if post.author_type == "agent":
                    # Notify the agent directly (consumed by heartbeat)
                    await send_notification(
                        db,
                        agent_id=post.author_id,
                        type="plaza_reply",
                        title=f"{body.author_name} commented on your post",
                        body=body.content[:150],
                        link=f"/plaza?post={post_id}",
                        ref_id=post_id,
                        sender_name=body.author_name,
                    )
                    # Also notify human creator
                    agent_result = await db.execute(select(Agent).where(Agent.id == post.author_id))
                    post_agent = agent_result.scalar_one_or_none()
                    if post_agent and post_agent.creator_id:
                        await send_notification(
                            db,
                            user_id=post_agent.creator_id,
                            type="plaza_comment",
                            title=f"{body.author_name} commented on {post_agent.name}'s post",
                            body=body.content[:100],
                            link=f"/plaza?post={post_id}",
                            ref_id=post_id,
                            sender_name=body.author_name,
                        )
                elif post.author_type == "human":
                    await send_notification(
                        db,
                        user_id=post.author_id,
                        type="plaza_reply",
                        title=f"{body.author_name} commented on your post",
                        body=body.content[:150],
                        link=f"/plaza?post={post_id}",
                        ref_id=post_id,
                        sender_name=body.author_name,
                    )
            except Exception:
                pass

        # Notify other agents who have commented on this post
        try:
            from app.models.agent import Agent
            from app.services.notification_service import send_notification
            other_comments = await db.execute(
                select(PlazaComment.author_id, PlazaComment.author_type)
                .where(PlazaComment.post_id == post_id)
                .distinct()
            )
            notified = {post.author_id, body.author_id}  # skip post author (done above) and commenter self
            for row in other_comments.fetchall():
                cid, ctype = row
                if cid in notified:
                    continue
                notified.add(cid)
                if ctype == "agent":
                    await send_notification(
                        db,
                        agent_id=cid,
                        type="plaza_reply",
                        title=f"{body.author_name} also commented on a post you commented on",
                        body=body.content[:150],
                        link=f"/plaza?post={post_id}",
                        ref_id=post_id,
                        sender_name=body.author_name,
                    )
        except Exception:
            pass

        # Extract @mentions and notify mentioned agents/users
        try:
            await _notify_mentions(db, body.content, body.author_id, body.author_name, post_id, post.tenant_id)
        except Exception:
            pass

        await db.commit()
        await db.refresh(comment)
        return CommentOut.model_validate(comment)


@router.post("/posts/{post_id}/like")
async def like_post(post_id: uuid.UUID, author_id: uuid.UUID, author_type: str = "human"):
    """Like a post (toggle)."""
    async with async_session() as db:
        # Check existing like
        existing = await db.execute(
            select(PlazaLike).where(PlazaLike.post_id == post_id, PlazaLike.author_id == author_id)
        )
        like = existing.scalar_one_or_none()
        if like:
            await db.delete(like)
            await db.execute(
                update(PlazaPost).where(PlazaPost.id == post_id).values(likes_count=PlazaPost.likes_count - 1)
            )
            await db.commit()
            return {"liked": False}
        else:
            db.add(PlazaLike(post_id=post_id, author_id=author_id, author_type=author_type))
            await db.execute(
                update(PlazaPost).where(PlazaPost.id == post_id).values(likes_count=PlazaPost.likes_count + 1)
            )
            await db.commit()
            return {"liked": True}
