import dataclasses
import shutil
from pathlib import Path

from jinja2 import Environment, FileSystemLoader, TemplateSyntaxError

from hatchdx.scaffolder import ScaffoldContext


# Path segments that contain template variables, mapped to context field names
PATH_VARIABLES = {
    "{{python_package}}": "python_package",
}


def _resolve_path(relative_path: Path, context: dict) -> Path:
    """Replace template variables in path segments (e.g., {{python_package}}/ -> weather_mcp/)."""
    parts = list(relative_path.parts)
    for i, part in enumerate(parts):
        for placeholder, field in PATH_VARIABLES.items():
            if placeholder in part:
                parts[i] = part.replace(placeholder, context[field])
    return Path(*parts)


def render_template(
    template_dir: Path,
    output_dir: Path,
    context: ScaffoldContext,
) -> None:
    """Render a template directory into the output directory.

    - Files ending in .j2 are rendered through Jinja2 (extension stripped).
    - All other files are copied as-is.
    - Directory names containing {{variable}} are resolved from context.
    """
    context_dict = dataclasses.asdict(context)
    env = Environment(
        loader=FileSystemLoader(str(template_dir)),
        keep_trailing_newline=True,
    )

    for source_path in sorted(template_dir.rglob("*")):
        if source_path.is_dir():
            continue

        relative = source_path.relative_to(template_dir)
        resolved = _resolve_path(relative, context_dict)

        if source_path.suffix == ".j2":
            # Render Jinja2 template
            resolved = resolved.with_suffix("")  # strip .j2
            dest = output_dir / resolved
            dest.parent.mkdir(parents=True, exist_ok=True)

            try:
                template = env.get_template(str(relative))
                rendered = template.render(context_dict)
            except TemplateSyntaxError as e:
                raise ValueError(
                    f"Template syntax error in {relative}: {e.message} (line {e.lineno})"
                ) from e

            try:
                dest.write_text(rendered)
            except OSError as e:
                raise OSError(f"Cannot write {dest}: {e}") from e
        else:
            # Copy non-template file as-is
            dest = output_dir / resolved
            dest.parent.mkdir(parents=True, exist_ok=True)
            try:
                shutil.copy2(source_path, dest)
            except OSError as e:
                raise OSError(f"Cannot copy {source_path.name} to {dest}: {e}") from e
