from datetime import datetime, timezone

from sqlalchemy import DateTime, Float, Index, Integer, Text
from sqlalchemy.orm import Mapped, mapped_column

from scribe.models import Base
from scribe.models.base import iso


class AppLog(Base):
    __tablename__ = "app_logs"

    id: Mapped[int] = mapped_column(primary_key=True)
    category: Mapped[str] = mapped_column(Text, nullable=False)
    user_id: Mapped[int | None] = mapped_column(Integer, nullable=True)
    username: Mapped[str | None] = mapped_column(Text, nullable=True)
    action: Mapped[str | None] = mapped_column(Text, nullable=True)
    endpoint: Mapped[str | None] = mapped_column(Text, nullable=True)
    method: Mapped[str | None] = mapped_column(Text, nullable=True)
    status_code: Mapped[int | None] = mapped_column(Integer, nullable=True)
    duration_ms: Mapped[float | None] = mapped_column(Float, nullable=True)
    ip_address: Mapped[str | None] = mapped_column(Text, nullable=True)
    details: Mapped[str | None] = mapped_column(Text, nullable=True)
    # Declared here rather than via CreatedAtMixin on purpose: the composite
    # index below orders on `created_at.desc()`, which needs the column object
    # in this class body — a mixin's column is not in scope there.
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
    )

    __table_args__ = (
        Index("ix_app_logs_category", "category"),
        Index("ix_app_logs_user_id", "user_id"),
        Index("ix_app_logs_created_at", "created_at"),
        Index("ix_app_logs_category_created_at", "category", created_at.desc()),
    )

    def to_dict(self) -> dict:
        return {
            "id": self.id,
            "category": self.category,
            "user_id": self.user_id,
            "username": self.username,
            "action": self.action,
            "endpoint": self.endpoint,
            "method": self.method,
            "status_code": self.status_code,
            "duration_ms": self.duration_ms,
            "ip_address": self.ip_address,
            "details": self.details,
            "created_at": iso(self.created_at),
        }
