"""MySQL persistence for admin refresh tokens (durability layer behind Redis)."""

from __future__ import annotations

import datetime
from datetime import timezone

from sqlalchemy import delete, select
from sqlalchemy.ext.asyncio import AsyncSession

from server.domain.models import AdminRefreshToken


def _utcnow() -> datetime.datetime:
    return datetime.datetime.now(timezone.utc)


class RefreshTokenRepositoryImpl:
    def __init__(self, session: AsyncSession):
        self.session = session

    async def add(self, admin_id: int, token_hash: str, expires_at: datetime.datetime) -> None:
        row = AdminRefreshToken(
            admin_id=admin_id,
            token_hash=token_hash,
            expires_at=expires_at,
        )
        self.session.add(row)
        await self.session.commit()

    async def exists_valid(self, admin_id: int, token_hash: str) -> bool:
        now = _utcnow()
        result = await self.session.execute(
            select(AdminRefreshToken.id)
            .where(AdminRefreshToken.admin_id == admin_id)
            .where(AdminRefreshToken.token_hash == token_hash)
            .where(AdminRefreshToken.expires_at > now)
            .limit(1)
        )
        return result.scalar_one_or_none() is not None

    async def rotate(
        self,
        admin_id: int,
        old_hash: str,
        new_hash: str,
        expires_at: datetime.datetime,
    ) -> bool:
        """Delete old hash if present and insert new; returns False if old was missing."""
        now = _utcnow()
        result = await self.session.execute(
            select(AdminRefreshToken)
            .where(AdminRefreshToken.admin_id == admin_id)
            .where(AdminRefreshToken.token_hash == old_hash)
            .where(AdminRefreshToken.expires_at > now)
            .limit(1)
        )
        old_row = result.scalar_one_or_none()
        if not old_row:
            await self.session.rollback()
            return False
        await self.session.delete(old_row)
        self.session.add(
            AdminRefreshToken(
                admin_id=admin_id,
                token_hash=new_hash,
                expires_at=expires_at,
            )
        )
        await self.session.commit()
        return True

    async def remove(self, admin_id: int, token_hash: str) -> None:
        await self.session.execute(
            delete(AdminRefreshToken)
            .where(AdminRefreshToken.admin_id == admin_id)
            .where(AdminRefreshToken.token_hash == token_hash)
        )
        await self.session.commit()

    async def delete_all_for_admin(self, admin_id: int) -> None:
        await self.session.execute(
            delete(AdminRefreshToken).where(AdminRefreshToken.admin_id == admin_id)
        )
        await self.session.commit()

    async def delete_expired(self) -> int:
        now = _utcnow()
        result = await self.session.execute(
            delete(AdminRefreshToken).where(AdminRefreshToken.expires_at <= now)
        )
        await self.session.commit()
        return result.rowcount or 0

    async def list_valid_grouped(self) -> dict[int, list[str]]:
        """All non-expired token hashes grouped by admin_id (for Redis hydration)."""
        now = _utcnow()
        result = await self.session.execute(
            select(AdminRefreshToken.admin_id, AdminRefreshToken.token_hash)
            .where(AdminRefreshToken.expires_at > now)
        )
        grouped: dict[int, list[str]] = {}
        for admin_id, token_hash in result.all():
            grouped.setdefault(admin_id, []).append(token_hash)
        return grouped
