import uuid

from fastapi import APIRouter, WebSocket, WebSocketDisconnect

from core.websocket import get_ws_current_user
from db.session import AsyncSessionLocal
from domains.groups.repo import get_group_member

from domains.realtime.ws_manager import manager
from domains.realtime.presence_service import (
    user_join_group,
    user_leave_group,
    list_online_users
)

from domains.realtime.speaker_service import (
    request_speak,
    stop_speaking,
    current_speaker
)

from integrations.livekit.token_service import generate_join_token

router = APIRouter()


@router.websocket("/ws/groups/{group_id}")
async def group_ws(websocket: WebSocket, group_id: str):

    user = await get_ws_current_user(websocket)
    try:
        group_id_uuid = uuid.UUID(group_id)
    except ValueError:
        await websocket.close(code=1008)
        return

    async with AsyncSessionLocal() as db:
        membership = await get_group_member(db, group_id_uuid, user.id)
    if not membership:
        await websocket.close(code=1008)
        return

    user_id = str(user.id)

    # connect websocket
    await manager.connect(group_id, websocket)

    # add presence
    await user_join_group(group_id, user_id)

    # give listener token
    listener_token = generate_join_token(
        user_id=user_id,
        group_id=group_id,
        can_publish=False
    )

    await websocket.send_json({
        "type": "livekit_token",
        "token": listener_token
    })

    # broadcast updated presence
    await manager.broadcast(
        group_id,
        {
            "type": "presence",
            "users": await list_online_users(group_id)
        }
    )

    try:
        async with AsyncSessionLocal() as db:
            while True:
                data = await websocket.receive_json()
                event = data.get("type")
                # user wants to speak
                if event == "request_speak":
                    token = await request_speak(
                        db,
                        group_id,
                        user_id
                    )
                    if token:
                        await manager.broadcast(
                            group_id,
                            {
                                "type": "speaker",
                                "user_id": user_id
                            }
                        )
                        await websocket.send_json({
                            "type": "speaker_granted",
                            "token": token
                        })
                    else:
                        speaker = await current_speaker(db, group_id)

                        await websocket.send_json({
                            "type": "speaker_busy",
                            "speaker": speaker
                        })

                # user stops speaking
                elif event == "stop_speak":
                    await stop_speaking(db, group_id, user_id)
                    await manager.broadcast(
                        group_id,
                        {
                            "type": "speaker_released"
                        }
                    )

    except WebSocketDisconnect:
        manager.disconnect(group_id, websocket)
        await user_leave_group(group_id, user_id)
        await manager.broadcast(
            group_id,
            {
                "type": "presence",
                "users": await list_online_users(group_id)
            }
        )
