"""
Notification Template Engine.
Uses Jinja2 for template rendering with variable support.
"""
import logging
from typing import Any

from django.utils import timezone
from jinja2 import Environment, BaseLoader, TemplateSyntaxError, UndefinedError
from jinja2.sandbox import SandboxedEnvironment

logger = logging.getLogger('notification.template')


class NotificationTemplateEngine:
    """
    Template engine for rendering notification content.
    Uses Jinja2 SandboxedEnvironment for security.
    """
    
    # Default variables available in all templates
    DEFAULT_VARIABLES = {
        'current_date': lambda: timezone.now().strftime('%Y/%m/%d'),
        'current_time': lambda: timezone.now().strftime('%H:%M'),
        'current_datetime': lambda: timezone.now().strftime('%Y/%m/%d %H:%M'),
    }
    
    def __init__(self):
        """Initialize sandboxed Jinja2 environment."""
        self.env = SandboxedEnvironment(
            loader=BaseLoader(),
            autoescape=True,
            trim_blocks=True,
            lstrip_blocks=True,
        )
        
        # Add custom filters
        self.env.filters['persian_number'] = self._to_persian_numbers
        self.env.filters['format_price'] = self._format_price
    
    def render(
        self,
        template_content: str,
        variables: dict[str, Any] | None = None
    ) -> str:
        """
        Render template with provided variables.
        
        Args:
            template_content: Jinja2 template string
            variables: Dictionary of variables to use in template
            
        Returns:
            Rendered string
            
        Raises:
            TemplateRenderError: If rendering fails
        """
        context = self._build_context(variables or {})
        
        try:
            template = self.env.from_string(template_content)
            return template.render(**context)
        except TemplateSyntaxError as e:
            logger.error(f"Template syntax error: {e}")
            raise TemplateRenderError(f"Invalid template syntax: {e}")
        except UndefinedError as e:
            logger.error(f"Undefined variable in template: {e}")
            raise TemplateRenderError(f"Missing variable: {e}")
        except Exception as e:
            logger.exception(f"Template render failed: {e}")
            raise TemplateRenderError(f"Render failed: {e}")
    
    def validate(self, template_content: str) -> tuple[bool, str | None]:
        """
        Validate template syntax.
        
        Args:
            template_content: Jinja2 template string
            
        Returns:
            Tuple of (is_valid, error_message)
        """
        try:
            self.env.from_string(template_content)
            return True, None
        except TemplateSyntaxError as e:
            return False, f"Syntax error at line {e.lineno}: {e.message}"
        except Exception as e:
            return False, str(e)
    
    def extract_variables(self, template_content: str) -> list[str]:
        """
        Extract variable names used in template.
        
        Args:
            template_content: Jinja2 template string
            
        Returns:
            List of variable names
        """
        from jinja2 import meta
        
        try:
            ast = self.env.parse(template_content)
            return list(meta.find_undeclared_variables(ast))
        except Exception as e:
            logger.error(f"Failed to extract variables: {e}")
            return []
    
    def preview(
        self,
        template_content: str,
        sample_variables: dict[str, Any] | None = None
    ) -> str:
        """
        Preview template with sample data.
        
        Args:
            template_content: Jinja2 template string
            sample_variables: Sample variables for preview
            
        Returns:
            Rendered preview string
        """
        # Use sample data if not provided
        variables = sample_variables or {}
        
        # Fill missing variables with placeholders
        extracted = self.extract_variables(template_content)
        for var in extracted:
            if var not in variables and var not in self.DEFAULT_VARIABLES:
                variables[var] = f"[{var}]"
        
        return self.render(template_content, variables)
    
    def _build_context(self, variables: dict[str, Any]) -> dict[str, Any]:
        """
        Build template context with default and provided variables.
        """
        context = {}
        
        # Add default variables (evaluate lambdas)
        for key, value in self.DEFAULT_VARIABLES.items():
            context[key] = value() if callable(value) else value
        
        # Add provided variables (override defaults)
        context.update(variables)
        
        return context
    
    @staticmethod
    def _to_persian_numbers(value: str | int) -> str:
        """Convert English numbers to Persian."""
        persian_digits = '۰۱۲۳۴۵۶۷۸۹'
        english_digits = '0123456789'
        
        text = str(value)
        for en, fa in zip(english_digits, persian_digits):
            text = text.replace(en, fa)
        return text
    
    @staticmethod
    def _format_price(value: int | float) -> str:
        """Format number as price with thousand separators."""
        return f"{value:,.0f}"


class TemplateRenderError(Exception):
    """Exception raised when template rendering fails."""
    pass


# Singleton instance
template_engine = NotificationTemplateEngine()
