import uuid
from collections.abc import Sequence
from typing import Any

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

from src.domain.entities import APIClientEntity, APIKeyEntity
from src.domain.interfaces import IAPIClientRepository, IAPIKeyRepository
from src.infrastructure.database.models import APIClientModel, APIKeyModel


class APIClientRepository(IAPIClientRepository):
    def __init__(self, session: AsyncSession) -> None:
        self._session = session

    async def get_by_id(self, client_id: uuid.UUID) -> APIClientEntity | None:
        stmt = select(APIClientModel).where(APIClientModel.id == client_id)
        result = await self._session.execute(stmt)
        m = result.scalar_one_or_none()
        if not m:
            return None
        return APIClientEntity(
            id=m.id, name=m.name, description=m.description, owner_id=m.owner_id,
            is_active=m.is_active, scopes=m.scopes or [], rate_limit=m.rate_limit,
            allowed_ips=m.allowed_ips or [], metadata_=m.metadata_, created_at=m.created_at, updated_at=m.updated_at,
        )

    async def list_clients(self, *, is_active: bool | None = None, offset: int = 0, limit: int = 50) -> tuple[Sequence[APIClientEntity], int]:
        filters: list[Any] = []
        if is_active is not None:
            filters.append(APIClientModel.is_active == is_active)
        count_stmt = select(func.count()).select_from(APIClientModel)
        if filters:
            count_stmt = count_stmt.where(*filters)
        total = (await self._session.execute(count_stmt)).scalar() or 0
        stmt = select(APIClientModel).order_by(APIClientModel.name).offset(offset).limit(limit)
        if filters:
            stmt = stmt.where(*filters)
        result = await self._session.execute(stmt)
        models = result.scalars().all()
        entities = [
            APIClientEntity(id=m.id, name=m.name, description=m.description, owner_id=m.owner_id,
                           is_active=m.is_active, scopes=m.scopes or [], rate_limit=m.rate_limit,
                           allowed_ips=m.allowed_ips or [], metadata_=m.metadata_, created_at=m.created_at, updated_at=m.updated_at)
            for m in models
        ]
        return entities, total

    async def create(self, entity: APIClientEntity) -> APIClientEntity:
        model = APIClientModel(
            name=entity.name, description=entity.description, owner_id=entity.owner_id,
            is_active=entity.is_active, scopes=entity.scopes, rate_limit=entity.rate_limit,
            allowed_ips=entity.allowed_ips, metadata_=entity.metadata_,
        )
        self._session.add(model)
        await self._session.flush()
        return await self.get_by_id(model.id)  # type: ignore[return-value]

    async def update(self, entity: APIClientEntity) -> APIClientEntity:
        stmt = update(APIClientModel).where(APIClientModel.id == entity.id).values(
            name=entity.name, description=entity.description, is_active=entity.is_active,
            scopes=entity.scopes, rate_limit=entity.rate_limit, allowed_ips=entity.allowed_ips,
            metadata_=entity.metadata_,
        )
        await self._session.execute(stmt)
        await self._session.flush()
        return await self.get_by_id(entity.id)  # type: ignore[return-value]


class APIKeyRepository(IAPIKeyRepository):
    def __init__(self, session: AsyncSession) -> None:
        self._session = session

    async def get_by_prefix(self, key_prefix: str) -> APIKeyEntity | None:
        stmt = select(APIKeyModel).where(APIKeyModel.key_prefix == key_prefix, APIKeyModel.is_active == True)  # noqa: E712
        result = await self._session.execute(stmt)
        m = result.scalar_one_or_none()
        if not m:
            return None
        return APIKeyEntity(
            id=m.id, api_client_id=m.api_client_id, key_prefix=m.key_prefix,
            key_hash=m.key_hash, name=m.name, is_active=m.is_active,
            expires_at=m.expires_at, last_used_at=m.last_used_at, created_at=m.created_at,
        )

    async def list_by_client(self, api_client_id: uuid.UUID) -> Sequence[APIKeyEntity]:
        stmt = select(APIKeyModel).where(APIKeyModel.api_client_id == api_client_id).order_by(APIKeyModel.created_at.desc())
        result = await self._session.execute(stmt)
        models = result.scalars().all()
        return [
            APIKeyEntity(
                id=m.id, api_client_id=m.api_client_id, key_prefix=m.key_prefix,
                key_hash=m.key_hash, name=m.name, is_active=m.is_active,
                expires_at=m.expires_at, last_used_at=m.last_used_at, created_at=m.created_at,
            )
            for m in models
        ]

    async def create(self, entity: APIKeyEntity) -> APIKeyEntity:
        model = APIKeyModel(
            api_client_id=entity.api_client_id, key_prefix=entity.key_prefix,
            key_hash=entity.key_hash, name=entity.name, is_active=entity.is_active,
            expires_at=entity.expires_at,
        )
        self._session.add(model)
        await self._session.flush()
        return APIKeyEntity(
            id=model.id, api_client_id=model.api_client_id, key_prefix=model.key_prefix,
            key_hash=model.key_hash, name=model.name, is_active=model.is_active,
            expires_at=model.expires_at, created_at=model.created_at,
        )

    async def update_last_used(self, key_id: uuid.UUID) -> None:
        stmt = update(APIKeyModel).where(APIKeyModel.id == key_id).values(last_used_at=func.now())
        await self._session.execute(stmt)
