"""Telegram media file repository — typed CRUD with dual-backend sync."""

from __future__ import annotations

import logging
from typing import Any

from repo_core import BaseRepository, hook
from repo_core.domains.telegram.media.model import TelegramMediaFile

logger = logging.getLogger(__name__)


class TelegramMediaFileRepository(BaseRepository[TelegramMediaFile]):
    """CRUD for ``telegram_media_files`` with PostgreSQL → Doris sync.

    Writes to both PostgreSQL (primary) and Doris (warehouse) so
    analytics dashboards can query media download statistics
    (bytes per channel, media type distribution, download velocity).

    Usage::

        repo = TelegramMediaFileRepository(model=TelegramMediaFile)
        async with repo:
            created = await repo.create_media_file(instance)
            media = await repo.get_media_by_message(peer_id=123, message_id=456)
    """

    TABLE = "telegram_media_files"
    _backends = {"db", "doris"}

    # ── Internal helpers ───────────────────────────────────────────────────

    async def _warehouse_upsert(self, media: TelegramMediaFile) -> None:
        """Insert or update the media file row in the Doris warehouse.

        Strips PostgreSQL-only fields before writing to Doris.
        """
        data = media.model_dump(mode="json")

        # Keep ONLY columns that exist in the Doris schema
        doris_columns = {
            "peer_id", "message_id",
            "storage_key", "bucket",
            "file_size", "mime_type", "media_type",
            "width", "height", "duration_seconds",
            "downloaded_at", "created_at", "updated_at",
        }
        data = {k: v for k, v in data.items() if k in doris_columns}

        # Serialize datetimes for Doris compatibility
        for key in ("downloaded_at", "created_at", "updated_at"):
            val = data.get(key)
            if val is not None and hasattr(val, "isoformat"):
                data[key] = val.isoformat()

        existing = await self.warehouse_first(
            self.TABLE,
            {"peer_id": data["peer_id"], "message_id": data["message_id"]},
        )
        if existing:
            await self.warehouse_update(
                self.TABLE,
                {"peer_id": data["peer_id"], "message_id": data["message_id"]},
                data,
            )
        else:
            await self.warehouse_insert_one(self.TABLE, data)

    # ── Convenience methods ────────────────────────────────────────────────

    async def create_media_file(
        self, instance: TelegramMediaFile
    ) -> TelegramMediaFile:
        """Insert a new media file record.

        Validates, emits hooks, and syncs to Doris.
        """
        data = instance.to_db()
        await self.emit("before_create", data)
        created = await self.insert_one(self.TABLE, data, returning=["*"])
        await self.emit("after_create", created)
        return created

    async def get_media_by_message(
        self, peer_id: int, message_id: int
    ) -> TelegramMediaFile | None:
        """Look up a media file by its composite logical key."""
        row = await self.first(
            self.TABLE,
            {"peer_id": peer_id, "message_id": message_id},
        )
        if row is None:
            return None
        return self.model.from_db(row)

    async def find_media_by_peer(
        self,
        peer_id: int,
        *,
        media_type: str | None = None,
        limit: int = 100,
        offset: int = 0,
    ) -> list[TelegramMediaFile]:
        """List media files for a given peer, optionally filtered by type."""
        conditions: dict[str, Any] = {"peer_id": peer_id}
        if media_type is not None:
            conditions["media_type"] = media_type
        rows = await self.find(
            self.TABLE, conditions, limit=limit, offset=offset,
            order_by=["-downloaded_at"],
        )
        return [self.model.from_db(r) for r in rows]

    async def update_media_file(
        self, peer_id: int, message_id: int, values: dict[str, Any]
    ) -> TelegramMediaFile | None:
        """Update a media file record by composite key.

        Emits ``after_update`` so the Doris warehouse stays in sync.
        Returns the updated model instance, or ``None`` if no row matched.
        """
        conditions = {"peer_id": peer_id, "message_id": message_id}
        self._inject_model_updated_at(values)
        await self.emit("before_update", values)
        await self.update(self.TABLE, conditions, values, limit=1)
        updated = await self.first(self.TABLE, conditions)
        if updated is not None:
            await self.emit("after_update", self.model.from_db(updated))
        return updated

    async def delete_media_file(
        self, peer_id: int, message_id: int
    ) -> int:
        """Delete a media file by composite key.

        Emits ``after_delete`` so the Doris warehouse row is removed.
        Returns the number of deleted rows (0 or 1).
        """
        conditions = {"peer_id": peer_id, "message_id": message_id}
        media = await self.first(self.TABLE, conditions)
        await self.emit("before_delete", conditions)
        deleted = await self.remove(self.TABLE, conditions)
        if media is not None:
            await self.emit("after_delete", self.model.from_db(media))
        return deleted

    # Aliases for consistency
    create = create_media_file
    get_by_message = get_media_by_message
