"""
AuditLog model for recording system security, administrative, and clinical mutations.
"""

import enum
from datetime import UTC, datetime
from typing import Any
from uuid import UUID

from sqlalchemy import DateTime, ForeignKey, String, Text, func
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.dialects.postgresql import UUID as PG_UUID
from sqlalchemy.orm import Mapped, mapped_column, relationship

from app.models.base import Base, LabScopedMixin, UUIDPrimaryKeyMixin


class AuditCategory(enum.StrEnum):
    SECURITY = "SECURITY"
    ADMIN = "ADMIN"
    CLINICAL = "CLINICAL"
    BILLING = "BILLING"
    SYSTEM = "SYSTEM"


class AuditSeverity(enum.StrEnum):
    INFO = "INFO"
    WARNING = "WARNING"
    CRITICAL = "CRITICAL"


class AuditLog(UUIDPrimaryKeyMixin, LabScopedMixin, Base):
    """
    Immutable system and security audit trail record.
    """

    __tablename__ = "audit_log"
    # Match the existing migration's FK/defaults; ORM values stay unchanged.
    lab_id: Mapped[UUID] = mapped_column(
        PG_UUID(as_uuid=True), ForeignKey("lab.id", ondelete="CASCADE"), nullable=False, index=True
    )

    action: Mapped[str] = mapped_column(String(100), nullable=False, index=True)
    category: Mapped[str] = mapped_column(
        String(50), nullable=False, default=AuditCategory.ADMIN.value, server_default="ADMIN", index=True
    )
    severity: Mapped[str] = mapped_column(
        String(20), nullable=False, default=AuditSeverity.INFO.value, server_default="INFO", index=True
    )
    actor_id: Mapped[UUID | None] = mapped_column(PG_UUID(as_uuid=True), nullable=True)
    actor_name: Mapped[str] = mapped_column(
        String(200), nullable=False, default="System Administrator", server_default="System Administrator"
    )
    actor_role: Mapped[str] = mapped_column(
        String(50), nullable=False, default="owner", server_default="owner"
    )
    target_type: Mapped[str | None] = mapped_column(String(100), nullable=True)
    target_id: Mapped[str | None] = mapped_column(String(100), nullable=True)
    description: Mapped[str] = mapped_column(Text, nullable=False)
    ip_address: Mapped[str | None] = mapped_column(
        String(50), nullable=True, default="127.0.0.1", server_default="127.0.0.1"
    )
    details: Mapped[dict[str, Any] | None] = mapped_column(JSONB, nullable=True)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True),
        nullable=False,
        default=lambda: datetime.now(UTC),
        server_default=func.now(),
        index=True,
    )

    # Relationship
    lab: Mapped["Lab"] = relationship(  # type: ignore[name-defined]
        "Lab",
        foreign_keys="[AuditLog.lab_id]",
        lazy="selectin",
    )
