import json
from pathlib import Path
from typing import Dict, Any, List

from pygeai import logger


class TemplateEngine:
    """
    Template generation engine for PyGEAI project scaffolding.

    Renders templates with parameter substitution and creates
    complete project structures for common workflows.
    """

    def __init__(self):
        self.templates_dir = Path(__file__).parent
        logger.debug(
            f"Template engine initialized with directory: {self.templates_dir}"
        )

    def list_templates(self, category: str = None) -> List[Dict[str, Any]]:
        """
        List available templates, optionally filtered by category.

        Excludes templates marked as WIP (containing WIP.md file).

        :param category: Optional category filter (rag/lab/evaluation/workflow)
        :return: List of template metadata dictionaries
        """
        templates = []

        for template_json in self.templates_dir.rglob("template.json"):
            # Skip WIP templates (those with WIP.md file)
            if (template_json.parent / "WIP.md").exists():
                logger.debug(f"Skipping WIP template: {template_json.parent.name}")
                continue

            with open(template_json, "r") as f:
                template_data = json.load(f)

            if category and template_data.get("category") != category:
                continue

            templates.append(
                {
                    "name": template_data["name"],
                    "description": template_data["description"],
                    "category": template_data["category"],
                    "path": template_json.parent,
                }
            )

        return templates

    def get_template(self, name: str) -> Dict[str, Any]:
        """
        Load template configuration by name.

        :param name: Template identifier
        :return: Template configuration dictionary
        :raises: FileNotFoundError if template doesn't exist
        """
        for template_json in self.templates_dir.rglob("template.json"):
            with open(template_json, "r") as f:
                template_data = json.load(f)

            if template_data["name"] == name:
                template_data["_path"] = template_json.parent
                return template_data

        raise FileNotFoundError(f"Template '{name}' not found")

    def render_template(self, template_content: str, params: Dict[str, Any]) -> str:
        """
        Simple template variable substitution.

        Replaces {{variable}} with values from params.

        :param template_content: Template file content
        :param params: Parameter values for substitution
        :return: Rendered content
        """
        result = template_content
        for key, value in params.items():
            pattern = f"{{{{{key}}}}}"
            result = result.replace(pattern, str(value))
        return result

    def validate_params(
        self, template: Dict[str, Any], params: Dict[str, Any]
    ) -> Dict[str, Any]:
        """
        Validate and fill in default values for template parameters.

        :param template: Template configuration
        :param params: User-provided parameters
        :return: Validated parameters with defaults
        :raises: ValueError if required parameter is missing
        """
        validated = {}

        for param_spec in template.get("parameters", []):
            param_name = param_spec["name"]

            if param_name in params:
                validated[param_name] = params[param_name]
            elif param_spec.get("required", False):
                raise ValueError(f"Required parameter '{param_name}' is missing")
            elif "default" in param_spec:
                validated[param_name] = param_spec["default"]
            else:
                validated[param_name] = ""

        return validated

    def generate(
        self, template_name: str, output_dir: Path, params: Dict[str, Any]
    ) -> List[Path]:
        """
        Generate project from template.

        :param template_name: Template identifier
        :param output_dir: Output directory path
        :param params: Template parameters
        :return: List of generated file paths
        """
        template = self.get_template(template_name)
        template_dir = template["_path"]

        validated_params = self.validate_params(template, params)

        output_dir = Path(output_dir)
        output_dir.mkdir(parents=True, exist_ok=True)

        generated_files = []

        for file_spec in template.get("files", []):
            source_path = template_dir / file_spec["source"]
            dest_filename = self.render_template(
                file_spec["destination"], validated_params
            )
            dest_path = output_dir / dest_filename

            with open(source_path, "r") as f:
                content = f.read()

            rendered_content = self.render_template(content, validated_params)

            dest_path.parent.mkdir(parents=True, exist_ok=True)
            dest_path.write_text(rendered_content)

            if file_spec.get("executable", False):
                dest_path.chmod(0o755)

            generated_files.append(dest_path)
            logger.debug(f"Generated file: {dest_path}")

        for dir_spec in template.get("directories", []):
            dir_path_str = self.render_template(dir_spec["path"], validated_params)
            dir_path = output_dir / dir_path_str
            dir_path.mkdir(parents=True, exist_ok=True)

            gitkeep_path = dir_path / ".gitkeep"
            gitkeep_path.touch()
            logger.debug(f"Created directory: {dir_path}")

        return generated_files
