#!/usr/bin/python3

# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
#
# SPDX-License-Identifier: MPL-2.0
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0.  If a copy of the MPL was not distributed with this
# file, you can obtain one at https://mozilla.org/MPL/2.0/.
#
# See the COPYRIGHT file distributed with this work for additional
# information regarding copyright ownership.

import os
from pathlib import Path
from typing import Any, Dict, Optional, Union

import pytest

from .log import debug


class TemplateEngine:
    """
    Engine for rendering jinja2 templates in system test directories.
    """

    def __init__(self, directory: Union[str, Path], env_vars=None):
        """
        Initialize the template engine for `directory`, optionally overriding
        the `env_vars` that will be used when rendering the templates (defaults
        to the environment variables set by the pytest runner).
        """
        self.directory = Path(directory)
        self._j2env = None
        if env_vars is None:
            self.env_vars = dict(os.environ)
        else:
            self.env_vars = dict(env_vars)

    @property
    def j2env(self):
        """
        Jinja2 engine that is initialized when first requested. In case the
        jinja2 package in unavailable, the current test will be skipped.
        """
        if self._j2env is None:
            try:
                import jinja2  # pylint: disable=import-outside-toplevel
            except ImportError:
                pytest.skip("jinja2 not found")

            loader = jinja2.FileSystemLoader(str(self.directory))
            return jinja2.Environment(
                loader=loader,
                undefined=jinja2.StrictUndefined,
                variable_start_string="@",
                variable_end_string="@",
            )
        return self._j2env

    def render(
        self,
        output: str,
        data: Optional[Dict[str, Any]] = None,
        template: Optional[str] = None,
    ) -> None:
        """
        Render `output` file from jinja `template` and fill in the `data`. The
        `template` defaults to *.j2.manual or *.j2 file. The environment
        variables which the engine was initialized with are also filled in. In
        case of a variable name clash, `data` has precedence.
        """
        if template is None:
            template = f"{output}.j2.manual"
            if not Path(template).is_file():
                template = f"{output}.j2"
        if not Path(template).is_file():
            raise RuntimeError('No jinja2 template found for "{output}"')

        if data is None:
            data = self.env_vars
        else:
            data = {**self.env_vars, **data}

        debug("rendering template `%s` to file `%s`", template, output)
        stream = self.j2env.get_template(template).stream(data)
        stream.dump(output, encoding="utf-8")

    def render_auto(self, data: Optional[Dict[str, Any]] = None):
        """
        Render all *.j2 templates with default (and optionally the provided)
        values and write the output to files without the .j2 extensions.
        """
        templates = [
            str(filepath.relative_to(self.directory))
            for filepath in self.directory.rglob("*.j2")
        ]
        for template in templates:
            self.render(template[:-3], data)
