# app/modules/messaging/routes/api/store_messages.py
"""
Store messaging endpoints.

Provides endpoints for:
- Viewing conversations (store_customer and admin_store channels)
- Sending and receiving messages
- Managing conversation status
- File attachments

Uses get_current_store_api dependency which guarantees token_store_id is present.
"""

import logging
from typing import Any

from fastapi import APIRouter, Depends, File, Form, Query, UploadFile
from pydantic import BaseModel
from sqlalchemy.orm import Session

from app.api.deps import get_current_store_api
from app.core.database import get_db
from app.modules.messaging.exceptions import (
    ConversationClosedException,
    ConversationNotFoundException,
    InvalidConversationTypeException,
    InvalidRecipientTypeException,
    MessageAttachmentException,
)
from app.modules.messaging.models import ConversationType, ParticipantType
from app.modules.messaging.schemas import (
    AttachmentResponse,
    CloseConversationResponse,
    ConversationCreate,
    ConversationDetailResponse,
    ConversationListResponse,
    ConversationSummary,
    MarkReadResponse,
    MessageResponse,
    NotificationPreferencesUpdate,
    ParticipantInfo,
    ParticipantResponse,
    RecipientListResponse,
    RecipientOption,
    ReopenConversationResponse,
    UnreadCountResponse,
)
from app.modules.messaging.services.message_attachment_service import (
    message_attachment_service,
)
from app.modules.messaging.services.messaging_service import messaging_service
from app.modules.tenancy.schemas.auth import UserContext

store_messages_router = APIRouter(prefix="/messages")
logger = logging.getLogger(__name__)


# ============================================================================
# HELPER FUNCTIONS
# ============================================================================


def _enrich_message(
    db: Session, message: Any, include_attachments: bool = True
) -> MessageResponse:
    """Enrich message with sender info and attachments."""
    sender_info = messaging_service.get_participant_info(
        db, message.sender_type, message.sender_id
    )

    attachments = []
    if include_attachments and message.attachments:
        for att in message.attachments:
            attachments.append(
                AttachmentResponse(
                    id=att.id,
                    filename=att.filename,
                    original_filename=att.original_filename,
                    file_size=att.file_size,
                    mime_type=att.mime_type,
                    is_image=att.is_image,
                    image_width=att.image_width,
                    image_height=att.image_height,
                    download_url=message_attachment_service.get_download_url(
                        att.file_path
                    ),
                    thumbnail_url=(
                        message_attachment_service.get_download_url(att.thumbnail_path)
                        if att.thumbnail_path
                        else None
                    ),
                )
            )

    return MessageResponse(
        id=message.id,
        conversation_id=message.conversation_id,
        sender_type=message.sender_type,
        sender_id=message.sender_id,
        content=message.content,
        is_system_message=message.is_system_message,
        is_deleted=message.is_deleted,
        created_at=message.created_at,
        sender_name=sender_info["name"] if sender_info else None,
        sender_email=sender_info["email"] if sender_info else None,
        attachments=attachments,
    )


def _enrich_conversation_summary(
    db: Session, conversation: Any, current_user_id: int, store_id: int
) -> ConversationSummary:
    """Enrich conversation with other participant info and unread count."""
    # Get current user's participant record
    my_participant = next(
        (
            p
            for p in conversation.participants
            if p.participant_type == ParticipantType.STORE
            and p.participant_id == current_user_id
            and p.store_id == store_id
        ),
        None,
    )
    unread_count = my_participant.unread_count if my_participant else 0

    # Get other participant info
    other = messaging_service.get_other_participant(
        conversation, ParticipantType.STORE, current_user_id
    )
    other_info = None
    if other:
        info = messaging_service.get_participant_info(
            db, other.participant_type, other.participant_id
        )
        if info:
            other_info = ParticipantInfo(
                id=info["id"],
                type=info["type"],
                name=info["name"],
                email=info.get("email"),
            )

    # Get last message preview
    last_message_preview = None
    if conversation.messages:
        last_msg = conversation.messages[-1] if conversation.messages else None
        if last_msg:
            preview = last_msg.content[:100]
            if len(last_msg.content) > 100:
                preview += "..."
            last_message_preview = preview

    return ConversationSummary(
        id=conversation.id,
        conversation_type=conversation.conversation_type,
        subject=conversation.subject,
        store_id=conversation.store_id,
        is_closed=conversation.is_closed,
        closed_at=conversation.closed_at,
        last_message_at=conversation.last_message_at,
        message_count=conversation.message_count,
        created_at=conversation.created_at,
        unread_count=unread_count,
        other_participant=other_info,
        last_message_preview=last_message_preview,
    )


# ============================================================================
# CONVERSATION LIST
# ============================================================================


@store_messages_router.get("", response_model=ConversationListResponse)
def list_conversations(
    conversation_type: ConversationType | None = Query(None, description="Filter by type"),
    is_closed: bool | None = Query(None, description="Filter by status"),
    skip: int = Query(0, ge=0),
    limit: int = Query(20, ge=1, le=100),
    db: Session = Depends(get_db),
    current_user: UserContext = Depends(get_current_store_api),
) -> ConversationListResponse:
    """List conversations for store (store_customer and admin_store channels)."""
    store_id = current_user.token_store_id

    conversations, total, total_unread = messaging_service.list_conversations(
        db=db,
        participant_type=ParticipantType.STORE,
        participant_id=current_user.id,
        store_id=store_id,
        conversation_type=conversation_type,
        is_closed=is_closed,
        skip=skip,
        limit=limit,
    )

    return ConversationListResponse(
        conversations=[
            _enrich_conversation_summary(db, c, current_user.id, store_id)
            for c in conversations
        ],
        total=total,
        total_unread=total_unread,
        skip=skip,
        limit=limit,
    )


@store_messages_router.get("/unread-count", response_model=UnreadCountResponse)
def get_unread_count(
    db: Session = Depends(get_db),
    current_user: UserContext = Depends(get_current_store_api),
) -> UnreadCountResponse:
    """Get total unread message count for header badge."""
    store_id = current_user.token_store_id

    count = messaging_service.get_unread_count(
        db=db,
        participant_type=ParticipantType.STORE,
        participant_id=current_user.id,
        store_id=store_id,
    )
    return UnreadCountResponse(total_unread=count)


# ============================================================================
# RECIPIENTS
# ============================================================================


@store_messages_router.get("/recipients", response_model=RecipientListResponse)
def get_recipients(
    recipient_type: ParticipantType = Query(..., description="Type of recipients to list"),
    search: str | None = Query(None, description="Search by name/email"),
    skip: int = Query(0, ge=0),
    limit: int = Query(50, ge=1, le=100),
    db: Session = Depends(get_db),
    current_user: UserContext = Depends(get_current_store_api),
) -> RecipientListResponse:
    """Get list of available recipients for compose modal."""
    store_id = current_user.token_store_id

    if recipient_type == ParticipantType.CUSTOMER:
        # List customers for this store (for store_customer conversations)
        recipient_data, total = messaging_service.get_customer_recipients(
            db=db,
            store_id=store_id,
            search=search,
            skip=skip,
            limit=limit,
        )
        recipients = [
            RecipientOption(
                id=r["id"],
                type=r["type"],
                name=r["name"],
                email=r["email"],
                store_id=r["store_id"],
            )
            for r in recipient_data
        ]
    else:
        # Stores can't start conversations with admins - admins initiate those
        recipients = []
        total = 0

    return RecipientListResponse(recipients=recipients, total=total)


# ============================================================================
# CREATE CONVERSATION
# ============================================================================


@store_messages_router.post("", response_model=ConversationDetailResponse)
def create_conversation(
    data: ConversationCreate,
    db: Session = Depends(get_db),
    current_user: UserContext = Depends(get_current_store_api),
) -> ConversationDetailResponse:
    """Create a new conversation with a customer."""
    store_id = current_user.token_store_id

    # Stores can only create store_customer conversations
    if data.conversation_type != ConversationType.STORE_CUSTOMER:
        raise InvalidConversationTypeException(
            message="Stores can only create store_customer conversations",
            allowed_types=["store_customer"],
        )

    if data.recipient_type != ParticipantType.CUSTOMER:
        raise InvalidRecipientTypeException(
            conversation_type="store_customer",
            expected_recipient_type="customer",
        )

    # Create conversation
    conversation = messaging_service.create_conversation(
        db=db,
        conversation_type=ConversationType.STORE_CUSTOMER,
        subject=data.subject,
        initiator_type=ParticipantType.STORE,
        initiator_id=current_user.id,
        recipient_type=ParticipantType.CUSTOMER,
        recipient_id=data.recipient_id,
        store_id=store_id,
        initial_message=data.initial_message,
    )
    db.commit()
    db.refresh(conversation)

    logger.info(
        f"Store {current_user.username} created conversation {conversation.id} "
        f"with customer:{data.recipient_id}"
    )

    # Return full detail response
    return _build_conversation_detail(db, conversation, current_user.id, store_id)


# ============================================================================
# CONVERSATION DETAIL
# ============================================================================


def _build_conversation_detail(
    db: Session, conversation: Any, current_user_id: int, store_id: int
) -> ConversationDetailResponse:
    """Build full conversation detail response."""
    # Get my participant for unread count
    my_participant = next(
        (
            p
            for p in conversation.participants
            if p.participant_type == ParticipantType.STORE
            and p.participant_id == current_user_id
        ),
        None,
    )
    unread_count = my_participant.unread_count if my_participant else 0

    # Build participant responses
    participants = []
    for p in conversation.participants:
        info = messaging_service.get_participant_info(
            db, p.participant_type, p.participant_id
        )
        participants.append(
            ParticipantResponse(
                id=p.id,
                participant_type=p.participant_type,
                participant_id=p.participant_id,
                unread_count=p.unread_count,
                last_read_at=p.last_read_at,
                email_notifications=p.email_notifications,
                muted=p.muted,
                participant_info=(
                    ParticipantInfo(
                        id=info["id"],
                        type=info["type"],
                        name=info["name"],
                        email=info.get("email"),
                    )
                    if info
                    else None
                ),
            )
        )

    # Build message responses
    messages = [_enrich_message(db, m) for m in conversation.messages]

    # Get store name if applicable
    store_name = None
    if conversation.store:
        store_name = conversation.store.name

    return ConversationDetailResponse(
        id=conversation.id,
        conversation_type=conversation.conversation_type,
        subject=conversation.subject,
        store_id=conversation.store_id,
        is_closed=conversation.is_closed,
        closed_at=conversation.closed_at,
        closed_by_type=conversation.closed_by_type,
        closed_by_id=conversation.closed_by_id,
        last_message_at=conversation.last_message_at,
        message_count=conversation.message_count,
        created_at=conversation.created_at,
        updated_at=conversation.updated_at,
        participants=participants,
        messages=messages,
        unread_count=unread_count,
        store_name=store_name,
    )


@store_messages_router.get("/{conversation_id}", response_model=ConversationDetailResponse)
def get_conversation(
    conversation_id: int,
    mark_read: bool = Query(True, description="Automatically mark as read"),
    db: Session = Depends(get_db),
    current_user: UserContext = Depends(get_current_store_api),
) -> ConversationDetailResponse:
    """Get conversation detail with messages."""
    store_id = current_user.token_store_id

    conversation = messaging_service.get_conversation(
        db=db,
        conversation_id=conversation_id,
        participant_type=ParticipantType.STORE,
        participant_id=current_user.id,
    )

    if not conversation:
        raise ConversationNotFoundException(str(conversation_id))

    # Verify store context
    if conversation.store_id and conversation.store_id != store_id:
        raise ConversationNotFoundException(str(conversation_id))

    # Mark as read if requested
    if mark_read:
        messaging_service.mark_conversation_read(
            db=db,
            conversation_id=conversation_id,
            reader_type=ParticipantType.STORE,
            reader_id=current_user.id,
        )
        db.commit()

    return _build_conversation_detail(db, conversation, current_user.id, store_id)


# ============================================================================
# SEND MESSAGE
# ============================================================================


@store_messages_router.post("/{conversation_id}/messages", response_model=MessageResponse)
async def send_message(
    conversation_id: int,
    content: str = Form(...),
    files: list[UploadFile] = File(default=[]),
    db: Session = Depends(get_db),
    current_user: UserContext = Depends(get_current_store_api),
) -> MessageResponse:
    """Send a message in a conversation, optionally with attachments."""
    store_id = current_user.token_store_id

    # Verify access
    conversation = messaging_service.get_conversation(
        db=db,
        conversation_id=conversation_id,
        participant_type=ParticipantType.STORE,
        participant_id=current_user.id,
    )

    if not conversation:
        raise ConversationNotFoundException(str(conversation_id))

    # Verify store context
    if conversation.store_id and conversation.store_id != store_id:
        raise ConversationNotFoundException(str(conversation_id))

    if conversation.is_closed:
        raise ConversationClosedException(conversation_id)

    # Process attachments
    attachments = []
    for file in files:
        try:
            att_data = await message_attachment_service.validate_and_store(
                db=db, file=file, conversation_id=conversation_id
            )
            attachments.append(att_data)
        except ValueError as e:
            raise MessageAttachmentException(str(e))

    # Send message
    message = messaging_service.send_message(
        db=db,
        conversation_id=conversation_id,
        sender_type=ParticipantType.STORE,
        sender_id=current_user.id,
        content=content,
        attachments=attachments if attachments else None,
    )
    db.commit()
    db.refresh(message)

    logger.info(
        f"Store {current_user.username} sent message {message.id} "
        f"in conversation {conversation_id}"
    )

    return _enrich_message(db, message)


# ============================================================================
# CONVERSATION ACTIONS
# ============================================================================


@store_messages_router.post("/{conversation_id}/close", response_model=CloseConversationResponse)
def close_conversation(
    conversation_id: int,
    db: Session = Depends(get_db),
    current_user: UserContext = Depends(get_current_store_api),
) -> CloseConversationResponse:
    """Close a conversation."""
    store_id = current_user.token_store_id

    # Verify access first
    conversation = messaging_service.get_conversation(
        db=db,
        conversation_id=conversation_id,
        participant_type=ParticipantType.STORE,
        participant_id=current_user.id,
    )

    if not conversation:
        raise ConversationNotFoundException(str(conversation_id))

    if conversation.store_id and conversation.store_id != store_id:
        raise ConversationNotFoundException(str(conversation_id))

    conversation = messaging_service.close_conversation(
        db=db,
        conversation_id=conversation_id,
        closer_type=ParticipantType.STORE,
        closer_id=current_user.id,
    )

    db.commit()
    logger.info(
        f"Store {current_user.username} closed conversation {conversation_id}"
    )

    return CloseConversationResponse(
        success=True,
        message="Conversation closed",
        conversation_id=conversation_id,
    )


@store_messages_router.post("/{conversation_id}/reopen", response_model=ReopenConversationResponse)
def reopen_conversation(
    conversation_id: int,
    db: Session = Depends(get_db),
    current_user: UserContext = Depends(get_current_store_api),
) -> ReopenConversationResponse:
    """Reopen a closed conversation."""
    store_id = current_user.token_store_id

    # Verify access first
    conversation = messaging_service.get_conversation(
        db=db,
        conversation_id=conversation_id,
        participant_type=ParticipantType.STORE,
        participant_id=current_user.id,
    )

    if not conversation:
        raise ConversationNotFoundException(str(conversation_id))

    if conversation.store_id and conversation.store_id != store_id:
        raise ConversationNotFoundException(str(conversation_id))

    conversation = messaging_service.reopen_conversation(
        db=db,
        conversation_id=conversation_id,
        opener_type=ParticipantType.STORE,
        opener_id=current_user.id,
    )

    db.commit()
    logger.info(
        f"Store {current_user.username} reopened conversation {conversation_id}"
    )

    return ReopenConversationResponse(
        success=True,
        message="Conversation reopened",
        conversation_id=conversation_id,
    )


@store_messages_router.put("/{conversation_id}/read", response_model=MarkReadResponse)
def mark_read(
    conversation_id: int,
    db: Session = Depends(get_db),
    current_user: UserContext = Depends(get_current_store_api),
) -> MarkReadResponse:
    """Mark conversation as read."""
    success = messaging_service.mark_conversation_read(
        db=db,
        conversation_id=conversation_id,
        reader_type=ParticipantType.STORE,
        reader_id=current_user.id,
    )
    db.commit()

    return MarkReadResponse(
        success=success,
        conversation_id=conversation_id,
        unread_count=0,
    )


class PreferencesUpdateResponse(BaseModel):
    """Response for preferences update."""
    success: bool


@store_messages_router.put("/{conversation_id}/preferences", response_model=PreferencesUpdateResponse)
def update_preferences(
    conversation_id: int,
    preferences: NotificationPreferencesUpdate,
    db: Session = Depends(get_db),
    current_user: UserContext = Depends(get_current_store_api),
) -> PreferencesUpdateResponse:
    """Update notification preferences for a conversation."""
    success = messaging_service.update_notification_preferences(
        db=db,
        conversation_id=conversation_id,
        participant_type=ParticipantType.STORE,
        participant_id=current_user.id,
        email_notifications=preferences.email_notifications,
        muted=preferences.muted,
    )
    db.commit()

    return PreferencesUpdateResponse(success=success)
