"""Conversation history API endpoints.

Provides endpoints for managing conversations and messages.
"""

from typing import Optional, List, Any
from datetime import datetime
from pydantic import BaseModel
from starlette.requests import Request
from starlette.responses import JSONResponse
from loguru import logger

from backend.chanakya.conversation.database import (
    create_conversation,
    get_conversations,
    get_conversation,
    update_conversation_title,
    delete_conversation,
    save_message,
    get_messages,
    toggle_bookmark,
    get_user_bookmarks,
)
from backend.chanakya.auth import get_user_by_username, decode_access_token


# ---------------------------------------------------------------------------
# Request/Response models
# ---------------------------------------------------------------------------

class CreateConversationRequest(BaseModel):
    """Request to create a new conversation."""
    title: Optional[str] = "New Chat"


class SaveMessageRequest(BaseModel):
    """Request to save a message."""
    message: str
    message_author: str  # 'user' or 'assistant'
    message_type: Optional[str] = None  # 'request' or 'response'
    sql_query: Optional[str] = None
    metadata: Optional[dict] = None
    parent_message_id: Optional[int] = None


class UpdateTitleRequest(BaseModel):
    """Request to update conversation title."""
    title: str


class ToggleBookmarkRequest(BaseModel):
    """Request to set or clear a bookmark on a message."""
    bookmarked: bool


#---------------------------------------------------------------------------
# Helper functions
# ---------------------------------------------------------------------------

def serialize_for_json(obj: Any) -> Any:
    """Convert datetime objects to ISO format strings for JSON serialization."""
    if isinstance(obj, dict):
        return {key: serialize_for_json(value) for key, value in obj.items()}
    elif isinstance(obj, list):
        return [serialize_for_json(item) for item in obj]
    elif isinstance(obj, datetime):
        return obj.isoformat()
    return obj


async def get_current_user_from_token(request: Request) -> Optional[dict]:
    """Extract and verify user from JWT token in Authorization header.
    
    Returns:
        User dict if valid token, None otherwise
    """
    auth_header = request.headers.get("authorization", "")
    logger.debug(f"[CONV-AUTH] Authorization header: {auth_header[:50] if auth_header else 'MISSING'}...")
    
    if not auth_header.startswith("Bearer "):
        logger.warning(f"[CONV-AUTH] Invalid auth header format: {auth_header[:20]}")
        return None
    
    token = auth_header.replace("Bearer ", "")
    logger.debug(f"[CONV-AUTH] Extracted token: {token[:20]}...")
    
    payload = decode_access_token(token)
    
    if not payload:
        logger.warning("[CONV-AUTH] Token decode failed")
        return None
    
    username = payload.get("sub")
    if not username:
        logger.warning("[CONV-AUTH] No username in token payload")
        return None
    
    logger.debug(f"[CONV-AUTH] Token valid for user: {username}")
    user = await get_user_by_username(username)
    
    if user:
        logger.debug(f"[CONV-AUTH] User found in database: {username}")
    else:
        logger.warning(f"[CONV-AUTH] User not found in database: {username}")
    
    return user


# ---------------------------------------------------------------------------
# Conversation endpoints
# ---------------------------------------------------------------------------

async def create_conversation_handler(request: Request) -> JSONResponse:
    """POST /conversations - Create a new conversation."""
    try:
        # Authenticate user
        user = await get_current_user_from_token(request)
        if not user:
            return JSONResponse(
                {"detail": "Authentication required"},
                status_code=401
            )
        
        # Parse request
        body = await request.json()
        req = CreateConversationRequest(**body)
        
        # Create conversation
        user_id = user["id"]
        conversation = await create_conversation(
            user_id=user_id,
            title=req.title,
            created_by=user_id
        )
        
        return JSONResponse(serialize_for_json(conversation))
        
    except Exception as e:
        return JSONResponse(
            {"detail": f"Failed to create conversation: {str(e)}"},
            status_code=500
        )


async def get_conversations_handler(request: Request) -> JSONResponse:
    """GET /conversations - Get paginated conversations for current user.

    Query params:
        limit  (int, default 10): number of conversations per page
        offset (int, default 0):  number to skip
    """
    try:
        # Authenticate user
        user = await get_current_user_from_token(request)
        if not user:
            return JSONResponse(
                {"detail": " Authentication required"},
                status_code=401
            )

        # Parse pagination query params
        try:
            limit = max(1, min(50, int(request.query_params.get("limit", 10))))
            offset = max(0, int(request.query_params.get("offset", 0)))
        except (TypeError, ValueError):
            limit, offset = 10, 0

        # Get conversations
        user_id = user["id"]
        result = await get_conversations(user_id=user_id, limit=limit, offset=offset)

        return JSONResponse(serialize_for_json(result))

    except Exception as e:
        return JSONResponse(
            {"detail": f"Failed to get conversations: {str(e)}"},
            status_code=500
        )


async def get_conversation_handler(request: Request) -> JSONResponse:
    """GET /conversations/{conversation_id} - Get conversation with messages."""
    try:
        # Authenticate user
        user = await get_current_user_from_token(request)
        if not user:
            return JSONResponse(
                {"detail": "Authentication required"},
                status_code=401
            )
        
        # Get conversation ID from path
        conversation_id = int(request.path_params.get("conversation_id"))
        
        # Get conversation
        conversation = await get_conversation(conversation_id)
        if not conversation:
            return JSONResponse(
                {"detail": "Conversation not found"},
                status_code=404
            )
        
        # Verify ownership
        user_id = user["id"]
        if conversation["user_id"] != user_id:
            return JSONResponse(
                {"detail": "Access denied"},
                status_code=403
            )
        
        # Get messages
        messages = await get_messages(conversation_id)
        
        return JSONResponse(serialize_for_json({
            "conversation": conversation,
            "messages": messages
        }))
        
    except Exception as e:
        return JSONResponse(
            {"detail": f"Failed to get conversation: {str(e)}"},
            status_code=500
        )


async def update_conversation_title_handler(request: Request) -> JSONResponse:
    """PUT /conversations/{conversation_id}/title - Update conversation title."""
    try:
        # Authenticate user
        user = await get_current_user_from_token(request)
        if not user:
            return JSONResponse(
                {"detail": "Authentication required"},
                status_code=401
            )
        
        # Get conversation ID from path
        conversation_id = int(request.path_params.get("conversation_id"))
        
        # Parse request
        body = await request.json()
        req = UpdateTitleRequest(**body)
        
        # Update title
        user_id = user["id"]
        updated = await update_conversation_title(
            conversation_id=conversation_id,
            title=req.title,
            updated_by=user_id
        )
        
        if not updated:
            return JSONResponse(
                {"detail": "Conversation not found"},
                status_code=404
            )
        
        return JSONResponse({"success": True})
        
    except Exception as e:
        return JSONResponse(
            {"detail": f"Failed to update title: {str(e)}"},
            status_code=500
        )


async def delete_conversation_handler(request: Request) -> JSONResponse:
    """DELETE /conversations/{conversation_id} - Delete a conversation."""
    try:
        # Authenticate user
        user = await get_current_user_from_token(request)
        if not user:
            return JSONResponse(
                {"detail": "Authentication required"},
                status_code=401
            )
        
        # Get conversation ID from path
        conversation_id = int(request.path_params.get("conversation_id"))
        
        # Delete conversation
        user_id = user["id"]
        deleted = await delete_conversation(
            conversation_id=conversation_id,
            deleted_by=user_id
        )
        
        if not deleted:
            return JSONResponse(
                {"detail": "Conversation not found"},
                status_code=404
            )
        
        return JSONResponse({"success": True})
        
    except Exception as e:
        return JSONResponse(
            {"detail": f"Failed to delete conversation: {str(e)}"},
            status_code=500
        )


# ---------------------------------------------------------------------------
# Message endpoints
# ---------------------------------------------------------------------------

async def save_message_handler(request: Request) -> JSONResponse:
    """POST /conversations/{conversation_id}/messages - Save a message."""
    try:
        # Authenticate user
        user = await get_current_user_from_token(request)
        if not user:
            return JSONResponse(
                {"detail": "Authentication required"},
                status_code=401
            )
        
        # Get conversation ID from path
        conversation_id = int(request.path_params.get("conversation_id"))
        
        # Parse request
        body = await request.json()
        req = SaveMessageRequest(**body)
        
        # Save message
        user_id = user["id"]
        
        message = await save_message(
            conversation_id=conversation_id,
            message_author=req.message_author,
            message=req.message,
            message_type=req.message_type,
            sql_query=req.sql_query,
            metadata=req.metadata,
            parent_message_id=req.parent_message_id,
            created_by=user_id
        )
        
        return JSONResponse(serialize_for_json(message))
        
    except Exception as e:
        logger.exception(e)
        return JSONResponse(
            {"detail": f"Failed to save message: {str(e)}"},
            status_code=500
        )


async def get_messages_handler(request: Request) -> JSONResponse:
    """GET /conversations/{conversation_id}/messages - Get all messages for a conversation."""
    try:
        conversation_id = int(request.path_params.get("conversation_id"))
        logger.info(f"[GET-MSGS] Get messages for conversation {conversation_id}")
        
        # Authenticate user
        user = await get_current_user_from_token(request)
        if not user:
            logger.warning(f"[GET-MSGS] Authentication failed for conversation {conversation_id}")
            return JSONResponse(
                {"detail": "Authentication required"},
                status_code=401
            )
        
        logger.info(f"[GET-MSGS] User authenticated: {user.get('username')}")
        
        # Verify conversation exists and user has access
        conversation = await get_conversation(conversation_id)
        if not conversation:
            logger.warning(f"[GET-MSGS] Conversation {conversation_id} not found")
            return JSONResponse(
                {"detail": "Conversation not found"},
                status_code=404
            )
        
        user_id = user["id"]
        if conversation["user_id"] != user_id:
            logger.warning(f"[GET-MSGS] Access denied for user {user_id} to conversation {conversation_id}")
            return JSONResponse(
                {"detail": "Access denied"},
                status_code=403
            )
        
        # Get messages
        messages = await get_messages(conversation_id)
        logger.info(f"[GET-MSGS] ✅ Found {len(messages)} messages for conversation {conversation_id}")
        
        for i, msg in enumerate(messages):
            has_metadata = bool(msg.get('metadata'))
            logger.debug(f"[GET-MSGS]   Message {i+1}: {msg.get('message_author')} - {msg.get('message_type')} (metadata: {has_metadata})")
        
        return JSONResponse(serialize_for_json(messages))
        
    except Exception as e:
        logger.error(f"[GET-MSGS] ❌ Error: {str(e)}")
        logger.exception(e)
        return JSONResponse(
            {"detail": f"Failed to get messages: {str(e)}"},
            status_code=500
        )


# ---------------------------------------------------------------------------
# Bookmark endpoints
# ---------------------------------------------------------------------------

async def toggle_bookmark_handler(request: Request) -> JSONResponse:
    """PATCH /conversations/{conversation_id}/messages/{message_id}/bookmark"""
    try:
        user = await get_current_user_from_token(request)
        if not user:
            return JSONResponse({"detail": "Authentication required"}, status_code=401)

        conversation_id = int(request.path_params.get("conversation_id"))
        message_id = int(request.path_params.get("message_id"))

        conversation = await get_conversation(conversation_id)
        if not conversation:
            return JSONResponse({"detail": "Conversation not found"}, status_code=404)

        if conversation["user_id"] != user["id"]:
            return JSONResponse({"detail": "Access denied"}, status_code=403)

        body = await request.json()
        req = ToggleBookmarkRequest(**body)

        result = await toggle_bookmark(message_id, req.bookmarked)
        if not result:
            return JSONResponse({"detail": "Message not found"}, status_code=404)

        if result["conversation_id"] != conversation_id:
            return JSONResponse({"detail": "Message not found"}, status_code=404)

        return JSONResponse({"id": result["id"], "bookmarked": result["bookmarked"]})

    except Exception as e:
        return JSONResponse(
            {"detail": f"Failed to toggle bookmark: {str(e)}"},
            status_code=500,
        )


async def get_user_bookmarks_handler(request: Request) -> JSONResponse:
    """GET /users/me/bookmarks"""
    try:
        user = await get_current_user_from_token(request)
        if not user:
            return JSONResponse({"detail": "Authentication required"}, status_code=401)

        bookmarks = await get_user_bookmarks(user["id"])
        return JSONResponse(serialize_for_json(bookmarks))

    except Exception as e:
        return JSONResponse(
            {"detail": f"Failed to get bookmarks: {str(e)}"},
            status_code=500,
        )
