"""
Init command - Initialize a new automation project.

This module provides the functionality to scaffold a new automation
project with the appropriate structure and boilerplate code using
Jinja2 templates.
"""

from __future__ import annotations

import os
import re
import shutil
from pathlib import Path
from typing import Any

from jinja2 import Environment, FileSystemLoader, TemplateNotFound
from rich.console import Console
from rich.panel import Panel
from rich.tree import Tree

console = Console()

# Path to templates directory (relative to this file)
TEMPLATES_DIR = Path(__file__).parent.parent / "templates"

# Template descriptions
TEMPLATE_DESCRIPTIONS: dict[str, str] = {
    "basic": "A basic automation with a single processing node",
    "data-pipeline": "A data processing automation with ETL stages (fetch, transform, analyze)",
    "ai-analysis": "An AI analysis automation with LLM-powered analysis stages",
    "webhook-handler": "A webhook handler automation for processing incoming events",
}

DEFAULT_GITIGNORE_CONTENT = """# Python caches
__pycache__/
*.py[cod]

# Virtual environments
.venv/
venv/
env/

# Test and tool caches
.pytest_cache/
.mypy_cache/
.ruff_cache/

# Local environment/config
.env
.env.*
.python-version

# OS / editor files
.DS_Store
"""


def get_available_templates() -> list[str]:
    """Get list of available template names."""
    if not TEMPLATES_DIR.exists():
        return []
    return [
        d.name
        for d in TEMPLATES_DIR.iterdir()
        if d.is_dir() and not d.name.startswith("_")
    ]


def to_class_name(name: str) -> str:
    """Convert a name to a valid Python class name."""
    # Replace hyphens and underscores with spaces, then title case
    words = re.sub(r"[-_]+", " ", name).split()
    return "".join(word.capitalize() for word in words)


def to_snake_case(name: str) -> str:
    """Convert a name to snake_case."""
    # Replace hyphens with underscores, handle camelCase
    s1 = re.sub(r"[-\s]+", "_", name)
    s2 = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", s1)
    return re.sub("([a-z0-9])([A-Z])", r"\1_\2", s2).lower()


def to_display_name(name: str) -> str:
    """Convert a name to a human-readable display name."""
    # Replace hyphens and underscores with spaces, then title case
    words = re.sub(r"[-_]+", " ", name).split()
    return " ".join(word.capitalize() for word in words)


def get_template_files(template_name: str) -> list[tuple[str, str]]:
    """
    Get all template files for a given template.

    Returns list of (relative_path, template_file_path) tuples.
    """
    template_dir = TEMPLATES_DIR / template_name
    if not template_dir.exists():
        return []

    files = []
    for root, _, filenames in os.walk(template_dir):
        for filename in filenames:
            full_path = Path(root) / filename
            rel_path = full_path.relative_to(template_dir)

            # Determine output filename (remove .j2 extension)
            if filename.endswith(".j2"):
                output_name = filename[:-3]
            else:
                output_name = filename

            output_rel_path = rel_path.parent / output_name
            files.append((str(output_rel_path), str(rel_path)))

    return files


def init_project(
    name: str,
    template: str,
    author_name: str = "",
    author_email: str = "",
    description: str = "",
) -> bool:
    """
    Initialize a new automation project.

    Args:
        name: Project name
        template: Template to use
        author_name: Author's name (optional)
        author_email: Author's email (optional)
        description: Project description (optional)

    Returns:
        True if successful, False otherwise
    """
    # Validate name
    if not re.match(r"^[a-zA-Z][a-zA-Z0-9_-]*$", name):
        console.print(
            "[red]Error:[/red] Project name must start with a letter and "
            "contain only letters, numbers, hyphens, and underscores."
        )
        return False

    # Check template exists
    available_templates = get_available_templates()
    if template not in available_templates:
        console.print(f"[red]Error:[/red] Unknown template '{template}'")
        console.print(f"Available templates: {', '.join(available_templates)}")
        return False

    # Create project directory
    project_dir = Path.cwd() / name
    if project_dir.exists():
        console.print(f"[red]Error:[/red] Directory '{name}' already exists")
        return False

    try:
        project_dir.mkdir(parents=True)
        console.print(f"[green]Creating project:[/green] {name}")

        # Set up Jinja2 environment
        template_dir = TEMPLATES_DIR / template
        env = Environment(
            loader=FileSystemLoader(str(template_dir)),
            keep_trailing_newline=True,
            trim_blocks=False,
            lstrip_blocks=False,
        )

        # Prepare template variables
        class_name = to_class_name(name)
        snake_name = to_snake_case(name)
        display_name = to_display_name(name) if not description else name

        template_vars: dict[str, Any] = {
            "name": name,
            "class_name": class_name,
            "snake_name": snake_name,
            "display_name": display_name if not description else to_display_name(name),
            "description": description or f"A {display_name} automation",
            "author_name": author_name or "Your Name",
            "author_email": author_email or "your.email@example.com",
        }

        # Create files
        tree = Tree(f"[bold]{name}/[/bold]")
        tree_nodes: dict[str, Tree] = {"": tree}

        template_files = get_template_files(template)
        for output_path, template_path in sorted(template_files):
            # Create parent directories
            full_path = project_dir / output_path
            parent = full_path.parent

            if parent != project_dir:
                parent.mkdir(parents=True, exist_ok=True)

            # Render template or copy file
            if template_path.endswith(".j2"):
                try:
                    jinja_template = env.get_template(template_path)
                    content = jinja_template.render(**template_vars)
                    full_path.write_text(content)
                except TemplateNotFound:
                    console.print(
                        f"[yellow]Warning:[/yellow] Template not found: {template_path}"
                    )
                    continue
            else:
                # Copy non-template files directly
                src_file = template_dir / template_path
                shutil.copy2(src_file, full_path)

            # Add to tree
            _add_to_tree(tree_nodes, output_path)

        # Always create a sane default .gitignore for generated projects.
        gitignore_path = project_dir / ".gitignore"
        if not gitignore_path.exists():
            gitignore_path.write_text(DEFAULT_GITIGNORE_CONTENT)
            _add_to_tree(tree_nodes, ".gitignore")

        console.print(tree)
        console.print()
        console.print(
            Panel.fit(
                f"[green]Success![/green] Created automation project '{name}'\n\n"
                f"[dim]Template: {template}[/dim]\n\n"
                "Next steps:\n"
                f"  [cyan]cd {name}[/cyan]\n"
                "  [cyan]python -m venv .venv[/cyan]\n"
                "  [cyan]source .venv/bin/activate[/cyan]\n"
                "  [cyan]pip install -r requirements.txt[/cyan]\n"
                "  [cyan]torivers run --input sample_input.json[/cyan]",
                title="Project Created",
            )
        )
        return True

    except Exception as e:
        console.print(f"[red]Error creating project:[/red] {e}")
        # Clean up on failure
        if project_dir.exists():
            shutil.rmtree(project_dir)
        return False


def _add_to_tree(tree_nodes: dict[str, Tree], path: str) -> None:
    """Add a path to the tree display."""
    parts = path.split("/") if "/" in path else path.split(os.sep)
    current_path = ""

    for i, part in enumerate(parts):
        parent_path = current_path
        current_path = f"{current_path}/{part}" if current_path else part

        if current_path not in tree_nodes:
            parent_node = tree_nodes[parent_path]
            if i == len(parts) - 1:
                # It's a file
                tree_nodes[current_path] = parent_node.add(f"[dim]{part}[/dim]")
            else:
                # It's a directory
                tree_nodes[current_path] = parent_node.add(f"[bold]{part}/[/bold]")


def list_templates() -> None:
    """List available project templates."""
    console.print("[bold]Available Templates:[/bold]\n")

    available_templates = get_available_templates()

    if not available_templates:
        console.print("  [yellow]No templates found.[/yellow]")
        return

    for template_name in sorted(available_templates):
        description = TEMPLATE_DESCRIPTIONS.get(
            template_name, "No description available"
        )
        console.print(f"  [cyan]{template_name}[/cyan]")
        console.print(f"    {description}")
        console.print()
