"""
Transclusion context management.

Provides context objects that manage variables, paths,
and state during transclusion processing.
"""

from pathlib import Path
from typing import Dict, Any, Optional, Set, List


class TransclusionContext:
    """
    Context object for transclusion operations.

    Manages variables, paths, processing state, and circular reference
    detection during transclusion processing.
    """

    def __init__(self, base_path: Optional[Path] = None,
                 variables: Optional[Dict[str, Any]] = None,
                 max_depth: int = 10):
        """
        Initialize transclusion context.

        Args:
            base_path: Base path for relative file resolution
            variables: Initial variables for substitution
            max_depth: Maximum inclusion depth to prevent infinite recursion
        """
        self.base_path = base_path or Path.cwd()
        self.variables = variables or {}
        self.max_depth = max_depth
        self.current_depth = 0
        self.inclusion_stack: List[Path] = []
        self.processed_files: Set[Path] = set()

    def enter_file(self, file_path: Path) -> None:
        """
        Enter processing of a file.

        Args:
            file_path: Path of file being processed

        Raises:
            CircularReferenceError: If file creates circular reference
            DepthLimitError: If max depth exceeded
        """
        from ..errors import CircularReferenceError, DepthLimitError

        # Check depth limit
        if self.current_depth >= self.max_depth:
            raise DepthLimitError(f"Maximum inclusion depth {self.max_depth} exceeded")

        # Check for circular references
        resolved_path = file_path.resolve()
        if resolved_path in self.inclusion_stack:
            cycle_start = self.inclusion_stack.index(resolved_path)
            cycle = self.inclusion_stack[cycle_start:] + [resolved_path]
            cycle_str = " -> ".join(str(p) for p in cycle)
            raise CircularReferenceError(f"Circular reference detected: {cycle_str}")

        # Enter file
        self.inclusion_stack.append(resolved_path)
        self.current_depth += 1

    def exit_file(self, file_path: Path) -> None:
        """
        Exit processing of a file.

        Args:
            file_path: Path of file being exited
        """
        resolved_path = file_path.resolve()
        if self.inclusion_stack and self.inclusion_stack[-1] == resolved_path:
            self.inclusion_stack.pop()
            self.current_depth -= 1
            self.processed_files.add(resolved_path)

    def resolve_path(self, path: str) -> Path:
        """
        Resolve a path relative to the current base path.

        Args:
            path: Path to resolve

        Returns:
            Resolved Path object
        """
        path_obj = Path(path)
        if path_obj.is_absolute():
            return path_obj
        else:
            return self.base_path / path_obj

    def set_variable(self, name: str, value: Any) -> None:
        """
        Set a variable in the context.

        Args:
            name: Variable name
            value: Variable value
        """
        self.variables[name] = value

    def get_variable(self, name: str, default: Any = None) -> Any:
        """
        Get a variable from the context.

        Args:
            name: Variable name
            default: Default value if variable not found

        Returns:
            Variable value or default
        """
        return self.variables.get(name, default)

    def substitute_variables(self, text: str) -> str:
        """
        Substitute variables in text using simple {{variable}} syntax.

        Args:
            text: Text containing variable references

        Returns:
            Text with variables substituted
        """
        import re

        def replace_var(match):
            var_name = match.group(1).strip()
            return str(self.get_variable(var_name, match.group(0)))

        return re.sub(r'\{\{([^}]+)\}\}', replace_var, text)

    def create_child_context(self, new_base_path: Optional[Path] = None) -> 'TransclusionContext':
        """
        Create a child context for nested processing.

        Args:
            new_base_path: New base path for the child context

        Returns:
            New TransclusionContext with inherited state
        """
        child = TransclusionContext(
            base_path=new_base_path or self.base_path,
            variables=self.variables.copy(),
            max_depth=self.max_depth
        )
        child.current_depth = self.current_depth
        child.inclusion_stack = self.inclusion_stack.copy()
        child.processed_files = self.processed_files.copy()
        return child