"""
Template Repository
Repository for notification template operations
"""

from typing import Optional, List, Dict, Any
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, text, and_
from datetime import datetime
import structlog
import json

from .base import NotificationBaseRepository
from app.models.notifications import NotificationTemplate, NotificationType
from shared.database.exceptions import DatabaseError, ValidationError, DuplicateRecordError

logger = structlog.get_logger()


class TemplateRepository(NotificationBaseRepository):
    """Repository for notification template operations"""
    
    def __init__(self, session: AsyncSession, cache_ttl: Optional[int] = 1800):
        # Templates don't change often, longer cache time (30 minutes)
        super().__init__(NotificationTemplate, session, cache_ttl)
    
    async def create_template(self, template_data: Dict[str, Any]) -> NotificationTemplate:
        """Create a new notification template with validation"""
        try:
            # Validate template data
            required_fields = ["template_key", "name", "category", "type", "body_template"]
            validation_result = self._validate_notification_data(template_data, required_fields)
            
            # Additional template-specific validation
            if validation_result["is_valid"]:
                # Check if template_key already exists
                existing_template = await self.get_by_template_key(template_data["template_key"])
                if existing_template:
                    raise DuplicateRecordError(f"Template key {template_data['template_key']} already exists")
                
                # Validate template variables if provided
                if "required_variables" in template_data:
                    if isinstance(template_data["required_variables"], list):
                        template_data["required_variables"] = json.dumps(template_data["required_variables"])
                    elif isinstance(template_data["required_variables"], str):
                        # Verify it's valid JSON
                        try:
                            json.loads(template_data["required_variables"])
                        except json.JSONDecodeError:
                            validation_result["errors"].append("Invalid JSON format in required_variables")
                            validation_result["is_valid"] = False
            
            if not validation_result["is_valid"]:
                raise ValidationError(f"Invalid template data: {validation_result['errors']}")
            
            # Set default values
            if "language" not in template_data:
                template_data["language"] = "es"
            if "is_active" not in template_data:
                template_data["is_active"] = True
            if "is_system" not in template_data:
                template_data["is_system"] = False
            if "default_priority" not in template_data:
                template_data["default_priority"] = "normal"
            
            # Create template
            template = await self.create(template_data)
            
            logger.info("Notification template created successfully",
                       template_id=template.id,
                       template_key=template.template_key,
                       type=template.type.value,
                       category=template.category)
            
            return template
            
        except (ValidationError, DuplicateRecordError):
            raise
        except Exception as e:
            logger.error("Failed to create template",
                        template_key=template_data.get("template_key"),
                        error=str(e))
            raise DatabaseError(f"Failed to create template: {str(e)}")
    
    async def get_by_template_key(self, template_key: str) -> Optional[NotificationTemplate]:
        """Get template by template key"""
        try:
            return await self.get_by_field("template_key", template_key)
        except Exception as e:
            logger.error("Failed to get template by key",
                        template_key=template_key,
                        error=str(e))
            raise DatabaseError(f"Failed to get template: {str(e)}")
    
    async def get_templates_by_category(
        self,
        category: str,
        tenant_id: str = None,
        include_system: bool = True
    ) -> List[NotificationTemplate]:
        """Get templates by category"""
        try:
            filters = {"category": category, "is_active": True}
            
            if tenant_id and include_system:
                # Get both tenant-specific and system templates
                tenant_templates = await self.get_multi(
                    filters={**filters, "tenant_id": tenant_id}
                )
                system_templates = await self.get_multi(
                    filters={**filters, "is_system": True}
                )
                return tenant_templates + system_templates
            elif tenant_id:
                # Only tenant-specific templates
                filters["tenant_id"] = tenant_id
                return await self.get_multi(filters=filters)
            elif include_system:
                # Only system templates
                filters["is_system"] = True
                return await self.get_multi(filters=filters)
            else:
                return []
                
        except Exception as e:
            logger.error("Failed to get templates by category",
                        category=category,
                        tenant_id=tenant_id,
                        error=str(e))
            return []
    
    async def get_templates_by_type(
        self,
        notification_type: NotificationType,
        tenant_id: str = None,
        include_system: bool = True
    ) -> List[NotificationTemplate]:
        """Get templates by notification type"""
        try:
            filters = {"type": notification_type, "is_active": True}
            
            if tenant_id and include_system:
                # Get both tenant-specific and system templates
                tenant_templates = await self.get_multi(
                    filters={**filters, "tenant_id": tenant_id}
                )
                system_templates = await self.get_multi(
                    filters={**filters, "is_system": True}
                )
                return tenant_templates + system_templates
            elif tenant_id:
                # Only tenant-specific templates
                filters["tenant_id"] = tenant_id
                return await self.get_multi(filters=filters)
            elif include_system:
                # Only system templates
                filters["is_system"] = True
                return await self.get_multi(filters=filters)
            else:
                return []
                
        except Exception as e:
            logger.error("Failed to get templates by type",
                        notification_type=notification_type.value,
                        tenant_id=tenant_id,
                        error=str(e))
            return []
    
    async def update_template(
        self,
        template_id: str,
        update_data: Dict[str, Any],
        allow_system_update: bool = False
    ) -> Optional[NotificationTemplate]:
        """Update template with system template protection"""
        try:
            template = await self.get_by_id(template_id)
            if not template:
                return None
            
            # Prevent updating system templates unless explicitly allowed
            if template.is_system and not allow_system_update:
                raise ValidationError("Cannot update system templates")
            
            # Validate required_variables if being updated
            if "required_variables" in update_data:
                if isinstance(update_data["required_variables"], list):
                    update_data["required_variables"] = json.dumps(update_data["required_variables"])
                elif isinstance(update_data["required_variables"], str):
                    try:
                        json.loads(update_data["required_variables"])
                    except json.JSONDecodeError:
                        raise ValidationError("Invalid JSON format in required_variables")
            
            # Update template
            updated_template = await self.update(template_id, update_data)
            
            logger.info("Template updated successfully",
                       template_id=template_id,
                       template_key=template.template_key,
                       updated_fields=list(update_data.keys()))
            
            return updated_template
            
        except ValidationError:
            raise
        except Exception as e:
            logger.error("Failed to update template",
                        template_id=template_id,
                        error=str(e))
            raise DatabaseError(f"Failed to update template: {str(e)}")
    
    async def deactivate_template(self, template_id: str) -> Optional[NotificationTemplate]:
        """Deactivate a template (soft delete)"""
        try:
            template = await self.get_by_id(template_id)
            if not template:
                return None
            
            # Prevent deactivating system templates
            if template.is_system:
                raise ValidationError("Cannot deactivate system templates")
            
            updated_template = await self.update(template_id, {
                "is_active": False,
                "updated_at": datetime.utcnow()
            })
            
            logger.info("Template deactivated",
                       template_id=template_id,
                       template_key=template.template_key)
            
            return updated_template
            
        except ValidationError:
            raise
        except Exception as e:
            logger.error("Failed to deactivate template",
                        template_id=template_id,
                        error=str(e))
            raise DatabaseError(f"Failed to deactivate template: {str(e)}")
    
    async def activate_template(self, template_id: str) -> Optional[NotificationTemplate]:
        """Activate a template"""
        try:
            updated_template = await self.update(template_id, {
                "is_active": True,
                "updated_at": datetime.utcnow()
            })
            
            if updated_template:
                logger.info("Template activated",
                           template_id=template_id,
                           template_key=updated_template.template_key)
            
            return updated_template
            
        except Exception as e:
            logger.error("Failed to activate template",
                        template_id=template_id,
                        error=str(e))
            raise DatabaseError(f"Failed to activate template: {str(e)}")
    
    async def search_templates(
        self,
        search_term: str,
        tenant_id: str = None,
        category: str = None,
        notification_type: NotificationType = None,
        include_system: bool = True,
        limit: int = 50
    ) -> List[NotificationTemplate]:
        """Search templates by name, description, or template key"""
        try:
            conditions = [
                "is_active = true",
                "(LOWER(name) LIKE LOWER(:search_term) OR LOWER(description) LIKE LOWER(:search_term) OR LOWER(template_key) LIKE LOWER(:search_term))"
            ]
            params = {"search_term": f"%{search_term}%", "limit": limit}
            
            # Add tenant/system filter
            if tenant_id and include_system:
                conditions.append("(tenant_id = :tenant_id OR is_system = true)")
                params["tenant_id"] = tenant_id
            elif tenant_id:
                conditions.append("tenant_id = :tenant_id")
                params["tenant_id"] = tenant_id
            elif include_system:
                conditions.append("is_system = true")
            
            # Add category filter
            if category:
                conditions.append("category = :category")
                params["category"] = category
            
            # Add type filter
            if notification_type:
                conditions.append("type = :notification_type")
                params["notification_type"] = notification_type.value
            
            query_text = f"""
                SELECT * FROM notification_templates
                WHERE {' AND '.join(conditions)}
                ORDER BY name ASC
                LIMIT :limit
            """
            
            result = await self.session.execute(text(query_text), params)
            
            templates = []
            for row in result.fetchall():
                record_dict = dict(row._mapping)
                # Convert enum string back to enum object
                record_dict["type"] = NotificationType(record_dict["type"])
                template = self.model(**record_dict)
                templates.append(template)
            
            return templates
            
        except Exception as e:
            logger.error("Failed to search templates",
                        search_term=search_term,
                        error=str(e))
            return []
    
    async def get_template_usage_statistics(self, template_id: str) -> Dict[str, Any]:
        """Get usage statistics for a template"""
        try:
            template = await self.get_by_id(template_id)
            if not template:
                return {"error": "Template not found"}
            
            # Get usage statistics from notifications table
            usage_query = text("""
                SELECT 
                    COUNT(*) as total_uses,
                    COUNT(CASE WHEN status = 'delivered' THEN 1 END) as successful_uses,
                    COUNT(CASE WHEN status = 'failed' THEN 1 END) as failed_uses,
                    COUNT(CASE WHEN created_at >= NOW() - INTERVAL '30 days' THEN 1 END) as uses_last_30_days,
                    MIN(created_at) as first_used,
                    MAX(created_at) as last_used
                FROM notifications 
                WHERE template_id = :template_key
            """)
            
            result = await self.session.execute(usage_query, {"template_key": template.template_key})
            stats = result.fetchone()
            
            total = stats.total_uses or 0
            successful = stats.successful_uses or 0
            success_rate = (successful / total * 100) if total > 0 else 0
            
            return {
                "template_id": template_id,
                "template_key": template.template_key,
                "total_uses": total,
                "successful_uses": successful,
                "failed_uses": stats.failed_uses or 0,
                "success_rate_percent": round(success_rate, 2),
                "uses_last_30_days": stats.uses_last_30_days or 0,
                "first_used": stats.first_used.isoformat() if stats.first_used else None,
                "last_used": stats.last_used.isoformat() if stats.last_used else None
            }
            
        except Exception as e:
            logger.error("Failed to get template usage statistics",
                        template_id=template_id,
                        error=str(e))
            return {
                "template_id": template_id,
                "error": str(e)
            }
    
    async def duplicate_template(
        self,
        template_id: str,
        new_template_key: str,
        new_name: str,
        tenant_id: str = None
    ) -> Optional[NotificationTemplate]:
        """Duplicate an existing template"""
        try:
            original_template = await self.get_by_id(template_id)
            if not original_template:
                return None
            
            # Check if new template key already exists
            existing_template = await self.get_by_template_key(new_template_key)
            if existing_template:
                raise DuplicateRecordError(f"Template key {new_template_key} already exists")
            
            # Create duplicate template data
            duplicate_data = {
                "template_key": new_template_key,
                "name": new_name,
                "description": f"Copy of {original_template.name}",
                "category": original_template.category,
                "type": original_template.type,
                "subject_template": original_template.subject_template,
                "body_template": original_template.body_template,
                "html_template": original_template.html_template,
                "language": original_template.language,
                "default_priority": original_template.default_priority,
                "required_variables": original_template.required_variables,
                "tenant_id": tenant_id,
                "is_active": True,
                "is_system": False  # Duplicates are never system templates
            }
            
            duplicated_template = await self.create(duplicate_data)
            
            logger.info("Template duplicated successfully",
                       original_template_id=template_id,
                       new_template_id=duplicated_template.id,
                       new_template_key=new_template_key)
            
            return duplicated_template
            
        except DuplicateRecordError:
            raise
        except Exception as e:
            logger.error("Failed to duplicate template",
                        template_id=template_id,
                        new_template_key=new_template_key,
                        error=str(e))
            raise DatabaseError(f"Failed to duplicate template: {str(e)}")
    
    async def get_system_templates(self) -> List[NotificationTemplate]:
        """Get all system templates"""
        try:
            return await self.get_multi(
                filters={"is_system": True, "is_active": True},
                order_by="category"
            )
        except Exception as e:
            logger.error("Failed to get system templates", error=str(e))
            return []
    
    async def get_tenant_templates(self, tenant_id: str) -> List[NotificationTemplate]:
        """Get all templates for a specific tenant"""
        try:
            return await self.get_multi(
                filters={"tenant_id": tenant_id, "is_active": True},
                order_by="category"
            )
        except Exception as e:
            logger.error("Failed to get tenant templates",
                        tenant_id=tenant_id,
                        error=str(e))
            return []