"""Telegram account repository — typed CRUD via BaseRepository backends.

All persistence is delegated to :class:`BaseRepository` methods — no raw
SQL or SQLAlchemy imports.  Account-specific hooks (logging) are kept here.
"""

from __future__ import annotations

import json
import logging
import uuid
from datetime import datetime
from typing import Any

from repo_core import BaseRepository, hook
from repo_core.domains.telegram.account.model import TelegramAccount

logger = logging.getLogger(__name__)


class TelegramAccountRepository(BaseRepository[TelegramAccount]):
    """CRUD for ``telegram_accounts`` via BaseRepository backends.

    Usage::

        repo = TelegramAccountRepository(
            model=TelegramAccount,
            db=create_postgres_client(),
        )
        async with repo:
            account = await repo.create_account({...})
            results = await repo.list_all(20, 0)
    """

    TABLE = "telegram_accounts"

    # -- Hooks -------------------------------------------------------------

    @hook("after_create")
    async def _log_create(self, account: TelegramAccount) -> None:
        logger.debug("Telegram account created: id=%s", account.id)

    @hook("after_update")
    async def _log_update(self, account: TelegramAccount) -> None:
        logger.debug("Telegram account updated: id=%s", account.id)

    @hook("after_delete")
    async def _log_delete(self, account: TelegramAccount) -> None:
        logger.debug("Telegram account deleted: id=%s", account.id)

    # -- CRUD --------------------------------------------------------------

    async def create_account(self, data: dict[str, Any]) -> TelegramAccount:
        """Insert a new row and return the domain-model instance."""
        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_by_id(self, account_id: uuid.UUID) -> TelegramAccount | None:
        return await self.find_by_id(self.TABLE, account_id)

    async def list_all(
        self, limit: int, offset: int
    ) -> tuple[list[TelegramAccount], int]:
        total = await self.count(self.TABLE)
        rows = await self.all(
            self.TABLE,
            limit=limit,
            offset=offset,
            order_by=["-created_at"],
        )
        return rows, total

    async def list_updated_since(
        self,
        since: datetime,
        limit: int,
        offset: int,
        *,
        is_platform_manager: bool | None = None,
    ) -> tuple[list[TelegramAccount], int]:
        """Return accounts whose ``updated_at`` is after *since*.

        Used for polling-based UI updates — only changed rows are
        transferred, keeping payloads small and rendering efficient.

        Optionally filtered by *is_platform_manager* so the caller can
        scope polling to managers or targets only.
        """
        qb = self.table(self.TABLE).where_gt("updated_at", since)
        if is_platform_manager is not None:
            qb = qb.where_eq("is_platform_manager", is_platform_manager)
        total = await qb.count()
        rows = await qb.order_by("-updated_at").limit(limit).offset(offset).all()
        return self._from_db_many(rows), total

    async def delete_account(self, account_id: uuid.UUID) -> int:
        account = await self.get_by_id(account_id)
        if account is not None:
            await self.emit("before_delete", account)
        result = await self.remove_by_id(self.TABLE, account_id)
        if account is not None:
            await self.emit("after_delete", account)
        return result

    async def set_platform_manager(
        self, account_id: uuid.UUID, is_manager: bool,
    ) -> TelegramAccount | None:
        """Set or unset the is_platform_manager flag."""
        updated = await self.update_by_id(
            self.TABLE, account_id, {"is_platform_manager": is_manager}
        )
        if updated is not None:
            await self.emit("after_update", updated)
        return updated

    async def list_by_manager_flag(
        self, is_manager: bool, limit: int, offset: int,
    ) -> tuple[list[TelegramAccount], int]:
        """List accounts filtered by is_platform_manager."""
        rows = await self.find(
            self.TABLE,
            conditions={"is_platform_manager": is_manager},
            limit=limit,
            offset=offset,
            order_by=["-created_at"],
        )
        total = await self.count(
            self.TABLE, conditions={"is_platform_manager": is_manager}
        )
        return rows, total

    async def get_all_active_accounts(self) -> list[TelegramAccount]:
        """Return all accounts with status='active'.

        Used by AccountLeaseService to find unassigned accounts.
        """
        return await self.find(
            self.TABLE,
            conditions={"status": "active"},
        )

    async def update_metadata(
        self, account_id: uuid.UUID, metadata: dict[str, Any]
    ) -> TelegramAccount | None:
        updated = await self.update_by_id(
            self.TABLE, account_id, {"account_metadata": json.dumps(metadata)}
        )
        if updated is not None:
            await self.emit("after_update", updated)
        return updated

    async def update_profile(
        self, account_id: uuid.UUID, fields: dict[str, Any]
    ) -> TelegramAccount | None:
        """Update account profile fields (username, display_name, phone_number)."""
        allowed = {"username", "display_name", "phone_number"}
        values = {k: v for k, v in fields.items() if k in allowed and v is not None}
        if not values:
            return await self.get_by_id(account_id)
        updated = await self.update_by_id(self.TABLE, account_id, values)
        if updated is not None:
            await self.emit("after_update", updated)
        return updated

    async def update_status(
        self,
        account_id: uuid.UUID,
        status: str,
        flood_wait_until: Any = None,
    ) -> TelegramAccount | None:
        values: dict[str, Any] = {"status": status}
        if flood_wait_until is not None:
            values["flood_wait_until"] = flood_wait_until
        updated = await self.update_by_id(self.TABLE, account_id, values)
        if updated is not None:
            await self.emit("after_update", updated)
        return updated

    # Short aliases matching legacy names
    create = create_account
    delete = delete_account
