"""
Base ORM class for SQLAlchemy models.

SQLAlchemy 2.0+ implementation with enhanced type safety and performance.
"""

from __future__ import annotations

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

from sqlalchemy import inspect
from sqlalchemy.orm import DeclarativeBase, declared_attr, Mapped, mapped_column
from sqlalchemy.util import symbol

if TYPE_CHECKING:
    from sqlalchemy.orm.base import InspectionAttr


class Base(DeclarativeBase):
    """
    Enhanced ORM base class with SQLAlchemy 2.0+ features.
    
    Provides automatic table naming, JSON serialization, and hybrid property support.
    Uses modern type annotations and SQLAlchemy 2.0 declarative syntax.
    """

    @declared_attr.directive
    def __tablename__(cls) -> str:
        """
        Generate __tablename__ for the SQLAlchemy model.
        
        Returns the explicit tablename if set, otherwise converts class name to lowercase.
        """
        if hasattr(cls, '__tablename__') and cls.__tablename__:
            return cls.__tablename__()
        return cls.__name__.lower()

    def _is_hybrid_property(self, orm_descriptor: InspectionAttr) -> bool:
        """Check if a model property is a hybrid property."""
        return orm_descriptor.extension_type == symbol("HYBRID_PROPERTY")

    def _is_hybrid_method(self, orm_descriptor: InspectionAttr) -> bool:
        """Check if a model property is a hybrid method."""
        return orm_descriptor.extension_type == symbol("HYBRID_METHOD")

    def _prepare_json(self, data: dict[str, Any]) -> dict[str, Any]:
        """
        Convert a dictionary to a JSON-serializable format.
        
        Handles UUID and datetime objects by converting them to strings.
        Recursively processes nested dictionaries.
        
        Args:
            data: Dictionary to convert
            
        Returns:
            JSON-serializable dictionary
        """
        result = {}
        for key, value in data.items():
            if isinstance(value, UUID):
                result[key] = str(value)
            elif isinstance(value, datetime):
                result[key] = value.isoformat()
            elif isinstance(value, dict):
                result[key] = self._prepare_json(value)
            else:
                result[key] = value
        return result

    def _asdict(self, include_properties: bool = True) -> dict[str, Any]:
        """
        Convert SQLAlchemy model to dictionary.
        
        If the model has joined attributes, they will be transformed to nested dictionaries.
        If include_properties is True, hybrid properties and methods will be included.
        
        Args:
            include_properties: Whether to include hybrid properties/methods
            
        Returns:
            Dictionary representation of the model
        """
        mapper = inspect(self).mapper
        orm_descriptors = mapper.all_orm_descriptors
        
        # Get regular attributes that have values
        defined_names = list(orm_descriptors.keys())
        results = {
            column: getattr(self, column)
            for column in defined_names
            if hasattr(self, column) and getattr(self, column) is not None
        }
        
        # Add hybrid properties and methods if requested
        if include_properties:
            hybrid_names = [
                key
                for key, descriptor in orm_descriptors.items()
                if self._is_hybrid_property(descriptor) or self._is_hybrid_method(descriptor)
            ]
            
            for name in hybrid_names:
                try:
                    # Only add if the property is accessible and not None
                    value = getattr(self, name)
                    if value is not None:
                        results[name] = value
                except (AttributeError, Exception):
                    # Skip properties that can't be accessed
                    continue

        return self._prepare_json(results)
