"""
Template engine for generating Markdown documentation from templates.
"""

import os
import re
import sys
from typing import Dict, Any, List, Optional


class TemplateEngine:
    """Simple template engine for generating Markdown files."""

    def __init__(
        self, templates_dir: str = "scripts/track_progress/templates"
    ):
        """Initialize the template engine with the templates directory."""
        self.templates_dir = templates_dir

    def _load_template(self, template_name: str) -> str:
        """Load a template file."""
        # Try multiple possible template locations
        possible_paths = [
            # Original path
            os.path.join(self.templates_dir, template_name),
            # Path relative to current working directory
            os.path.join("templates", template_name),
            # Path relative to executable directory
            os.path.join(
                os.path.dirname(sys.executable), "templates", template_name
            ),
            # Path relative to script directory
            os.path.join(
                os.path.dirname(os.path.abspath(__file__)),
                "templates",
                template_name,
            ),
            # Path with 'scripts/track_progress' removed (for compiled version)
            template_name.replace("scripts/track_progress/", ""),
            # Just the filename
            template_name,
        ]

        # Try each path
        for template_path in possible_paths:
            if os.path.exists(template_path):
                with open(template_path, "r", encoding="utf-8") as f:
                    return f.read()

        # If we get here, none of the paths worked
        error_message = f"Template not found: {template_name}\nTried paths: {possible_paths}"
        raise FileNotFoundError(error_message)

    def _replace_variables(
        self, template: str, context: Dict[str, Any]
    ) -> str:
        """Replace {{ variable }} with values from context."""

        def replace_var(match):
            var_name = match.group(1).strip()

            # Handle nested variables with dot notation
            if "." in var_name:
                parts = var_name.split(".")
                value = context
                for part in parts:
                    if isinstance(value, dict) and part in value:
                        value = value[part]
                    else:
                        return match.group(0)  # Return original if not found
                return str(value) if value is not None else ""

            # Handle simple variables
            if var_name in context:
                return (
                    str(context[var_name])
                    if context[var_name] is not None
                    else ""
                )

            return match.group(0)  # Return original if not found

        # Replace {{ variable }}
        pattern = r"{{(.*?)}}"
        return re.sub(pattern, replace_var, template)

    def _process_conditionals(
        self, template: str, context: Dict[str, Any]
    ) -> str:
        """Process {% if condition %} ... {% endif %} blocks."""

        def replace_conditional(match):
            condition_var = match.group(1).strip()
            content = match.group(2)

            # Handle nested conditions with dot notation
            if "." in condition_var:
                parts = condition_var.split(".")
                value = context
                for part in parts:
                    if isinstance(value, dict) and part in value:
                        value = value[part]
                    else:
                        return ""  # Condition not met

                if value:
                    return content
                return ""

            # Handle simple conditions
            if condition_var in context and context[condition_var]:
                return content
            return ""

        # Process {% if condition %} ... {% endif %}
        pattern = r"{%\s*if\s+(.*?)\s*%}(.*?){%\s*endif\s*%}"
        return re.sub(pattern, replace_conditional, template, flags=re.DOTALL)

    def _process_loops(self, template: str, context: Dict[str, Any]) -> str:
        """Process {% for item_var, item in items.items() %} ... {% endfor %} blocks."""

        # First, process dictionary iteration with items()
        def replace_dict_loop(match):
            item_key_var = match.group(1).strip()
            item_val_var = match.group(2).strip()
            collection_var = match.group(3).strip()
            content_template = match.group(4)

            # Handle nested collections with dot notation
            if "." in collection_var:
                parts = collection_var.split(".")
                collection = context
                for part in parts:
                    if isinstance(collection, dict) and part in collection:
                        collection = collection[part]
                    else:
                        return ""  # Collection not found
            else:
                collection = context.get(collection_var, {})

            if not isinstance(collection, dict):
                return ""  # Not a dictionary

            result = []
            for key, value in collection.items():
                # Create a new context for each iteration
                loop_context = context.copy()
                loop_context[item_key_var] = key
                loop_context[item_val_var] = value

                # Process the content template with the loop context
                item_content = self._replace_variables(
                    content_template, loop_context
                )
                item_content = self._process_conditionals(
                    item_content, loop_context
                )
                result.append(item_content)

            return "".join(result)

        # Process {% for key, value in dict.items() %} ... {% endfor %}
        dict_pattern = r"{%\s*for\s+(.*?),\s*(.*?)\s+in\s+(.*?)\.items\(\)\s*%}(.*?){%\s*endfor\s*%}"
        template = re.sub(
            dict_pattern, replace_dict_loop, template, flags=re.DOTALL
        )

        # Then, process regular list iteration
        def replace_list_loop(match):
            item_var = match.group(1).strip()
            collection_var = match.group(2).strip()
            content_template = match.group(3)

            # Handle nested collections with dot notation
            if "." in collection_var:
                parts = collection_var.split(".")
                collection = context
                for part in parts:
                    if isinstance(collection, dict) and part in collection:
                        collection = collection[part]
                    else:
                        return ""  # Collection not found
            else:
                collection = context.get(collection_var, [])

            if not isinstance(collection, (list, dict)):
                return ""  # Not a collection

            result = []
            if isinstance(collection, dict):
                # If it's a dict, iterate over keys
                for key in collection:
                    # Create a new context for each iteration
                    loop_context = context.copy()
                    loop_context[item_var] = key

                    # Process the content template with the loop context
                    item_content = self._replace_variables(
                        content_template, loop_context
                    )
                    item_content = self._process_conditionals(
                        item_content, loop_context
                    )
                    result.append(item_content)
            else:  # list
                for item in collection:
                    # Create a new context for each iteration
                    loop_context = context.copy()
                    loop_context[item_var] = item

                    # Process the content template with the loop context
                    item_content = self._replace_variables(
                        content_template, loop_context
                    )
                    item_content = self._process_conditionals(
                        item_content, loop_context
                    )
                    result.append(item_content)

            return "".join(result)

        # Process {% for item in items %} ... {% endfor %}
        list_pattern = (
            r"{%\s*for\s+(.*?)\s+in\s+(.*?)\s*%}(.*?){%\s*endfor\s*%}"
        )
        return re.sub(
            list_pattern, replace_list_loop, template, flags=re.DOTALL
        )

    def render(self, template_name: str, context: Dict[str, Any]) -> str:
        """Render a template with the given context."""
        template = self._load_template(template_name)

        # Process template directives in order
        template = self._process_loops(template, context)
        template = self._process_conditionals(template, context)
        template = self._replace_variables(template, context)

        return template
