import logging
import os
import shutil
from dataclasses import dataclass, field
from . import utils
from .config import TEMPLATE_DIR, ENCODING
# Default templates, used to create custom template or document if no custom on exists
DEFAULT_TEMPLATES = {
    "protocol": os.path.abspath(
        os.path.join(os.path.dirname(__file__), "templates", "template_protocol.md")
    ),
}
logger = logging.getLogger(__name__)
[docs]@dataclass
class TemplateManager:
    """Class to manage templates used to documentate a measurement.
    Args:
        templates_dir (str): The path to the directory containing custom templates.
        default_templates: Dictionary with keys of known *document types* and values
            specifying the paths of the fallback templates for the corresponding
            document types.
    Raises:
        ValueError: If an unknown *document type* is provided in any of its methods.
    """
    template_dir: str = TEMPLATE_DIR
    default_templates: dict[str, str] = field(default_factory=lambda: DEFAULT_TEMPLATES)
    separator: str = ": "
    @property
    def types(self):
        """List with known document types"""
        return list(self.default_templates.keys())
    def _validate_type(self, document_type) -> None:
        """Raise a ValueError of an unknown document_type is provided."""
        if document_type not in self.types:
            raise ValueError(f"Unknown document type specified ({document_type!r}).")
    def _get_default_template(self, document_type: str) -> str:
        """Returns path to the default template for a certain document type."""
        return self.default_templates[document_type]
    def _get_filename_prefix(self, document_type: str) -> str:
        """Get the prefix for a certain document type."""
        return document_type + "-template_"
    def _get_name_from_filename(self, document_type: str, filename: str) -> str:
        """Get the template name for a certain filename and document type."""
        prefix = self._get_filename_prefix(document_type)
        if not filename.startswith(prefix):
            raise ValueError(f"Template filename should start with {prefix!r}.")
        return os.path.splitext(filename.replace(prefix, ""))[0]
    def _get_filename_from_name(self, document_type: str, name: str) -> str:
        """Get the filename for a certain template name and document type."""
        prefix = self._get_filename_prefix(document_type)
        _, ext = os.path.splitext(self._get_default_template(document_type))
        return prefix + name + ext
[docs]    def get_type_name_list(self) -> list[str]:
        """Returns list of registered templates and their document type."""
        if not os.path.isdir(self.template_dir):
            return None
        basenames = [os.path.splitext(i)[0] for i in os.listdir(self.template_dir)]
        return [i.replace("-template_", self.separator) for i in basenames] 
[docs]    def get_template_dict(self) -> dict[str, list[str]]:
        """Get a dictionary with document types as keys and list of names as values."""
        type_name_lst = self.get_type_name_list()
        types = list(set([i.split(self.separator)[0] for i in type_name_lst]))
        tmplt_dct = {k: [] for k in types}
        for item in type_name_lst:
            k, name = item.split(self.separator)
            tmplt_dct[k].append(name)
        return tmplt_dct 
[docs]    def get_type(self, name: str) -> str:
        """Get the document type of a registered template.
        Args:
            name (str): The name of a registered template whose type shall be returned.
        Returns:
            str: The document type of the provided template name.
        """
        for document_type, tmplates in self.get_template_dict().items():
            if name in tmplates:
                return document_type 
[docs]    def get_template(self, document_type: str, name: str) -> str:
        """Get the file path of a registered template.
        Args:
            document_type (str): The document type of the template to be returned.
            name (str): The name of a registered template that shall be returned.
        Returns:
            str: The path to the template with the provided name and document type.
        """
        filename = self._get_filename_from_name(document_type, name)
        return os.path.join(self.template_dir, filename) 
[docs]    def get_templates(self, document_type: str, names: bool = False) -> list[str]:
        """Get a list to paths (or names) of available templates.
        If no custom templates exist, the list will contain the path to the fallback
        template for the specified document_type, otherwise the fallback template will
        be excluded.
        If names are requested, 'None' is returned if no custom template is registered
        for the specified type.
        Args:
            document_type (str): The document type of the templates to be considered.
            names (bool): Flag to indicate that template names instead of paths shall
                be returned.
        Returns:
            list[str]: A list with paths to or names of available templates for the
                specified document type.
        """
        if os.path.isdir(self.template_dir):
            prefix = self._get_filename_prefix(document_type)
            templates = [
                i for i in os.listdir(self.template_dir) if i.startswith(prefix)
            ]
            if names:
                lst = [
                    self._get_name_from_filename(document_type, i) for i in templates
                ]
            else:
                lst = [os.path.join(self.template_dir, i) for i in templates]
            if len(lst) > 0:
                return lst
        if names:
            return [None]
        return [self._get_default_template(document_type)] 
[docs]    def create_template(
        self,
        name: str,
        document_type: str,
        force: bool = False,
    ) -> None:
        """
        Create a custom template for a certain document type.
        Args:
            name (str): The name of the template.
            document_type (str): The type of document that the template will be used for.
            force (bool, optional): Whether to overwrite the template if it already exists.
                Defaults to False.
        Raises:
            FileExistsError: If a template with the same name already exists and 'force'
                is set to False.
        """
        self._validate_type(document_type)
        default_file = self._get_default_template(document_type)
        file_path = self.get_template(document_type, name)
        if not force:
            utils.raise_file_exists(file_path, force=True)
        utils.make_dirs(file_path)
        shutil.copyfile(default_file, file_path)
        logger.debug(f"Created custom measurement {document_type!r} template: {name!r}") 
[docs]    def remove_template(self, document_type: str, name: str) -> None:
        """Delete a custom template file."""
        self._validate_type(document_type)
        template = self.get_template(document_type, name)
        os.remove(template)
        logger.info(f"Removed template: {name!r} ({document_type})")  
[docs]def get_template_cli(
    template_manager: TemplateManager, document_type: str, template_name: str = None
) -> tuple[str, str]:
    """CLI to get the (name of) and path to a template for the specified document types.
    Args:
        template_manager (TemplateManager): TemplateManager instance initiated with the
            template directory of interest.
        document_type (str): The document type of the template that shall be returned.
        template_name (str, optional): Specify the name of the desired document template.
    Returns:
        tuple[str,str]: A tuple containing the (1.) template_name, and the corresponding (2.) file_path.
    """
    template_file = None
    if template_name is None:
        options = template_manager.get_templates(document_type, names=True)
        if len(options) == 1 and options[0] is None:
            template_file = template_manager.default_templates[document_type]
            template_name = "default"
        elif len(options) == 1:
            template_name = options[0]
        else:
            msg = f"Choose a measurement {document_type} template:"
            template_name = utils.select_option(options, msg)
    if template_file is None:
        template_file = template_manager.get_template(document_type, template_name)
    return template_name, template_file 
# import jinja2
# from .config import ENCODING
# TEMPLATES_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "templates"))
# TEMPLATES = {
#     "measurement_protocol": "template_protocol.md",
# }
# @dataclass
# class TemplateProcessor:
#     """
#     Processing of jinja2 templates.
#     :templates_dir: Path to the templates directory.
#     :encoding:      Encoding used to read and write files.
#     """
#     templates_dir: str = TEMPLATES_DIR
#     encoding: str = ENCODING
#     def _get_env_obj(self) -> jinja2.Environment:
#         """Get Jinja2 Environment object.
#         Returns:
#             jinja2.Environment: jinja2 Environment object
#         """
#         loader = jinja2.FileSystemLoader(TEMPLATES_DIR)
#         return jinja2.Environment(loader=loader)
#     def list_templates(self) -> list[str]:
#         """List available Jinja2 templates.
#         Returns:
#             list[str]: list of templates
#         """
#         return self._get_env_obj().list_templates()
#     def get_template(self, template_name) -> jinja2.Template:
#         """Load jinja2 Template object from template file.
#         Returns:
#             jinja2.Template: jinja2 Template object
#         """
#         env = self._get_env_obj()
#         return env.get_template(template_name)
#     def create_file(
#         self, values: dict[str, any], template_name: str, file_path: str
#     ) -> None:
#         """Create a file from the template and values.
#         Args:
#             values: dict[str, any]
#                 dictionary with the values to be used in the template
#             file_path: str
#                 path to the file to be created
#         """
#         # Render the template using the values
#         output = self.get_template(template_name).render(**values)
#         # Save the rendered template to a file
#         with open(file_path, "w", encoding=self.encoding) as fobj:
#             fobj.write(output)