# app/modules/messaging/routes/api/admin_messages.py
"""
Admin messaging endpoints.

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

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_admin_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 (
    AdminConversationListResponse,
    AdminConversationSummary,
    AttachmentResponse,
    CloseConversationResponse,
    ConversationCreate,
    ConversationDetailResponse,
    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

admin_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
) -> AdminConversationSummary:
    """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.ADMIN
            and p.participant_id == current_user_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.ADMIN, 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

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

    return AdminConversationSummary(
        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,
        store_name=store_name,
        store_code=store_code,
    )


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


@admin_messages_router.get("", response_model=AdminConversationListResponse)
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_admin: UserContext = Depends(get_current_admin_api),
) -> AdminConversationListResponse:
    """List conversations for admin (admin_store and admin_customer channels)."""
    conversations, total, total_unread = messaging_service.list_conversations(
        db=db,
        participant_type=ParticipantType.ADMIN,
        participant_id=current_admin.id,
        conversation_type=conversation_type,
        is_closed=is_closed,
        skip=skip,
        limit=limit,
    )

    return AdminConversationListResponse(
        conversations=[
            _enrich_conversation_summary(db, c, current_admin.id) for c in conversations
        ],
        total=total,
        total_unread=total_unread,
        skip=skip,
        limit=limit,
    )


@admin_messages_router.get("/unread-count", response_model=UnreadCountResponse)
def get_unread_count(
    db: Session = Depends(get_db),
    current_admin: UserContext = Depends(get_current_admin_api),
) -> UnreadCountResponse:
    """Get total unread message count for header badge."""
    count = messaging_service.get_unread_count(
        db=db,
        participant_type=ParticipantType.ADMIN,
        participant_id=current_admin.id,
    )
    return UnreadCountResponse(total_unread=count)


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


@admin_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"),
    store_id: int | None = Query(None, description="Filter by store"),
    skip: int = Query(0, ge=0),
    limit: int = Query(50, ge=1, le=100),
    db: Session = Depends(get_db),
    current_admin: UserContext = Depends(get_current_admin_api),
) -> RecipientListResponse:
    """Get list of available recipients for compose modal."""
    if recipient_type == ParticipantType.STORE:
        recipient_data, total = messaging_service.get_store_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"],
                store_name=r.get("store_name"),
            )
            for r in recipient_data
        ]
    elif recipient_type == ParticipantType.CUSTOMER:
        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:
        recipients = []
        total = 0

    return RecipientListResponse(recipients=recipients, total=total)


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


@admin_messages_router.post("", response_model=ConversationDetailResponse)
def create_conversation(
    data: ConversationCreate,
    db: Session = Depends(get_db),
    current_admin: UserContext = Depends(get_current_admin_api),
) -> ConversationDetailResponse:
    """Create a new conversation."""
    # Validate conversation type for admin
    if data.conversation_type not in [
        ConversationType.ADMIN_STORE,
        ConversationType.ADMIN_CUSTOMER,
    ]:
        raise InvalidConversationTypeException(
            message="Admin can only create admin_store or admin_customer conversations",
            allowed_types=["admin_store", "admin_customer"],
        )

    # Validate recipient type matches conversation type
    if (
        data.conversation_type == ConversationType.ADMIN_STORE
        and data.recipient_type != ParticipantType.STORE
    ):
        raise InvalidRecipientTypeException(
            conversation_type="admin_store",
            expected_recipient_type="store",
        )
    if (
        data.conversation_type == ConversationType.ADMIN_CUSTOMER
        and data.recipient_type != ParticipantType.CUSTOMER
    ):
        raise InvalidRecipientTypeException(
            conversation_type="admin_customer",
            expected_recipient_type="customer",
        )

    # Create conversation
    conversation = messaging_service.create_conversation(
        db=db,
        conversation_type=data.conversation_type,
        subject=data.subject,
        initiator_type=ParticipantType.ADMIN,
        initiator_id=current_admin.id,
        recipient_type=data.recipient_type,
        recipient_id=data.recipient_id,
        store_id=data.store_id,
        initial_message=data.initial_message,
    )
    db.commit()
    db.refresh(conversation)

    logger.info(
        f"Admin {current_admin.username} created conversation {conversation.id} "
        f"with {data.recipient_type.value}:{data.recipient_id}"
    )

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


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


def _build_conversation_detail(
    db: Session, conversation: Any, current_user_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.ADMIN
            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,
    )


@admin_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_admin: UserContext = Depends(get_current_admin_api),
) -> ConversationDetailResponse:
    """Get conversation detail with messages."""
    conversation = messaging_service.get_conversation(
        db=db,
        conversation_id=conversation_id,
        participant_type=ParticipantType.ADMIN,
        participant_id=current_admin.id,
    )

    if not conversation:
        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.ADMIN,
            reader_id=current_admin.id,
        )
        db.commit()

    return _build_conversation_detail(db, conversation, current_admin.id)


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


@admin_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_admin: UserContext = Depends(get_current_admin_api),
) -> MessageResponse:
    """Send a message in a conversation, optionally with attachments."""
    # Verify access
    conversation = messaging_service.get_conversation(
        db=db,
        conversation_id=conversation_id,
        participant_type=ParticipantType.ADMIN,
        participant_id=current_admin.id,
    )

    if not conversation:
        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.ADMIN,
        sender_id=current_admin.id,
        content=content,
        attachments=attachments if attachments else None,
    )
    db.commit()
    db.refresh(message)

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

    return _enrich_message(db, message)


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


@admin_messages_router.post("/{conversation_id}/close", response_model=CloseConversationResponse)
def close_conversation(
    conversation_id: int,
    db: Session = Depends(get_db),
    current_admin: UserContext = Depends(get_current_admin_api),
) -> CloseConversationResponse:
    """Close a conversation."""
    conversation = messaging_service.close_conversation(
        db=db,
        conversation_id=conversation_id,
        closer_type=ParticipantType.ADMIN,
        closer_id=current_admin.id,
    )

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

    db.commit()
    logger.info(
        f"Admin {current_admin.username} closed conversation {conversation_id}"
    )

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


@admin_messages_router.post("/{conversation_id}/reopen", response_model=ReopenConversationResponse)
def reopen_conversation(
    conversation_id: int,
    db: Session = Depends(get_db),
    current_admin: UserContext = Depends(get_current_admin_api),
) -> ReopenConversationResponse:
    """Reopen a closed conversation."""
    conversation = messaging_service.reopen_conversation(
        db=db,
        conversation_id=conversation_id,
        opener_type=ParticipantType.ADMIN,
        opener_id=current_admin.id,
    )

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

    db.commit()
    logger.info(
        f"Admin {current_admin.username} reopened conversation {conversation_id}"
    )

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


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

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


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


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

    return PreferencesUpdateResponse(success=success)
