"""Business logic services for {{ module_name_title }} module with event-driven architecture."""

import asyncio
import logging
from datetime import datetime, timedelta
from typing import List, Optional, Dict, Any
from sqlalchemy.orm import Session
from sqlalchemy import and_, or_, desc, asc

from .models import {{ module_name_title }}, EventLog
from .schemas import {{ module_name_title }}Create, {{ module_name_title }}Update, EventFilter, EventStatus, EventPriority

logger = logging.getLogger(__name__)


class {{ module_name_title }}Service:
    """Service class for {{ module_name_title }} operations with event handling."""
    
    def __init__(self, db: Session):
        self.db = db
    
    async def create_{{ module_name }}(self, {{ module_name }}_data: {{ module_name_title }}Create, event_metadata: Optional[Dict[str, Any]] = None) -> {{ module_name_title }}:
        """Create a new {{ module_name_title }} with event logging."""
        try:
            # Create the {{ module_name_title }}
            db_{{ module_name }} = {{ module_name_title }}(
                name={{ module_name }}_data.name,
                description={{ module_name }}_data.description,
                metadata={{ module_name }}_data.metadata,
                event_source={{ module_name }}_data.event_source,
                event_type={{ module_name }}_data.event_type,
                priority={{ module_name }}_data.priority,
                event_status=EventStatus.PENDING
            )
            
            self.db.add(db_{{ module_name }})
            self.db.commit()
            self.db.refresh(db_{{ module_name }})
            
            # Log the creation event
            await self._log_event(
                event_type="creation",
                event_source={{ module_name }}_data.event_source,
                status=EventStatus.COMPLETED,
                message=f"{{ module_name_title }} '{db_{{ module_name }}.name}' created successfully",
                metadata=event_metadata or {}
            )
            
            # Update status to completed
            db_{{ module_name }}.event_status = EventStatus.COMPLETED
            self.db.commit()
            
            logger.info(f"Created {{ module_name_title }} with ID: {db_{{ module_name }}.id}")
            return db_{{ module_name }}
            
        except Exception as e:
            # Log the failure event
            await self._log_event(
                event_type="creation",
                event_source={{ module_name }}_data.event_source,
                status=EventStatus.FAILED,
                message=f"Failed to create {{ module_name_title }}: {str(e)}",
                metadata=event_metadata or {}
            )
            logger.error(f"Error creating {{ module_name_title }}: {str(e)}")
            raise
    
    async def get_{{ module_name }}(self, {{ module_name }}_id: int) -> Optional[{{ module_name_title }}]:
        """Get a {{ module_name_title }} by ID."""
        return self.db.query({{ module_name_title }}).filter({{ module_name_title }}.id == {{ module_name }}_id).first()
    
    async def get_{{ module_name_plural }}(self, skip: int = 0, limit: int = 100, filters: Optional[EventFilter] = None) -> List[{{ module_name_title }}]:
        """Get {{ module_name_plural }} with optional filtering and pagination."""
        query = self.db.query({{ module_name_title }})
        
        if filters:
            query = self._apply_filters(query, filters)
        
        return query.offset(skip).limit(limit).all()
    
    async def update_{{ module_name }}(self, {{ module_name }}_id: int, {{ module_name }}_data: {{ module_name_title }}Update, event_metadata: Optional[Dict[str, Any]] = None) -> Optional[{{ module_name_title }}]:
        """Update a {{ module_name_title }} with event logging."""
        try:
            db_{{ module_name }} = await self.get_{{ module_name }}({{ module_name }}_id)
            if not db_{{ module_name }}:
                return None
            
            # Store original values for event logging
            original_name = db_{{ module_name }}.name
            
            # Update fields
            update_data = {{ module_name }}_data.dict(exclude_unset=True)
            for field, value in update_data.items():
                setattr(db_{{ module_name }}, field, value)
            
            db_{{ module_name }}.updated_at = datetime.utcnow()
            db_{{ module_name }}.event_status = EventStatus.PROCESSING
            
            self.db.commit()
            self.db.refresh(db_{{ module_name }})
            
            # Log the update event
            await self._log_event(
                event_type="update",
                event_source=event_metadata.get("source", "system") if event_metadata else "system",
                status=EventStatus.COMPLETED,
                message=f"{{ module_name_title }} '{original_name}' updated to '{db_{{ module_name }}.name}'",
                metadata=event_metadata or {}
            )
            
            # Update status to completed
            db_{{ module_name }}.event_status = EventStatus.COMPLETED
            self.db.commit()
            
            logger.info(f"Updated {{ module_name_title }} with ID: {db_{{ module_name }}.id}")
            return db_{{ module_name }}
            
        except Exception as e:
            # Log the failure event
            await self._log_event(
                event_type="update",
                event_source=event_metadata.get("source", "system") if event_metadata else "system",
                status=EventStatus.FAILED,
                message=f"Failed to update {{ module_name_title }} { {{ module_name }}_id}: {str(e)}",
                metadata=event_metadata or {}
            )
            logger.error(f"Error updating {{ module_name_title }} { {{ module_name }}_id}: {str(e)}")
            raise
    
    async def delete_{{ module_name }}(self, {{ module_name }}_id: int, event_metadata: Optional[Dict[str, Any]] = None) -> bool:
        """Delete a {{ module_name_title }} with event logging."""
        try:
            db_{{ module_name }} = await self.get_{{ module_name }}({{ module_name }}_id)
            if not db_{{ module_name }}:
                return False
            
            name = db_{{ module_name }}.name
            
            # Log the deletion event before actually deleting
            await self._log_event(
                event_type="deletion",
                event_source=event_metadata.get("source", "system") if event_metadata else "system",
                status=EventStatus.COMPLETED,
                message=f"{{ module_name_title }} '{name}' deleted successfully",
                metadata=event_metadata or {}
            )
            
            self.db.delete(db_{{ module_name }})
            self.db.commit()
            
            logger.info(f"Deleted {{ module_name_title }} with ID: { {{ module_name }}_id}")
            return True
            
        except Exception as e:
            # Log the failure event
            await self._log_event(
                event_type="deletion",
                event_source=event_metadata.get("source", "system") if event_metadata else "system",
                status=EventStatus.FAILED,
                message=f"Failed to delete {{ module_name_title }} { {{ module_name }}_id}: {str(e)}",
                metadata=event_metadata or {}
            )
            logger.error(f"Error deleting {{ module_name_title }} { {{ module_name }}_id}: {str(e)}")
            raise
    
    async def get_{{ module_name_plural }}_by_event_status(self, status: EventStatus) -> List[{{ module_name_title }}]:
        """Get {{ module_name_plural }} by event status."""
        return self.db.query({{ module_name_title }}).filter({{ module_name_title }}.event_status == status).all()
    
    async def get_{{ module_name_plural }}_by_priority(self, priority: EventPriority) -> List[{{ module_name_title }}]:
        """Get {{ module_name_plural }} by priority level."""
        return self.db.query({{ module_name_title }}).filter({{ module_name_title }}.priority == priority).all()
    
    async def retry_failed_events(self) -> int:
        """Retry all failed events by resetting their status to pending."""
        try:
            failed_{{ module_name_plural }} = await self.get_{{ module_name_plural }}_by_event_status(EventStatus.FAILED)
            
            for {{ module_name }} in failed_{{ module_name_plural }}:
                {{ module_name }}.event_status = EventStatus.PENDING
                {{ module_name }}.updated_at = datetime.utcnow()
            
            self.db.commit()
            
            # Log the retry event
            await self._log_event(
                event_type="retry",
                event_source="system",
                status=EventStatus.COMPLETED,
                message=f"Retried {len(failed_{{ module_name_plural }})} failed events",
                metadata={"retry_count": len(failed_{{ module_name_plural }})}
            )
            
            logger.info(f"Retried {len(failed_{{ module_name_plural }})} failed events")
            return len(failed_{{ module_name_plural }})
            
        except Exception as e:
            logger.error(f"Error retrying failed events: {str(e)}")
            raise
    
    def _apply_filters(self, query, filters: EventFilter):
        """Apply filters to the query."""
        if filters.event_type:
            query = query.filter({{ module_name_title }}.event_type == filters.event_type)
        
        if filters.event_source:
            query = query.filter({{ module_name_title }}.event_source == filters.event_source)
        
        if filters.status:
            query = query.filter({{ module_name_title }}.event_status == filters.status)
        
        if filters.priority:
            query = query.filter({{ module_name_title }}.priority == filters.priority)
        
        if filters.start_date:
            query = query.filter({{ module_name_title }}.created_at >= filters.start_date)
        
        if filters.end_date:
            query = query.filter({{ module_name_title }}.created_at <= filters.end_date)
        
        if filters.search:
            search_term = f"%{filters.search}%"
            query = query.filter(
                or_(
                    {{ module_name_title }}.name.ilike(search_term),
                    {{ module_name_title }}.description.ilike(search_term)
                )
            )
        
        return query
    
    async def _log_event(self, event_type: str, event_source: str, status: EventStatus, message: str, metadata: Dict[str, Any]):
        """Log an event to the event log."""
        try:
            event_log = EventLog(
                event_type=event_type,
                event_source=event_source,
                status=status,
                message=message,
                metadata=metadata
            )
            
            self.db.add(event_log)
            self.db.commit()
            
        except Exception as e:
            logger.error(f"Error logging event: {str(e)}")
            # Don't raise here to avoid breaking the main operation


class EventProcessingService:
    """Service for processing events asynchronously."""
    
    def __init__(self, db: Session):
        self.db = db
    
    async def process_pending_events(self) -> int:
        """Process all pending events."""
        try:
            pending_{{ module_name_plural }} = self.db.query({{ module_name_title }}).filter(
                {{ module_name_title }}.event_status == EventStatus.PENDING
            ).all()
            
            processed_count = 0
            
            for {{ module_name }} in pending_{{ module_name_plural }}:
                try:
                    # Mark as processing
                    {{ module_name }}.event_status = EventStatus.PROCESSING
                    self.db.commit()
                    
                    # Simulate async processing
                    await asyncio.sleep(0.1)
                    
                    # Mark as completed
                    {{ module_name }}.event_status = EventStatus.COMPLETED
                    {{ module_name }}.updated_at = datetime.utcnow()
                    self.db.commit()
                    
                    processed_count += 1
                    
                except Exception as e:
                    # Mark as failed
                    {{ module_name }}.event_status = EventStatus.FAILED
                    {{ module_name }}.updated_at = datetime.utcnow()
                    self.db.commit()
                    
                    logger.error(f"Error processing {{ module_name_title }} { {{ module_name }}.id}: {str(e)}")
            
            return processed_count
            
        except Exception as e:
            logger.error(f"Error processing pending events: {str(e)}")
            raise
    
    async def cleanup_old_events(self, days: int = 30) -> int:
        """Clean up old completed events."""
        try:
            cutoff_date = datetime.utcnow() - timedelta(days=days)
            
            # Get old completed events
            old_events = self.db.query({{ module_name_title }}).filter(
                and_(
                    {{ module_name_title }}.event_status == EventStatus.COMPLETED,
                    {{ module_name_title }}.updated_at < cutoff_date
                )
            ).all()
            
            deleted_count = 0
            
            for event in old_events:
                self.db.delete(event)
                deleted_count += 1
            
            self.db.commit()
            
            logger.info(f"Cleaned up {deleted_count} old events")
            return deleted_count
            
        except Exception as e:
            logger.error(f"Error cleaning up old events: {str(e)}")
            raise
