# Copyright Visual Collaboration Technologies Inc. All Rights Reserved.
# See LICENSE for details.
"""Template registry — named collection of template strings.

Templates can be registered by name and retrieved later. This decouples
template storage from rendering, allowing consumers to register domain-specific
templates (chart HTML, report HTML, etc.) without the manager needing to
know about them.

Example::

    from vcti.template import TemplateRegistry

    registry = TemplateRegistry()
    registry.register("greeting", "Hello, {{ name }}!")
    registry.get("greeting")  # "Hello, {{ name }}!"
"""

from types import MappingProxyType

from .errors import TemplateNotFoundError


class TemplateRegistry:
    """A named collection of template strings.

    Templates are registered as raw strings keyed by name. The registry
    provides lookup, listing, and an immutable view of all templates.

    Multiple registries can coexist — e.g., one for chart templates,
    another for report templates.
    """

    def __init__(self) -> None:
        self._templates: dict[str, str] = {}

    def register(self, name: str, template: str, *, replace: bool = False) -> None:
        """Register a template string by name.

        Args:
            name: Unique template name. Must be a non-empty string.
            template: Raw Jinja2 template string.
            replace: If True, overwrite existing; otherwise raise.

        Raises:
            ValueError: If *name* is empty or not a string.
            KeyError: If name already exists and replace is False.
        """
        if not isinstance(name, str) or not name.strip():
            raise ValueError(f"Template name must be a non-empty string, got {name!r}.")
        if name in self._templates and not replace:
            raise KeyError(f"Template '{name}' already registered.")
        self._templates[name] = template

    def get(self, name: str) -> str:
        """Retrieve a template string by name.

        Raises:
            TemplateNotFoundError: If name is not registered.
        """
        try:
            return self._templates[name]
        except KeyError:
            raise TemplateNotFoundError(f"Template '{name}' not found.") from None

    def has(self, name: str) -> bool:
        """Check if a template name is registered."""
        return name in self._templates

    def remove(self, name: str) -> None:
        """Remove a registered template.

        Raises:
            TemplateNotFoundError: If name is not registered.
        """
        try:
            del self._templates[name]
        except KeyError:
            raise TemplateNotFoundError(f"Template '{name}' not found.") from None

    def names(self) -> list[str]:
        """Return all registered template names."""
        return list(self._templates.keys())

    @property
    def templates(self) -> MappingProxyType[str, str]:
        """Immutable view of all registered templates."""
        return MappingProxyType(self._templates)

    def __len__(self) -> int:
        return len(self._templates)

    def __contains__(self, name: str) -> bool:
        return name in self._templates

    def __repr__(self) -> str:
        return f"TemplateRegistry({self.names()})"
