"""
Template engine for rendering templates with data.

This module provides the core template rendering functionality,
building on the parser module for variable extraction and substitution.
"""

import re
from typing import Dict, Any, Optional, Union
from .parser import TemplateParser, TemplateParsingError


class TemplateRenderError(TemplateParsingError):
    """Exception raised during template rendering."""
    pass


class VariableNotFoundError(TemplateRenderError):
    """Raised when required variable is missing from data."""
    pass


class TemplateEngine:
    """Template rendering engine for dynamic document generation."""

    def __init__(self):
        """Initialize the template engine."""
        self.parser = TemplateParser()

    def render(self, template_text: str, data: Dict[str, Any], strict: bool = True) -> str:
        """
        Render a template with the provided data.

        Args:
            template_text: The template content to render
            data: Dictionary containing data for variable substitution
            strict: If True, raise error for missing variables. If False, preserve placeholders.

        Returns:
            Rendered template with variables substituted

        Raises:
            TemplateRenderError: When variables are missing in strict mode
            TypeError: When data is not a dictionary
        """
        if not isinstance(data, dict):
            raise TypeError("Data must be a dictionary")

        if not template_text:
            return template_text

        # Use the parser's regex pattern to find and replace variables
        def replace_variable(match):
            variable_name = match.group(1)
            try:
                value = self._get_nested_value(data, variable_name)
                return str(value) if value is not None else "None"
            except (KeyError, TypeError, AttributeError) as e:
                if strict:
                    raise VariableNotFoundError(f"Variable '{variable_name}' not found in data", context=str(e))
                else:
                    # Return the original placeholder in lenient mode
                    return match.group(0)

        # Perform the substitution
        result = self.parser.VARIABLE_PATTERN.sub(replace_variable, template_text)
        return result

    def _get_nested_value(self, data: Dict[str, Any], key: str) -> Any:
        """
        Get nested value using dot notation.

        Args:
            data: Dictionary containing the data
            key: Key with dot notation (e.g., "nested.category")

        Returns:
            Value at the specified key path

        Raises:
            KeyError: When the key path is not found
        """
        keys = key.split('.')
        current = data

        path_so_far = []
        for k in keys:
            path_so_far.append(k)
            if isinstance(current, dict) and k in current:
                current = current[k]
            else:
                available_keys = list(current.keys()) if isinstance(current, dict) else "not a dictionary"
                raise KeyError(f"Key '{k}' not found in path '{key}'. Available keys at '{'.'.join(path_so_far[:-1])}': {available_keys}")

        return current

    def validate_template(self, template_text: str) -> list:
        """
        Validate template syntax and return any errors.

        Args:
            template_text: The template content to validate

        Returns:
            List of validation errors (empty if template is valid)
        """
        return self.parser.validate_variable_syntax(template_text)

    def get_required_variables(self, template_text: str) -> list:
        """
        Get list of variables required by the template.

        Args:
            template_text: The template content to analyze

        Returns:
            List of variable names required by the template
        """
        return self.parser.extract_variables(template_text)

    def check_data_completeness(self, template_text: str, data: Dict[str, Any]) -> Dict[str, list]:
        """
        Check if provided data contains all required variables.

        Args:
            template_text: The template content to check
            data: Data dictionary to validate

        Returns:
            Dictionary with 'missing' and 'available' variable lists
        """
        required_vars = self.get_required_variables(template_text)
        missing_vars = []
        available_vars = []

        for var in required_vars:
            try:
                self._get_nested_value(data, var)
                available_vars.append(var)
            except (KeyError, TypeError, AttributeError):
                missing_vars.append(var)

        return {
            'missing': missing_vars,
            'available': available_vars,
            'completeness': len(available_vars) / len(required_vars) if required_vars else 1.0
        }