import json
import logging

from fastapi import APIRouter, WebSocket, WebSocketDisconnect
from sqlalchemy.orm import Session

from app.core.database import SessionLocal
from app.services.conversation_service import ConversationService

logger = logging.getLogger(__name__)
router = APIRouter()


@router.websocket("/ws/v1/conversations/{conversation_id}")
async def conversation_ws(websocket: WebSocket, conversation_id: str) -> None:
    await websocket.accept()
    db: Session = SessionLocal()
    service = ConversationService(db)

    try:
        await websocket.send_json(
            {
                "type": "character_state",
                "state": "idle",
                "conversation_id": conversation_id,
            }
        )

        while True:
            message = await websocket.receive()

            if message.get("type") == "websocket.disconnect":
                break

            if "bytes" in message and message["bytes"]:
                await websocket.send_json({"type": "character_state", "state": "listening"})
                await websocket.send_json({"type": "character_state", "state": "thinking"})

                result = await service.process_audio(conversation_id, message["bytes"])

                await websocket.send_json(
                    {"type": "user_transcript", "text": result.user_text}
                )
                await websocket.send_json(
                    {"type": "assistant_text", "text": result.assistant_text}
                )
                await websocket.send_json(
                    {"type": "character_state", "state": "talking"}
                )
                await websocket.send_json(
                    {"type": "audio", "url": result.audio_url, "latency": result.latency}
                )
                await websocket.send_json({"type": "character_state", "state": "idle"})
                continue

            raw = message.get("text")
            if not raw:
                continue

            data = json.loads(raw)
            msg_type = data.get("type")

            if msg_type == "ping":
                await websocket.send_json({"type": "pong"})
            elif msg_type == "set_state":
                await websocket.send_json(
                    {
                        "type": "character_state",
                        "state": data.get("state", "idle"),
                    }
                )
            else:
                await websocket.send_json(
                    {
                        "type": "error",
                        "message": f"Unsupported event type: {msg_type}",
                    }
                )
    except WebSocketDisconnect:
        logger.info("WS disconnected conversation=%s", conversation_id)
    except Exception as exc:  # noqa: BLE001
        logger.exception("WS error conversation=%s", conversation_id)
        try:
            await websocket.send_json({"type": "error", "message": str(exc)})
        except Exception:  # noqa: BLE001
            pass
    finally:
        db.close()
