"""project_auth domain — data access layer."""
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.project_auth.models import (
    AuthAssignmentTier,
    AuthKeyStatus,
    AuthRoleCreator,
    ProjectAuthConfig,
    ProjectAuthKey,
    ProjectAuthRole,
)


class ProjectAuthConfigRepository:
    def __init__(self, db: AsyncSession) -> None:
        self._db = db

    async def get_by_project(self, project_id: UUID) -> Optional[ProjectAuthConfig]:
        result = await self._db.execute(
            select(ProjectAuthConfig).where(ProjectAuthConfig.project_id == project_id)
        )
        return result.scalar_one_or_none()

    async def create(self, project_id: UUID, **fields) -> ProjectAuthConfig:
        row = ProjectAuthConfig(id=uuid4(), project_id=project_id, **fields)
        self._db.add(row)
        await self._db.flush()
        return row

    async def update(self, row: ProjectAuthConfig, **fields) -> ProjectAuthConfig:
        for k, v in fields.items():
            setattr(row, k, v)
        await self._db.flush()
        return row


class ProjectAuthKeyRepository:
    def __init__(self, db: AsyncSession) -> None:
        self._db = db

    async def get_active_for_project(self, project_id: UUID) -> Optional[ProjectAuthKey]:
        result = await self._db.execute(
            select(ProjectAuthKey).where(
                ProjectAuthKey.project_id == project_id,
                ProjectAuthKey.status == AuthKeyStatus.active,
            )
        )
        return result.scalar_one_or_none()

    async def list_for_project(self, project_id: UUID) -> list[ProjectAuthKey]:
        result = await self._db.execute(
            select(ProjectAuthKey)
            .where(ProjectAuthKey.project_id == project_id)
            .order_by(ProjectAuthKey.created_at.desc())
        )
        return list(result.scalars().all())

    async def create(
        self,
        *,
        project_id: UUID,
        key_id: str,
        public_key_pem: str,
        private_key_enc: str,
    ) -> ProjectAuthKey:
        row = ProjectAuthKey(
            id=uuid4(),
            project_id=project_id,
            key_id=key_id,
            status=AuthKeyStatus.active,
            public_key_pem=public_key_pem,
            private_key_enc=private_key_enc,
        )
        self._db.add(row)
        await self._db.flush()
        return row

    async def mark_rotated(self, key: ProjectAuthKey) -> ProjectAuthKey:
        key.status = AuthKeyStatus.rotated
        key.rotated_at = datetime.now(timezone.utc)
        await self._db.flush()
        return key


class ProjectAuthRoleRepository:
    def __init__(self, db: AsyncSession) -> None:
        self._db = db

    async def list_for_project(self, project_id: UUID) -> list[ProjectAuthRole]:
        result = await self._db.execute(
            select(ProjectAuthRole)
            .where(ProjectAuthRole.project_id == project_id)
            .order_by(ProjectAuthRole.created_at)
        )
        return list(result.scalars().all())

    async def get_by_name(self, project_id: UUID, name: str) -> Optional[ProjectAuthRole]:
        result = await self._db.execute(
            select(ProjectAuthRole).where(
                ProjectAuthRole.project_id == project_id,
                ProjectAuthRole.name == name,
            )
        )
        return result.scalar_one_or_none()

    async def create(
        self,
        *,
        project_id: UUID,
        name: str,
        description: Optional[str],
        is_default: bool,
        permissions_json: list,
        created_by: AuthRoleCreator,
        assignment_tier: Optional[AuthAssignmentTier] = None,
        protected: bool = False,
    ) -> ProjectAuthRole:
        row = ProjectAuthRole(
            id=uuid4(),
            project_id=project_id,
            name=name,
            description=description,
            is_default=is_default,
            permissions_json=permissions_json,
            created_by=created_by,
            assignment_tier=assignment_tier,
            protected=protected,
        )
        self._db.add(row)
        await self._db.flush()
        return row

    async def update(self, row: ProjectAuthRole, **fields) -> ProjectAuthRole:
        for k, v in fields.items():
            setattr(row, k, v)
        await self._db.flush()
        return row

    async def delete(self, row: ProjectAuthRole) -> None:
        await self._db.delete(row)
        await self._db.flush()

    async def clear_default_flag(self, project_id: UUID) -> None:
        """Clear is_default on all roles for a project before setting a new default."""
        await self._db.execute(
            update(ProjectAuthRole)
            .where(ProjectAuthRole.project_id == project_id)
            .values(is_default=False)
        )
