"""
Base model classes for OpenScientist database models.

Provides declarative base and common mixins for all ORM models.
"""

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

from sqlalchemy import DateTime, text
from sqlalchemy.dialects.postgresql import UUID as PGUUID
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column


class Base(DeclarativeBase):
    """
    Base class for all SQLAlchemy ORM models.

    Provides the declarative base that all models inherit from.
    """


class UUIDv7Mixin:
    """
    Mixin providing UUIDv7 primary key and timestamp tracking.

    UUIDv7 advantages:
    - Time-ordered: Can be sorted chronologically
    - Globally unique: No collision risk across distributed systems
    - Database-efficient: Better index performance than UUIDv4
    - Native PostgreSQL 18 support via pg_uuidv7 extension

    Attributes:
        id: UUIDv7 primary key, generated by PostgreSQL
        created_at: Timestamp when record was created (UTC)
        updated_at: Timestamp when record was last updated (UTC)
    """

    id: Mapped[UUID] = mapped_column(
        PGUUID(as_uuid=True),
        primary_key=True,
        server_default=text("uuidv7()"),
        comment="UUIDv7 primary key generated by PostgreSQL",
    )

    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True),
        nullable=False,
        server_default=text("CURRENT_TIMESTAMP"),
        comment="Timestamp when record was created (UTC)",
    )

    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True),
        nullable=False,
        server_default=text("CURRENT_TIMESTAMP"),
        onupdate=lambda: datetime.now(UTC),
        comment="Timestamp when record was last updated (UTC)",
    )

    def __repr__(self) -> str:
        """String representation of the model instance."""
        class_name = self.__class__.__name__
        return f"<{class_name}(id={self.id})>"

    def to_dict(self) -> dict[str, Any]:
        """
        Convert model instance to dictionary.

        Returns:
            dict: Dictionary representation of the model.
        """
        result = {}
        for column in self.__table__.columns:  # type: ignore[attr-defined]
            value = getattr(self, column.name)
            # Convert datetime to ISO format string
            if isinstance(value, datetime):
                value = value.isoformat()
            # Convert UUID to string
            elif isinstance(value, UUID):
                value = str(value)
            result[column.name] = value
        return result
