"""
Helm templating utility for VoiceRun projects.

Renders .voicerun/templates/ using Helm with values from values.{environment}.yaml files.
"""

import shutil
import subprocess
import tempfile

from pathlib import Path
from typing import Optional, List, Dict

import yaml


class HelmError(Exception):
    """Exception raised when Helm templating fails."""
    pass


def is_helm_available() -> bool:
    """Check if the helm CLI is available."""
    return shutil.which("helm") is not None


def require_helm() -> bool:
    """
    Check if Helm is available and print error message if not.

    Returns:
        True if Helm is available, False otherwise (with error printed).
    """
    if is_helm_available():
        return True

    # Import here to avoid circular imports
    from vr_cli.utils.utils import print_info

    print_info("Run 'vr setup' to install required dependencies")
    return False


def get_available_environments(voicerun_dir: Path) -> List[str]:
    """Get list of available environments based on values.*.yaml files."""
    environments = []
    for values_file in voicerun_dir.glob("values.*.yaml"):
        # Extract environment name from values.{env}.yaml
        env_name = values_file.stem.replace("values.", "")
        environments.append(env_name)
    return sorted(environments)


def load_values_file(voicerun_dir: Path, environment: str) -> Optional[dict]:
    """Load values from values.{environment}.yaml file."""
    values_path = voicerun_dir / f"values.{environment}.yaml"
    if not values_path.exists():
        return None

    with open(values_path, "r") as f:
        return yaml.safe_load(f) or {}


def load_agent_data(voicerun_dir: Path) -> dict:
    """Load agent data from agent.yaml for use in templates."""
    agent_path = voicerun_dir / "agent.yaml"
    if not agent_path.exists():
        return {}

    with open(agent_path, "r") as f:
        data = yaml.safe_load(f) or {}

    # Extract relevant agent fields for templating
    return {
        "Name": data.get("name", ""),
        "Description": data.get("description", ""),
    }


def render_templates(
    project_dir: Path,
    environment: Optional[str] = None,
    values_file: Optional[Path] = None,
    set_values: Optional[Dict[str, str]] = None,
) -> List[dict]:
    """
    Render all templates in .voicerun/templates/ using Helm.

    Args:
        project_dir: Path to the project directory
        environment: Environment name (e.g., 'development', 'production')
        values_file: Optional path to a custom values file
        set_values: Optional dict of key=value overrides

    Returns:
        List of parsed YAML documents from rendered templates

    Raises:
        HelmError: If Helm is not available or rendering fails
    """
    if not is_helm_available():
        raise HelmError(
            "Helm CLI not found. Please install Helm: https://helm.sh/docs/intro/install/"
        )

    voicerun_dir = project_dir / ".voicerun"
    templates_dir = voicerun_dir / "templates"

    if not templates_dir.exists():
        return []

    # Load values from file
    if values_file:
        if not values_file.exists():
            raise HelmError(f"Values file not found: {values_file}")
        with open(values_file, "r") as f:
            values = yaml.safe_load(f) or {}
    elif environment:
        values = load_values_file(voicerun_dir, environment)
        if values is None:
            raise HelmError(
                f"Values file not found: values.{environment}.yaml\n"
                f"Available environments: {', '.join(get_available_environments(voicerun_dir))}"
            )
    else:
        values = {}

    # Apply set_values overrides
    if set_values:
        for key, value in set_values.items():
            # Support nested keys like "stt.model"
            keys = key.split(".")
            target = values
            for k in keys[:-1]:
                if k not in target:
                    target[k] = {}
                target = target[k]
            target[keys[-1]] = value

    # Load agent data
    agent_data = load_agent_data(voicerun_dir)

    # Create temporary Helm chart structure
    with tempfile.TemporaryDirectory() as tmpdir:
        chart_dir = Path(tmpdir) / "chart"
        chart_templates_dir = chart_dir / "templates"
        chart_templates_dir.mkdir(parents=True)

        # Create Chart.yaml
        chart_yaml = {
            "apiVersion": "v2",
            "name": agent_data.get("Name", "voicerun-agent"),
            "version": "0.1.0",
            "description": agent_data.get("Description", "VoiceRun agent"),
        }
        with open(chart_dir / "Chart.yaml", "w") as f:
            yaml.dump(chart_yaml, f)

        # Create values.yaml with both Values and Agent namespaces
        combined_values = {
            "Values": values,
            "Agent": agent_data,
        }
        with open(chart_dir / "values.yaml", "w") as f:
            yaml.dump(combined_values, f)

        # Copy all templates, wrapping content to access nested values
        for template_file in templates_dir.glob("**/*.yaml"):
            rel_path = template_file.relative_to(templates_dir)
            dest_path = chart_templates_dir / rel_path
            dest_path.parent.mkdir(parents=True, exist_ok=True)

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

            # Transform {{ .Values.x }} to {{ .Values.Values.x }}
            # and {{ .Agent.x }} to {{ .Values.Agent.x }}
            content = content.replace("{{ .Values.", "{{ .Values.Values.")
            content = content.replace("{{ .Agent.", "{{ .Values.Agent.")
            content = content.replace("{{.Values.", "{{.Values.Values.")
            content = content.replace("{{.Agent.", "{{.Values.Agent.")

            with open(dest_path, "w") as f:
                f.write(content)

        # Also handle .yml files
        for template_file in templates_dir.glob("**/*.yml"):
            rel_path = template_file.relative_to(templates_dir)
            dest_path = chart_templates_dir / rel_path
            dest_path.parent.mkdir(parents=True, exist_ok=True)

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

            content = content.replace("{{ .Values.", "{{ .Values.Values.")
            content = content.replace("{{ .Agent.", "{{ .Values.Agent.")
            content = content.replace("{{.Values.", "{{.Values.Values.")
            content = content.replace("{{.Agent.", "{{.Values.Agent.")

            with open(dest_path, "w") as f:
                f.write(content)

        # Run helm template
        try:
            result = subprocess.run(
                ["helm", "template", "release", str(chart_dir)],
                capture_output=True,
                text=True,
                check=True,
            )
        except subprocess.CalledProcessError as e:
            raise HelmError(f"Helm template failed:\n{e.stderr}")

        # Parse the rendered output (multiple YAML documents)
        rendered_docs = []
        for doc in yaml.safe_load_all(result.stdout):
            if doc:  # Skip empty documents
                rendered_docs.append(doc)

        return rendered_docs


def render_templates_all_environments(
    project_dir: Path,
) -> Dict[str, List[dict]]:
    """
    Render templates for all available environments.

    Returns:
        Dict mapping environment name to list of rendered documents
    """
    voicerun_dir = project_dir / ".voicerun"
    environments = get_available_environments(voicerun_dir)

    results = {}
    for env in environments:
        results[env] = render_templates(project_dir, env)

    return results
