from __future__ import annotations

from typing import TYPE_CHECKING, Protocol
from uuid import UUID

from sqlalchemy import select, update
from sqlalchemy.exc import SQLAlchemyError

from core.logging import get_logger
from models.inbox_messages import InboxMessage

if TYPE_CHECKING:
    from sqlalchemy.ext.asyncio import AsyncSession

logger = get_logger("v1.inbox_messages.repository")


class InboxMessageRepository(Protocol):
    async def create(self, data: dict[str, object]) -> InboxMessage: ...
    async def get_by_id(
        self, message_id: UUID, recipient_id: UUID
    ) -> InboxMessage | None: ...
    async def list_by_recipient(
        self, recipient_id: UUID, status: str | None = None
    ) -> list[InboxMessage]: ...
    async def update_status(
        self,
        message_id: UUID,
        recipient_id: UUID,
        status: str,
    ) -> InboxMessage | None: ...


class SQLAlchemyInboxMessageRepository:
    _session: AsyncSession

    def __init__(self, session: AsyncSession) -> None:
        self._session = session

    async def create(self, data: dict[str, object]) -> InboxMessage:
        try:
            message = InboxMessage(**data)
            self._session.add(message)
            await self._session.flush()
            return message
        except SQLAlchemyError:
            logger.exception("Inbox message creation failed")
            raise

    async def get_by_id(
        self, message_id: UUID, recipient_id: UUID
    ) -> InboxMessage | None:
        try:
            stmt = (
                select(InboxMessage)
                .where(InboxMessage.id == message_id)
                .where(InboxMessage.recipient_id == recipient_id)
            )
            result = await self._session.execute(stmt)
            return result.scalar_one_or_none()
        except SQLAlchemyError:
            logger.exception(
                "Inbox message lookup failed",
                message_id=str(message_id),
                recipient_id=str(recipient_id),
            )
            raise

    async def list_by_recipient(
        self, recipient_id: UUID, status: str | None = None
    ) -> list[InboxMessage]:
        try:
            stmt = (
                select(InboxMessage)
                .where(InboxMessage.recipient_id == recipient_id)
                .order_by(InboxMessage.created_at.desc())
            )
            if status is not None:
                stmt = stmt.where(InboxMessage.status == status)
            result = await self._session.execute(stmt)
            return list(result.scalars().all())
        except SQLAlchemyError:
            logger.exception(
                "Inbox message list failed",
                recipient_id=str(recipient_id),
                status=status,
            )
            raise

    async def update_status(
        self,
        message_id: UUID,
        recipient_id: UUID,
        status: str,
    ) -> InboxMessage | None:
        try:
            stmt = (
                update(InboxMessage)
                .where(InboxMessage.id == message_id)
                .where(InboxMessage.recipient_id == recipient_id)
                .values(status=status, is_read=True)
                .returning(InboxMessage)
            )
            result = await self._session.execute(stmt)
            await self._session.flush()
            return result.scalar_one_or_none()
        except SQLAlchemyError:
            logger.exception(
                "Inbox message status update failed",
                message_id=str(message_id),
                recipient_id=str(recipient_id),
                status=status,
            )
            raise
