# views/forum.py
from snek.system.view import BaseView
from aiohttp import web
import json



from aiohttp import web

from snek.system.view import BaseView


class ForumIndexView(BaseView):
    
    login_required = True
    
    async def get(self):
        if self.login_required and not self.session.get("logged_in"):
            return web.HTTPFound("/")
        channel = await self.services.channel.get(
            uid=self.request.match_info.get("channel")
        )
        if not channel:
            user = await self.services.user.get(
                uid=self.request.match_info.get("channel")
            )
            if user:
                channel = await self.services.channel.get_dm(
                    self.session.get("uid"), user["uid"]
                )
                if channel:
                    return web.HTTPFound("/channel/{}.html".format(channel["uid"]))
        if not channel:
            return web.HTTPNotFound()

        channel_member = await self.app.services.channel_member.get(
            user_uid=self.session.get("uid"), channel_uid=channel["uid"]
        )
        if not channel_member:
            if not channel["is_private"]:
                channel_member = await self.app.services.channel_member.create(
                    channel_uid=channel["uid"],
                    user_uid=self.session.get("uid"),
                    is_moderator=False,
                    is_read_only=False,
                    is_muted=False,
                    is_banned=False,
                )

            return web.HTTPNotFound()

        channel_member["new_count"] = 0
        await self.app.services.channel_member.save(channel_member)

        user = await self.services.user.get(uid=self.session.get("uid"))

        messages = [
            await self.app.services.channel_message.to_extended_dict(message)
            for message in await self.app.services.channel_message.offset(
                channel["uid"]
            )
        ]
        for message in messages:
            await self.app.services.notification.mark_as_read(
                self.session.get("uid"), message["uid"]
            )
        name = await channel_member.get_name()
        return await self.render_template(
            "forum.html",
            {"name": name, "channel": channel, "user": user, "messages": messages},
        )





class ForumView(BaseView):
    """REST API endpoints for forum"""

    login_required = True

    async def get_forums(self):
        request = self 
        self = request.app
        """GET /forum/api/forums - Get all active forums"""
        forums = []
        async for forum in self.services.forum.get_active_forums():
            forums.append({
                "uid": forum["uid"],
                "name": forum["name"],
                "description": forum["description"],
                "slug": forum["slug"],
                "icon": forum["icon"],
                "thread_count": forum["thread_count"],
                "post_count": forum["post_count"],
                "last_post_at": forum["last_post_at"],
                "last_thread_uid": forum["last_thread_uid"]
            })
        return web.json_response({"forums": forums})

    async def get_forum(self):
        request = self 
        self = request.app
        setattr(self, "request", request)
        """GET /forum/api/forums/:slug - Get forum by slug"""
        slug = self.request.match_info["slug"]
        forum = await self.services.forum.get(slug=slug, is_active=True)
        
        if not forum:
            return web.json_response({"error": "Forum not found"}, status=404)
        
        # Get threads
        threads = []
        page = int(self.request.query.get("page", 1))
        limit = 50
        offset = (page - 1) * limit
        
        async for thread in forum.get_threads(limit=limit, offset=offset):
            # Get author info
            author = await self.services.user.get(uid=thread["created_by_uid"])
            last_post_author = None
            if thread["last_post_by_uid"]:
                last_post_author = await self.services.user.get(uid=thread["last_post_by_uid"])
            
            threads.append({
                "uid": thread["uid"],
                "title": thread["title"],
                "slug": thread["slug"],
                "is_pinned": thread["is_pinned"],
                "is_locked": thread["is_locked"],
                "view_count": thread["view_count"],
                "post_count": thread["post_count"],
                "created_at": thread["created_at"],
                "last_post_at": thread["last_post_at"],
                "author": {
                    "uid": author["uid"],
                    "username": author["username"],
                    "nick": author["nick"],
                    "color": author["color"]
                },
                "last_post_author": {
                    "uid": last_post_author["uid"],
                    "username": last_post_author["username"],
                    "nick": last_post_author["nick"],
                    "color": last_post_author["color"]
                } if last_post_author else None
            })
        
        return web.json_response({
            "forum": {
                "uid": forum["uid"],
                "name": forum["name"],
                "description": forum["description"],
                "slug": forum["slug"],
                "icon": forum["icon"],
                "thread_count": forum["thread_count"],
                "post_count": forum["post_count"]
            },
            "threads": threads,
            "page": page,
            "hasMore": len(threads) == limit
        })

    async def create_thread(self):
        request = self 
        self = request.app
        
        setattr(self, "request", request)
        """POST /forum/api/forums/:slug/threads - Create new thread"""
        if not self.request.session.get("logged_in"):
            return web.json_response({"error": "Unauthorized"}, status=401)
        
        slug = self.request.match_info["slug"]
        forum = await self.services.forum.get(slug=slug, is_active=True)
        
        if not forum:
            return web.json_response({"error": "Forum not found"}, status=404)
        
        data = await self.request.json()
        title = data.get("title", "").strip()
        content = data.get("content", "").strip()
        
        if not title or not content:
            return web.json_response({"error": "Title and content required"}, status=400)
        
        try:
            thread, post = await self.services.thread.create_thread(
                forum_uid=forum["uid"],
                title=title,
                content=content,
                created_by_uid=self.request.session["uid"]
            )
            
            return web.json_response({
                "thread": {
                    "uid": thread["uid"],
                    "slug": thread["slug"],
                    "forum_slug": forum["slug"]
                }
            })
        except Exception as e:
            return web.json_response({"error": str(e)}, status=400)

    async def get_thread(self):
        request = self 
        self = request.app

        setattr(self, "request", request)
        """GET /forum/api/threads/:thread_slug - Get thread with posts"""
        thread_slug = self.request.match_info["thread_slug"]
        thread = await self.services.thread.get(slug=thread_slug)
        
        if not thread:
            return web.json_response({"error": "Thread not found"}, status=404)
        
        # Increment view count
        await thread.increment_view_count()
        
        # Get forum
        forum = await self.services.forum.get(uid=thread["forum_uid"])
        
        # Get posts
        posts = []
        page = int(self.request.query.get("page", 1))
        limit = 50
        offset = (page - 1) * limit
        
        current_user_uid = self.request.session.get("uid")
        
        async for post in thread.get_posts(limit=limit, offset=offset):
            author = await post.get_author()
            is_liked = False
            if current_user_uid:
                is_liked = await post.is_liked_by(current_user_uid)
            
            posts.append({
                "uid": post["uid"],
                "content": post["content"],
                "created_at": post["created_at"],
                "edited_at": post["edited_at"],
                "is_first_post": post["is_first_post"],
                "like_count": post["like_count"],
                "is_liked": is_liked,
                "author": {
                    "uid": author["uid"],
                    "username": author["username"],
                    "nick": author["nick"],
                    "color": author["color"]
                }
            })
        
        # Get thread author
        thread_author = await self.services.user.get(uid=thread["created_by_uid"])
        
        return web.json_response({
            "thread": {
                "uid": thread["uid"],
                "title": thread["title"],
                "slug": thread["slug"],
                "is_pinned": thread["is_pinned"],
                "is_locked": thread["is_locked"],
                "view_count": thread["view_count"],
                "post_count": thread["post_count"],
                "created_at": thread["created_at"],
                "author": {
                    "uid": thread_author["uid"],
                    "username": thread_author["username"],
                    "nick": thread_author["nick"],
                    "color": thread_author["color"]
                }
            },
            "forum": {
                "uid": forum["uid"],
                "name": forum["name"],
                "slug": forum["slug"]
            },
            "posts": posts,
            "page": page,
            "hasMore": len(posts) == limit
        })

    async def create_post(self):
        request = self 
        self = request.app

        setattr(self, "request", request)
        """POST /forum/api/threads/:thread_uid/posts - Create new post"""
        if not self.request.session.get("logged_in"):
            return web.json_response({"error": "Unauthorized"}, status=401)
        
        thread_uid = self.request.match_info["thread_uid"]
        thread = await self.services.thread.get(uid=thread_uid)
        
        if not thread:
            return web.json_response({"error": "Thread not found"}, status=404)
        
        data = await self.request.json()
        content = data.get("content", "").strip()
        
        if not content:
            return web.json_response({"error": "Content required"}, status=400)
        
        try:
            post = await self.services.post.create_post(
                thread_uid=thread["uid"],
                forum_uid=thread["forum_uid"],
                content=content,
                created_by_uid=self.request.session["uid"]
            )
            
            author = await post.get_author()
            
            return web.json_response({
                "post": {
                    "uid": post["uid"],
                    "content": post["content"],
                    "created_at": post["created_at"],
                    "like_count": post["like_count"],
                    "is_liked": False,
                    "author": {
                        "uid": author["uid"],
                        "username": author["username"],
                        "nick": author["nick"],
                        "color": author["color"]
                    }
                }
            })
        except Exception as e:
            return web.json_response({"error": str(e)}, status=400)

    async def edit_post(self):
        request = self 
        self = request.app

        setattr(self, "request", request)
        """PUT /forum/api/posts/:post_uid - Edit post"""
        if not self.request.session.get("logged_in"):
            return web.json_response({"error": "Unauthorized"}, status=401)
        
        post_uid = self.request.match_info["post_uid"]
        data = await self.request.json()
        content = data.get("content", "").strip()
        
        if not content:
            return web.json_response({"error": "Content required"}, status=400)
        
        post = await self.services.post.edit_post(
            post_uid=post_uid,
            content=content,
            user_uid=self.request.session["uid"]
        )
        
        if not post:
            return web.json_response({"error": "Cannot edit post"}, status=403)
        
        return web.json_response({
            "post": {
                "uid": post["uid"],
                "content": post["content"],
                "edited_at": post["edited_at"]
            }
        })

    async def delete_post(self):
        request = self 
        self = request.app

        setattr(self, "request", request)
        """DELETE /forum/api/posts/:post_uid - Delete post"""
        if not self.request.session.get("logged_in"):
            return web.json_response({"error": "Unauthorized"}, status=401)
        
        post_uid = self.request.match_info["post_uid"]
        
        success = await self.services.post.delete_post(
            post_uid=post_uid,
            user_uid=self.request.session["uid"]
        )
        
        if not success:
            return web.json_response({"error": "Cannot delete post"}, status=403)
        
        return web.json_response({"success": True})

    async def toggle_like(self):
        request = self 
        self = request.app

        setattr(self, "request", request)
        """POST /forum/api/posts/:post_uid/like - Toggle post like"""
        if not self.request.session.get("logged_in"):
            return web.json_response({"error": "Unauthorized"}, status=401)
        
        post_uid = self.request.match_info["post_uid"]
        
        is_liked = await self.services.post_like.toggle_like(
            post_uid=post_uid,
            user_uid=self.request.session["uid"]
        )
        
        if is_liked is None:
            return web.json_response({"error": "Failed to toggle like"}, status=400)
        
        # Get updated post
        post = await self.services.post.get(uid=post_uid)
        
        return web.json_response({
            "is_liked": is_liked,
            "like_count": post["like_count"]
        })

    async def toggle_pin(self):
        request = self 
        self = request.app

        setattr(self, "request", request)
        """POST /forum/api/threads/:thread_uid/pin - Toggle thread pin"""
        if not self.request.session.get("logged_in"):
            return web.json_response({"error": "Unauthorized"}, status=401)
        
        thread_uid = self.request.match_info["thread_uid"]
        
        thread = await self.services.thread.toggle_pin(
            thread_uid=thread_uid,
            user_uid=self.request.session["uid"]
        )
        
        if not thread:
            return web.json_response({"error": "Cannot toggle pin"}, status=403)
        
        return web.json_response({"is_pinned": thread["is_pinned"]})

    async def toggle_lock(self):
        request = self 
        self = request.app
        
        setattr(self, "request", request)
        """POST /forum/api/threads/:thread_uid/lock - Toggle thread lock"""
        if not self.request.session.get("logged_in"):
            return web.json_response({"error": "Unauthorized"}, status=401)
        
        thread_uid = self.request.match_info["thread_uid"]
        
        thread = await self.services.thread.toggle_lock(
            thread_uid=thread_uid,
            user_uid=self.request.session["uid"]
        )
        
        if not thread:
            return web.json_response({"error": "Cannot toggle lock"}, status=403)
        
        return web.json_response({"is_locked": thread["is_locked"]})


# views/forum_websocket.py
class ForumWebSocketView(BaseView):
    """WebSocket view for real-time forum updates"""
    
    async def get(self):
        ws = web.WebSocketResponse()
        await ws.prepare(self.request)
        
        # Store WebSocket connection
        ws_id = self.services.forum.generate_uid()
        if not hasattr(self.app, 'forum_websockets'):
            self.app.forum_websockets = {}
        
        user_uid = self.request.session.get("uid")
        self.app.forum_websockets[ws_id] = {
            "ws": ws,
            "user_uid": user_uid,
            "subscriptions": set()
        }
        
        try:
            async for msg in ws:
                if msg.type == web.WSMsgType.TEXT:
                    data = json.loads(msg.data)
                    await self.handle_ws_message(ws_id, data)
                elif msg.type == web.WSMsgType.ERROR:
                    break
        finally:
            # Clean up
            if ws_id in self.app.forum_websockets:
                del self.app.forum_websockets[ws_id]
        
        return ws
    
    async def handle_ws_message(self, ws_id, data):
        """Handle incoming WebSocket messages"""
        action = data.get("action")
        
        if action == "subscribe":
            # Subscribe to forum/thread updates
            target_type = data.get("type")  # "forum" or "thread"
            target_id = data.get("id")
            
            if target_type and target_id:
                subscription = f"{target_type}:{target_id}"
                self.app.forum_websockets[ws_id]["subscriptions"].add(subscription)
                
                # Send confirmation
                ws = self.app.forum_websockets[ws_id]["ws"]
                await ws.send_str(json.dumps({
                    "type": "subscribed",
                    "subscription": subscription
                }))
        
        elif action == "unsubscribe":
            target_type = data.get("type")
            target_id = data.get("id")
            
            if target_type and target_id:
                subscription = f"{target_type}:{target_id}"
                self.app.forum_websockets[ws_id]["subscriptions"].discard(subscription)
                
                # Send confirmation
                ws = self.app.forum_websockets[ws_id]["ws"]
                await ws.send_str(json.dumps({
                    "type": "unsubscribed",
                    "subscription": subscription
                }))
    
    @staticmethod
    async def broadcast_update(app, event_type, data):
        """Broadcast updates to subscribed WebSocket clients"""
        if not hasattr(app, 'forum_websockets'):
            return
        
        # Determine subscription targets based on event
        targets = set()
        
        if event_type in ["thread_created", "post_created", "post_edited", "post_deleted"]:
            if "forum_uid" in data:
                targets.add(f"forum:{data['forum_uid']}")
        
        if event_type in ["post_created", "post_edited", "post_deleted", "post_liked", "post_unliked"]:
            if "thread_uid" in data:
                targets.add(f"thread:{data['thread_uid']}")
        
        # Send to subscribed clients
        for ws_id, ws_data in app.forum_websockets.items():
            if ws_data["subscriptions"] & targets:
                try:
                    await ws_data["ws"].send_str(json.dumps({
                        "type": event_type,
                        "data": data
                    }))
                except Exception:
                    pass
