"""Source-chat and media persistence."""

from __future__ import annotations

from sqlalchemy import func, select
from sqlalchemy.orm import selectinload

from app.database.base import utcnow
from app.database.models import MediaAsset, MediaStatus, SourceChat
from app.database.repositories.base import BaseRepository


class MediaRepository(BaseRepository):
    async def get_source(self, chat_id: int) -> SourceChat | None:
        async with self.db.session() as session:
            return await session.scalar(select(SourceChat).where(SourceChat.chat_id == chat_id))

    async def get_source_by_id(self, source_id: int) -> SourceChat | None:
        async with self.db.session() as session:
            return await session.get(SourceChat, source_id)

    async def update_source_by_id(
        self,
        source_id: int,
        *,
        is_active: bool | None = None,
        auto_ingest: bool | None = None,
    ) -> bool:
        async with self.db.session() as session:
            source = await session.get(SourceChat, source_id)
            if source is None:
                return False
            if is_active is not None:
                source.is_active = is_active
            if auto_ingest is not None:
                source.auto_ingest = auto_ingest
            return True

    async def list_sources(self, *, active_only: bool = False) -> list[SourceChat]:
        async with self.db.session() as session:
            statement = select(SourceChat).order_by(SourceChat.created_at)
            if active_only:
                statement = statement.where(SourceChat.is_active.is_(True))
            return list(await session.scalars(statement))

    async def add_source(
        self,
        chat_id: int,
        *,
        title: str | None,
        chat_type: str,
        added_by: int | None,
        auto_ingest: bool = True,
    ) -> tuple[SourceChat, bool]:
        async with self.db.session() as session:
            source = await session.scalar(select(SourceChat).where(SourceChat.chat_id == chat_id))
            if source is not None:
                source.is_active = True
                if title:
                    source.title = title
                return source, False
            source = SourceChat(
                chat_id=chat_id,
                title=title,
                chat_type=chat_type,
                is_active=True,
                auto_ingest=auto_ingest,
                added_by=added_by,
            )
            session.add(source)
            await session.flush()
            return source, True

    async def update_source(
        self,
        chat_id: int,
        *,
        is_active: bool | None = None,
        auto_ingest: bool | None = None,
        title: str | None = None,
        last_error: str | None = None,
    ) -> bool:
        async with self.db.session() as session:
            source = await session.scalar(select(SourceChat).where(SourceChat.chat_id == chat_id))
            if source is None:
                return False
            if is_active is not None:
                source.is_active = is_active
            if auto_ingest is not None:
                source.auto_ingest = auto_ingest
            if title:
                source.title = title
            source.last_error = last_error
            return True

    async def remove_source(self, chat_id: int) -> bool:
        async with self.db.session() as session:
            source = await session.scalar(select(SourceChat).where(SourceChat.chat_id == chat_id))
            if source is None:
                return False
            await session.delete(source)
            return True

    async def touch_source(self, source_id: int) -> None:
        async with self.db.session() as session:
            source = await session.get(SourceChat, source_id)
            if source is not None:
                source.last_message_at = utcnow()
                source.last_error = None

    async def find_by_message(self, source_chat_id: int, message_id: int) -> MediaAsset | None:
        async with self.db.session() as session:
            return await session.scalar(
                select(MediaAsset).where(
                    MediaAsset.source_chat_id == source_chat_id,
                    MediaAsset.source_message_id == message_id,
                )
            )

    async def find_by_unique_id(self, file_unique_id: str) -> MediaAsset | None:
        async with self.db.session() as session:
            return await session.scalar(
                select(MediaAsset).where(MediaAsset.file_unique_id == file_unique_id)
            )

    async def add(
        self,
        *,
        source_chat_id: int,
        source_message_id: int,
        file_id: str,
        file_unique_id: str,
        media_type: str,
        mime_type: str | None,
        file_name: str | None,
        file_size: int | None,
        duration_seconds: float | None,
        width: int | None,
        height: int | None,
        thumbnail_file_id: str | None,
        original_caption: str | None,
        status: MediaStatus,
        error_message: str | None = None,
    ) -> MediaAsset:
        async with self.db.session() as session:
            media = MediaAsset(
                source_chat_id=source_chat_id,
                source_message_id=source_message_id,
                file_id=file_id,
                file_unique_id=file_unique_id,
                media_type=media_type,
                mime_type=mime_type,
                file_name=file_name,
                file_size=file_size,
                duration_seconds=duration_seconds,
                width=width,
                height=height,
                thumbnail_file_id=thumbnail_file_id,
                original_caption=original_caption,
                status=status,
                error_message=error_message,
            )
            session.add(media)
            await session.flush()
            return media

    async def get(self, media_id: int, *, with_source: bool = False) -> MediaAsset | None:
        async with self.db.session() as session:
            statement = select(MediaAsset).where(MediaAsset.id == media_id)
            if with_source:
                statement = statement.options(selectinload(MediaAsset.source_chat))
            return await session.scalar(statement)

    async def list(
        self,
        *,
        statuses: list[MediaStatus] | None = None,
        limit: int = 10,
        offset: int = 0,
    ) -> list[MediaAsset]:
        async with self.db.session() as session:
            statement = (
                select(MediaAsset)
                .options(selectinload(MediaAsset.source_chat))
                .order_by(MediaAsset.received_at.desc())
                .limit(limit)
                .offset(offset)
            )
            if statuses:
                statement = statement.where(MediaAsset.status.in_(statuses))
            return list(await session.scalars(statement))

    async def count(self, statuses: list[MediaStatus] | None = None) -> int:
        async with self.db.session() as session:
            statement = select(func.count(MediaAsset.id))
            if statuses:
                statement = statement.where(MediaAsset.status.in_(statuses))
            return int(await session.scalar(statement) or 0)

    async def set_status(
        self, media_id: int, status: MediaStatus, error_message: str | None = None
    ) -> bool:
        async with self.db.session() as session:
            media = await session.get(MediaAsset, media_id)
            if media is None:
                return False
            media.status = status
            media.error_message = error_message
            return True
