# createsonline/ai/orm.py
"""
CREATESONLINE AI-Enhanced ORM

AI-powered database operations with smart queries and intelligent fields.
"""
from typing import Any, Dict, List, Optional, Type
import sqlalchemy as sa
from sqlalchemy.orm import sessionmaker, Session
from datetime import datetime

# Import CreatesonlineModel as base
from createsonline.database.models import CreatesonlineModel

class AIBaseModel(CreatesonlineModel):
    """
    Base model class with AI enhancements for CREATESONLINE applications
    
    Provides automatic AI field processing and intelligent database operations.
    Inherits from CreatesonlineModel for compatibility with CREATESONLINE database system.
    """
    __abstract__ = True
    
    def __init__(self, **kwargs):
        """Initialize model with AI field processing"""
        super().__init__(**kwargs)
        self._ai_fields = {}
        self._discover_ai_fields()
    
    def _discover_ai_fields(self):
        """Discover AI fields in this model"""
        from createsonline.ai.fields import AIFieldMixin
        
        for attr_name in dir(self.__class__):
            attr = getattr(self.__class__, attr_name)
            if isinstance(attr, type) and issubclass(attr, AIFieldMixin):
                self._ai_fields[attr_name] = attr
    
    async def compute_ai_fields(self):
        """Compute all AI fields for this instance"""
        from createsonline.ai.fields import AIComputedField, LLMField, VectorField
        
        for field_name, field in self._ai_fields.items():
            try:
                if isinstance(field, AIComputedField):
                    # Compute AI-predicted value
                    features = self._extract_features_for_field(field)
                    value = await field.compute_value(self, features)
                    setattr(self, field_name, value)
                    
                elif isinstance(field, LLMField):
                    # Generate content with LLM
                    template_data = self._extract_template_data_for_field(field)
                    content = await field.generate_content(self, template_data)
                    setattr(self, field_name, content)
                    
                elif isinstance(field, VectorField):
                    # Generate vector embedding
                    source_data = self._extract_source_data_for_field(field)
                    embedding = await field.generate_embedding(self, source_data)
                    setattr(self, field_name, embedding)
                    
            except Exception as e:
                print(f"AI field computation failed for {field_name}: {e}")
    
    def _extract_features_for_field(self, field) -> Dict[str, Any]:
        """Extract features for AI computation"""
        features = {}
        
        # Get features from field configuration
        feature_fields = field.ai_config.get('features', [])
        source_field = field.ai_config.get('source_field')
        
        if feature_fields:
            for feature_field in feature_fields:
                if hasattr(self, feature_field):
                    features[feature_field] = getattr(self, feature_field)
        elif source_field:
            if hasattr(self, source_field):
                features[source_field] = getattr(self, source_field)
        
        return features
    
    def _extract_template_data_for_field(self, field) -> Dict[str, Any]:
        """Extract template data for LLM generation"""
        template_data = {}
        
        # Extract all non-AI fields as template variables
        for attr_name in dir(self):
            if not attr_name.startswith('_') and hasattr(self, attr_name):
                value = getattr(self, attr_name)
                if isinstance(value, (str, int, float, bool)) and value is not None:
                    template_data[attr_name] = value
        
        return template_data
    
    def _extract_source_data_for_field(self, field) -> Any:
        """Extract source data for vector embedding"""
        source_field = field.ai_config.get('source_field')
        
        if source_field and hasattr(self, source_field):
            return getattr(self, source_field)
        
        # Fallback to description or name field
        for fallback_field in ['description', 'content', 'text', 'name']:
            if hasattr(self, fallback_field):
                return getattr(self, fallback_field)
        
        return str(self)
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert model to dictionary"""
        result = {}
        for column in self.__table__.columns:
            value = getattr(self, column.name)
            if isinstance(value, datetime):
                result[column.name] = value.isoformat()
            elif isinstance(value, (list, dict)):
                result[column.name] = value
            else:
                result[column.name] = value
        return result
    
    def __repr__(self) -> str:
        return f"<{self.__class__.__name__}(id={getattr(self, 'id', None)})>"

class AIEnhancedORM:
    """
    AI-Enhanced ORM for CREATESONLINE framework
    
    Provides intelligent database operations with AI capabilities.
    """
    
    def __init__(self, database_url: str, echo: bool = False):
        """
        Initialize AI-Enhanced ORM
        
        Args:
            database_url: Database connection string
            echo: Enable SQL logging
        """
        self.database_url = database_url
        self.engine = sa.create_engine(database_url, echo=echo)
        self.SessionLocal = sessionmaker(
            autocommit=False, 
            autoflush=False, 
            bind=self.engine
        )
        
        # AI services integration
        self._ai_enabled = True
        self._ai_cache = {}
    
    def create_tables(self):
        """Create all tables in the database"""
        from createsonline.database.models import SQLAlchemyBase
        SQLAlchemyBase.metadata.create_all(bind=self.engine)
    
    def drop_tables(self):
        """Drop all tables in the database"""
        from createsonline.database.models import SQLAlchemyBase
        SQLAlchemyBase.metadata.drop_all(bind=self.engine)
    
    def get_session(self) -> Session:
        """Get a database session"""
        return self.SessionLocal()
    
    @staticmethod
    def _to_snake_case(name: str) -> str:
        """Convert ``CamelCase`` identifiers into ``snake_case`` table names."""

        import re

        if not name:
            return name

        snake = re.sub(r"(.)([A-Z][a-z]+)", r"\1_\2", name)
        snake = re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", snake)
        return snake.replace("__", "_").strip("_").lower()

    async def smart_query(
        self,
        model: Type[AIBaseModel],
        natural_language_query: str
    ) -> List[AIBaseModel]:
        """
        Execute a query using natural language
        
        Args:
            model: Model class to query
            natural_language_query: Natural language query string
            
        Returns:
            List of query results
        """
        # TODO: Implement natural language to SQL transformation
        # For now, return basic query
        session = self.get_session()
        try:
            # Simple implementation - would be enhanced with AI
            query = session.query(model)
            
            # Basic keyword matching
            if 'recent' in natural_language_query.lower():
                query = query.order_by(model.created_at.desc())
            
            if 'limit' in natural_language_query.lower():
                # Extract number after 'limit'
                import re
                match = re.search(r'limit\s+(\d+)', natural_language_query.lower())
                if match:
                    limit = int(match.group(1))
                    query = query.limit(limit)
            
            return query.all()
            
        finally:
            session.close()
    
    def create_model_from_description(
        self,
        description: str,
        model_name: str = "GeneratedModel"
    ) -> Type[AIBaseModel]:
        """
        Generate a model class from natural language description
        
        Args:
            description: Natural language description of the model
            model_name: Name for the generated model
            
        Returns:
            Generated model class
        """
        # TODO: Implement AI-driven schema generation
        # For now, create a basic model
        
        attributes = {
            '__tablename__': self._to_snake_case(model_name),
            'name': sa.Column(sa.String(255), nullable=False),
            'description': sa.Column(sa.Text),
        }
        
        # Create dynamic model class
        GeneratedModel = type(model_name, (AIBaseModel,), attributes)

        return GeneratedModel

    def generate_schema_from_description(
        self,
        description: str,
        model_name: str = "GeneratedModel"
    ) -> Type[AIBaseModel]:
        """Backward compatible wrapper for deprecated API usage.

        Earlier revisions of the project exposed ``generate_schema_from_description``
        which delegated to the dynamic model creation helper.  The method was
        renamed during refactoring, but parts of the codebase – including the
        published tests – still rely on the original name.  Provide the thin
        alias so existing integrations continue to function while new code can
        adopt :meth:`create_model_from_description`.
        """

        return self.create_model_from_description(description, model_name)
    
    async def predict_related_entities(
        self, 
        instance: AIBaseModel
    ) -> Dict[str, List[Any]]:
        """
        Predict related entities using AI
        
        Args:
            instance: Model instance
            
        Returns:
            Dictionary of predicted related entities
        """
        # TODO: Implement AI-based relationship prediction
        return {}
    
    def get_model_statistics(self, model: Type[AIBaseModel]) -> Dict[str, Any]:
        """Get statistics for a model"""
        session = self.get_session()
        try:
            total_count = session.query(model).count()
            
            # Recent activity (last 30 days)
            thirty_days_ago = datetime.utcnow().replace(day=1)  # Simplified
            recent_count = session.query(model).filter(
                model.created_at >= thirty_days_ago
            ).count()
            
            return {
                'total_records': total_count,
                'recent_records': recent_count,
                'model_name': model.__name__,
                'table_name': model.__tablename__
            }
            
        finally:
            session.close()

# Global ORM instance (initialized by application)
ai_orm: Optional[AIEnhancedORM] = None

def get_ai_orm() -> AIEnhancedORM:
    """Get the global AI ORM instance"""
    if ai_orm is None:
        raise RuntimeError("AI ORM not initialized. Call initialize_ai_orm() first.")
    return ai_orm

def initialize_ai_orm(database_url: str, echo: bool = False) -> AIEnhancedORM:
    """Initialize the global AI ORM instance"""
    global ai_orm
    ai_orm = AIEnhancedORM(database_url, echo)
    return ai_orm
