"""Environment module file generation."""

from __future__ import annotations

import re
from dataclasses import dataclass
from pathlib import Path
from typing import Optional

from jinja2 import Environment, StrictUndefined


class ModuleError(Exception):
    """Raised when module generation fails."""


@dataclass(slots=True)
class ModuleConfig:
    """Configuration for environment module generation."""

    name: str  # e.g., "myenv/1.0"
    wrapper_dir: Path
    output_dir: Path
    description: str
    image_ref: str
    runtime: str  # "singularity" or "docker"
    commands: Optional[list[str]] = None


def _parse_module_name(name: str) -> tuple[str, str]:
    """
    Parse module name into base name and version.
    
    Examples:
        "myenv/1.0" -> ("myenv", "1.0")
        "tools/python/3.11" -> ("tools/python", "3.11")
    """
    parts = name.rsplit("/", 1)
    if len(parts) == 2:
        return parts[0], parts[1]
    else:
        # No version specified
        return parts[0], ""


def _module_base_name(name: str) -> str:
    """
    Extract base module name for conflict declarations.
    
    Examples:
        "myenv/1.0" -> "myenv"
        "tools/python/3.11" -> "tools/python"
    """
    base, _ = _parse_module_name(name)
    return base


def _tcl_module_template() -> str:
    """Return Tcl module file template."""
    return '''#%Module1.0
##
## Auto-generated by absconda module
## Image: {{ image_ref }}
## Runtime: {{ runtime }}
##
proc ModulesHelp { } {
    puts stderr "{{ description }}"
    puts stderr ""
    puts stderr "Containerized environment: {{ image_ref }}"
    puts stderr "Runtime: {{ runtime }}"
{% if commands %}
    puts stderr "Wrapped commands: {{ commands | join(', ') }}"
{% endif %}
}

module-whatis "{{ description }}"

conflict {{ conflict_name }}

prepend-path PATH {{ wrapper_dir }}

{% if version %}
setenv {{ env_prefix }}_VERSION {{ version }}
{% endif %}
setenv {{ env_prefix }}_IMAGE {{ image_ref }}
setenv {{ env_prefix }}_RUNTIME {{ runtime }}
'''


def generate_module(config: ModuleConfig) -> Path:
    """
    Generate an environment module file.
    
    Returns the path to the generated module file.
    """
    if not config.name:
        raise ModuleError("Module name is required")
    
    if not config.wrapper_dir.exists():
        raise ModuleError(f"Wrapper directory does not exist: {config.wrapper_dir}")
    
    if config.runtime not in ("singularity", "docker"):
        raise ModuleError(f"Unsupported runtime: {config.runtime}")
    
    # Parse module name
    base_name, version = _parse_module_name(config.name)
    conflict_name = _module_base_name(config.name)
    
    # Create environment variable prefix from base name
    # e.g., "myenv" -> "MYENV", "tools/python" -> "TOOLS_PYTHON"
    env_prefix = re.sub(r'[/-]', '_', base_name).upper()
    
    # Prepare output path
    config.output_dir.mkdir(parents=True, exist_ok=True)
    module_file = config.output_dir / config.name
    
    # Ensure parent directory exists (for nested modules like "tools/python/3.11")
    module_file.parent.mkdir(parents=True, exist_ok=True)
    
    # Render template
    env = Environment(
        undefined=StrictUndefined,
        trim_blocks=True,
        lstrip_blocks=True,
        keep_trailing_newline=True,
    )
    template = env.from_string(_tcl_module_template())
    
    context = {
        "description": config.description,
        "image_ref": config.image_ref,
        "runtime": config.runtime,
        "commands": config.commands or [],
        "conflict_name": conflict_name,
        "wrapper_dir": str(config.wrapper_dir.resolve()),
        "env_prefix": env_prefix,
        "version": version,
    }
    
    module_content = template.render(**context)
    module_file.write_text(module_content, encoding="utf-8")
    
    return module_file
