from typing import Any, Dict, List, Optional

from fastapi import APIRouter, Depends, HTTPException, status
from pydantic import BaseModel, Field
from sqlalchemy.future import select
from storage.database import session_maker
from storage.feedback import ConversationFeedback
from storage.stored_conversation_metadata import StoredConversationMetadata

from openhands.events.event_store import EventStore
from openhands.server.shared import file_store
from openhands.server.user_auth import get_user_id
from openhands.utils.async_utils import call_sync_from_async

router = APIRouter(prefix='/feedback', tags=['feedback'])


async def get_event_ids(conversation_id: str, user_id: str) -> List[int]:
    """Get all event IDs for a given conversation.

    Args:
        conversation_id: The ID of the conversation to get events for
        user_id: The ID of the user who owns the conversation

    Returns:
        List of event IDs in the conversation

    Raises:
        HTTPException: If conversation metadata not found
    """

    # Verify the conversation belongs to the user
    def _verify_conversation():
        with session_maker() as session:
            metadata = (
                session.query(StoredConversationMetadata)
                .filter(
                    StoredConversationMetadata.conversation_id == conversation_id,
                    StoredConversationMetadata.user_id == user_id,
                )
                .first()
            )
            if not metadata:
                raise HTTPException(
                    status_code=status.HTTP_404_NOT_FOUND,
                    detail=f'Conversation {conversation_id} not found',
                )

    await call_sync_from_async(_verify_conversation)

    # Create an event store to access the events directly
    # This works even when the conversation is not running
    event_store = EventStore(
        sid=conversation_id,
        file_store=file_store,
        user_id=user_id,
    )

    # Get events from the event store
    events = event_store.search_events(start_id=0)

    # Return list of event IDs
    return [event.id for event in events]


class FeedbackRequest(BaseModel):
    conversation_id: str
    event_id: Optional[int] = None
    rating: int = Field(..., ge=1, le=5)
    reason: Optional[str] = None
    metadata: Optional[Dict[str, Any]] = None


@router.post('/conversation', status_code=status.HTTP_201_CREATED)
async def submit_conversation_feedback(feedback: FeedbackRequest):
    """
    Submit feedback for a conversation.

    This endpoint accepts a rating (1-5) and optional reason for the feedback.
    The feedback is associated with a specific conversation and optionally a specific event.
    """
    # Validate rating is between 1 and 5
    if feedback.rating < 1 or feedback.rating > 5:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail='Rating must be between 1 and 5',
        )

    # Create new feedback record
    new_feedback = ConversationFeedback(
        conversation_id=feedback.conversation_id,
        event_id=feedback.event_id,
        rating=feedback.rating,
        reason=feedback.reason,
        metadata=feedback.metadata,
    )

    # Add to database
    def _save_feedback():
        with session_maker() as session:
            session.add(new_feedback)
            session.commit()

    await call_sync_from_async(_save_feedback)

    return {'status': 'success', 'message': 'Feedback submitted successfully'}


@router.get('/conversation/{conversation_id}/batch')
async def get_batch_feedback(conversation_id: str, user_id: str = Depends(get_user_id)):
    """
    Get feedback for all events in a conversation.

    Returns feedback status for each event, including whether feedback exists
    and if so, the rating and reason.
    """
    # Get all event IDs for the conversation
    event_ids = await get_event_ids(conversation_id, user_id)
    if not event_ids:
        return {}

    # Query for existing feedback for all events
    def _check_feedback():
        with session_maker() as session:
            result = session.execute(
                select(ConversationFeedback).where(
                    ConversationFeedback.conversation_id == conversation_id,
                    ConversationFeedback.event_id.in_(event_ids),
                )
            )

            # Create a mapping of event_id to feedback
            feedback_map = {
                feedback.event_id: {
                    'exists': True,
                    'rating': feedback.rating,
                    'reason': feedback.reason,
                }
                for feedback in result.scalars()
            }

            # Build response including all events
            response = {}
            for event_id in event_ids:
                response[str(event_id)] = feedback_map.get(event_id, {'exists': False})

            return response

    return await call_sync_from_async(_check_feedback)
