"""
Repository for system and security audit logs.
"""

from typing import Any
from uuid import UUID

from sqlalchemy import func, or_, select
from sqlalchemy.ext.asyncio import AsyncSession

from app.models.audit import AuditCategory, AuditLog, AuditSeverity


class AuditRepository:
    """Data access layer for immutable audit trail events."""

    async def create_log(
        self,
        db: AsyncSession,
        lab_id: UUID,
        action: str,
        category: str = AuditCategory.ADMIN.value,
        severity: str = AuditSeverity.INFO.value,
        actor_name: str = "System Administrator",
        actor_role: str = "owner",
        actor_id: UUID | None = None,
        target_type: str | None = None,
        target_id: str | None = None,
        description: str = "",
        ip_address: str | None = "127.0.0.1",
        details: dict[str, Any] | None = None,
    ) -> AuditLog:
        """Append an immutable audit entry."""
        log = AuditLog(
            lab_id=lab_id,
            action=action,
            category=category,
            severity=severity,
            actor_name=actor_name,
            actor_role=actor_role,
            actor_id=actor_id,
            target_type=target_type,
            target_id=target_id,
            description=description,
            ip_address=ip_address,
            details=details,
        )
        db.add(log)
        await db.flush()
        await db.refresh(log)
        return log

    async def list_logs(
        self,
        db: AsyncSession,
        lab_id: UUID,
        category: str | None = None,
        severity: str | None = None,
        search: str | None = None,
        limit: int = 50,
        offset: int = 0,
    ) -> tuple[list[AuditLog], int]:
        """List audit logs matching optional filters."""
        stmt = select(AuditLog).where(AuditLog.lab_id == lab_id)

        if category and category != "ALL":
            stmt = stmt.where(AuditLog.category == category)
        if severity and severity != "ALL":
            stmt = stmt.where(AuditLog.severity == severity)
        if search:
            pattern = f"%{search.strip()}%"
            stmt = stmt.where(
                or_(
                    AuditLog.action.ilike(pattern),
                    AuditLog.description.ilike(pattern),
                    AuditLog.actor_name.ilike(pattern),
                    AuditLog.target_type.ilike(pattern),
                    AuditLog.target_id.ilike(pattern),
                )
            )

        count_stmt = select(func.count()).select_from(stmt.subquery())
        total = (await db.execute(count_stmt)).scalar() or 0

        stmt = stmt.order_by(AuditLog.created_at.desc()).limit(limit).offset(offset)
        result = await db.execute(stmt)
        return list(result.scalars().all()), total

    async def get_metrics(self, db: AsyncSession, lab_id: UUID) -> dict[str, int]:
        """Aggregate event counts by functional category."""
        counts = {"total": 0, "security": 0, "clinical": 0, "admin": 0, "billing": 0}

        base_stmt = (
            select(AuditLog.category, func.count())
            .where(AuditLog.lab_id == lab_id)
            .group_by(AuditLog.category)
        )
        res = await db.execute(base_stmt)
        for cat, cnt in res.all():
            counts["total"] += cnt
            c_upper = (cat or "").upper()
            if c_upper == AuditCategory.SECURITY.value:
                counts["security"] += cnt
            elif c_upper == AuditCategory.CLINICAL.value:
                counts["clinical"] += cnt
            elif c_upper == AuditCategory.BILLING.value:
                counts["billing"] += cnt
            else:
                counts["admin"] += cnt

        return counts
