"""Desktop-auth data access."""
from __future__ import annotations
from datetime import datetime, timezone
from typing import Optional
from uuid import UUID, uuid4

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

from src.domain.desktop_auth.models import DesktopAuthRequest, DesktopAuthStatus, DesktopHandoffToken


class DesktopAuthRequestRepository:
    def __init__(self, db: AsyncSession):
        self._db = db

    async def create(
        self,
        *,
        login_code: str,
        user_code: str,
        expires_at: datetime,
        app_name: Optional[str] = None,
        os: Optional[str] = None,
    ) -> DesktopAuthRequest:
        row = DesktopAuthRequest(
            id=uuid4(),
            login_code=login_code,
            user_code=user_code,
            status=DesktopAuthStatus.pending.value,
            expires_at=expires_at,
            app_name=app_name,
            os=os,
        )
        self._db.add(row)
        await self._db.flush()
        await self._db.refresh(row)
        return row

    async def get_by_login_code(self, login_code: str) -> Optional[DesktopAuthRequest]:
        result = await self._db.execute(
            select(DesktopAuthRequest).where(DesktopAuthRequest.login_code == login_code)
        )
        return result.scalar_one_or_none()

    async def get_by_user_code(self, user_code: str) -> Optional[DesktopAuthRequest]:
        result = await self._db.execute(
            select(DesktopAuthRequest).where(DesktopAuthRequest.user_code == user_code)
        )
        return result.scalar_one_or_none()

    async def mark_completed(self, row: DesktopAuthRequest, user_id: UUID) -> DesktopAuthRequest:
        row.user_id = user_id
        row.status = DesktopAuthStatus.completed.value
        row.completed_at = datetime.now(timezone.utc)
        await self._db.flush()
        return row

    async def mark_completed_if_pending(self, request_id: UUID, user_id: UUID) -> bool:
        result = await self._db.execute(
            update(DesktopAuthRequest)
            .where(DesktopAuthRequest.id == request_id)
            .where(DesktopAuthRequest.status == DesktopAuthStatus.pending.value)
            .values(
                user_id=user_id,
                status=DesktopAuthStatus.completed.value,
                completed_at=datetime.now(timezone.utc),
            )
        )
        await self._db.flush()
        return result.rowcount == 1

    async def mark_exchanged(self, row: DesktopAuthRequest) -> DesktopAuthRequest:
        row.status = DesktopAuthStatus.exchanged.value
        row.exchanged_at = datetime.now(timezone.utc)
        await self._db.flush()
        return row

    async def mark_exchanged_if_completed(self, request_id: UUID) -> bool:
        result = await self._db.execute(
            update(DesktopAuthRequest)
            .where(DesktopAuthRequest.id == request_id)
            .where(DesktopAuthRequest.status == DesktopAuthStatus.completed.value)
            .where(DesktopAuthRequest.exchanged_at.is_(None))
            .values(
                status=DesktopAuthStatus.exchanged.value,
                exchanged_at=datetime.now(timezone.utc),
            )
        )
        await self._db.flush()
        return result.rowcount == 1


class DesktopHandoffTokenRepository:
    def __init__(self, db: AsyncSession):
        self._db = db

    async def create(
        self,
        *,
        token_hash: str,
        user_id: UUID,
        next_path: str,
        expires_at: datetime,
    ) -> DesktopHandoffToken:
        row = DesktopHandoffToken(
            id=uuid4(),
            token_hash=token_hash,
            user_id=user_id,
            next_path=next_path,
            expires_at=expires_at,
        )
        self._db.add(row)
        await self._db.flush()
        await self._db.refresh(row)
        return row

    async def get_by_token_hash(self, token_hash: str) -> Optional[DesktopHandoffToken]:
        result = await self._db.execute(
            select(DesktopHandoffToken).where(DesktopHandoffToken.token_hash == token_hash)
        )
        return result.scalar_one_or_none()

    async def mark_used(self, row: DesktopHandoffToken) -> DesktopHandoffToken:
        row.used_at = datetime.now(timezone.utc)
        await self._db.flush()
        return row

    async def mark_used_if_unused(self, handoff_id: UUID, *, now: datetime) -> bool:
        result = await self._db.execute(
            update(DesktopHandoffToken)
            .where(DesktopHandoffToken.id == handoff_id)
            .where(DesktopHandoffToken.used_at.is_(None))
            .where(DesktopHandoffToken.expires_at > now)
            .values(used_at=now)
        )
        await self._db.flush()
        return result.rowcount == 1
