from __future__ import annotations

import sys
from enum import Enum
from pathlib import Path
from typing import Any, Dict, List, Optional

import questionary
import typer
from jinja2 import Environment, FileSystemLoader
from rich.console import Console
from rich.panel import Panel

from terminaluse.lib.cli.utils.errors import CLIError, ErrorCode, ExitCode
from terminaluse.lib.utils.logging import make_logger
from terminaluse.types.namespace import Namespace

logger = make_logger(__name__)
console = Console()

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


class TemplateType(str, Enum):
    TEMPORAL = "temporal"
    DEFAULT = "default"


def render_template(template_path: str, context: Dict[str, Any], template_type: TemplateType) -> str:
    """Render a template with the given context"""
    env = Environment(loader=FileSystemLoader(TEMPLATES_DIR / template_type.value))
    template = env.get_template(template_path)
    return template.render(**context)


def create_project_structure(path: Path, context: Dict[str, Any], template_type: TemplateType, use_uv: bool):
    """Create the project structure from templates"""
    # Create project directory
    project_dir: Path = path / context["project_name"]
    project_dir.mkdir(parents=True, exist_ok=True)

    # Create src/ directory for agent code
    code_dir: Path = project_dir / "src"
    code_dir.mkdir(parents=True, exist_ok=True)

    # Create __init__.py
    (code_dir / "__init__.py").touch()

    # Define source files based on template type
    source_files = {
        TemplateType.TEMPORAL: ["agent.py", "workflow.py", "run_worker.py"],
        TemplateType.DEFAULT: ["agent.py"],
    }[template_type]

    # Create src/ files
    for template in source_files:
        template_path = f"src/{template}.j2"
        output_path = code_dir / template
        output_path.write_text(render_template(template_path, context, template_type))

    # Create root files
    root_templates = {
        ".dockerignore.j2": ".dockerignore",
        "config.yaml.j2": "config.yaml",
        "README.md.j2": "README.md",
    }

    # Add package management file based on uv choice
    if use_uv:
        root_templates["pyproject.toml.j2"] = "pyproject.toml"
        root_templates["Dockerfile-uv.j2"] = "Dockerfile"
    else:
        root_templates["requirements.txt.j2"] = "requirements.txt"
        root_templates["Dockerfile.j2"] = "Dockerfile"

    for template, output in root_templates.items():
        output_path = project_dir / output
        output_path.write_text(render_template(template, context, template_type))

    console.print(f"\n[green]✓[/green] Created project structure at: {project_dir}")


def get_project_context(answers: Dict[str, Any], project_path: Path, manifest_root: Path) -> Dict[str, Any]:  # noqa: ARG001
    """Get the project context from user answers"""
    # Use agent_directory_name as project_name
    project_name = answers["agent_directory_name"].replace("-", "_")

    # Now, this is actually the exact same as the project_name because we changed the build root to be ../
    project_path_from_build_root = project_name

    # Use the already-parsed namespace and agent short name from answers
    agent_short_name = answers["agent_short_name"]

    return {
        **answers,
        "project_name": project_name,
        "workflow_class": "".join(word.capitalize() for word in agent_short_name.split("-")) + "Workflow",
        "workflow_name": agent_short_name,
        "queue_name": project_name + "_queue",
        "project_path_from_build_root": project_path_from_build_root,
    }


def validate_slug(text: str) -> bool:
    """Validate a slug (lowercase alphanumeric with hyphens)"""
    return bool(len(text) >= 1 and text.replace("-", "").isalnum() and text.islower())


def fetch_user_namespaces() -> List[Namespace]:
    """Fetch namespaces the user has access to.

    Returns:
        List of Namespace objects the user can access.
        Returns empty list if not authenticated or on API error.
    """
    try:
        from terminaluse.lib.cli.utils.client import get_authenticated_client

        client = get_authenticated_client()
        namespaces = client.namespaces.list()
        # Filter to READY or PENDING (provisioning) namespaces
        return [ns for ns in namespaces if ns.status in ("READY", "PENDING")]
    except CLIError:
        # Not authenticated or token expired
        return []
    except Exception as e:
        logger.debug(f"Failed to fetch namespaces: {e}")
        return []


def init(
    namespace: Optional[str] = typer.Option(
        None,
        "--namespace",
        "-ns",
        help="Namespace slug (e.g., 'acme-corp')",
    ),
    name: Optional[str] = typer.Option(
        None,
        "--name",
        "-n",
        help="Agent name (e.g., 'my-agent')",
    ),
    description: Optional[str] = typer.Option(
        None,
        "--description",
        "-d",
        help="Agent description",
    ),
    template: str = typer.Option(
        "default",
        "--template",
        "-t",
        help="Template type: 'default' or 'temporal'",
    ),
    no_uv: bool = typer.Option(
        False,
        "--no-uv",
        help="Use pip instead of uv for package management",
    ),
):
    """Initialize a new agent project.

    Can be run interactively (no arguments) or non-interactively with --namespace and --name.

    Examples:
        tu init                                    # Interactive mode
        tu init -ns acme-corp -n my-agent          # Non-interactive mode
        tu init -ns test -n hello-world -d "My first agent"
    """
    # Determine if we need interactive prompts for any missing values
    needs_interactive = namespace is None or name is None

    if needs_interactive:
        # Check for interactive mode - init requires user input for missing values
        if not sys.stdin.isatty():
            raise CLIError(
                message="The 'init' command requires interactive mode or --namespace and --name arguments",
                code=ErrorCode.VALIDATION_ERROR,
                exit_code=ExitCode.USER_ERROR,
                hint="Usage: tu init --namespace <namespace> --name <agent-name>",
            )

        console.print(
            Panel.fit(
                "[bold blue]Create New Agent[/bold blue]",
                border_style="blue",
            )
        )
        console.print()

        def validate_slug_questionary(text: str) -> bool | str:
            """Validate a slug for questionary (returns error message on failure)"""
            if not validate_slug(text):
                return "Invalid format. Use only lowercase letters, numbers, and hyphens (e.g., 'acme-corp')"
            return True

        # Question 1: Namespace selection (skip if provided via CLI)
        if namespace is None:
            # Try to fetch available namespaces from the API
            namespaces = fetch_user_namespaces()

            if namespaces:
                # Build choices for the select prompt
                # Format: "slug (name)" for better readability
                choices = [
                    questionary.Choice(
                        title=f"{ns.slug} ({ns.name})" if ns.name != ns.slug else ns.slug,
                        value=ns.slug,
                    )
                    for ns in namespaces
                ]
                # Add option to enter a different namespace manually
                choices.append(questionary.Choice(title="Enter a different namespace...", value="__manual__"))

                namespace = questionary.select(
                    "Select a namespace:",
                    choices=choices,
                ).ask()

                if not namespace:
                    return

                # If user chose manual entry, prompt for text input
                if namespace == "__manual__":
                    namespace = questionary.text(
                        "Namespace slug (e.g., 'acme-corp'):",
                        validate=validate_slug_questionary,
                    ).ask()
                    if not namespace:
                        return
            else:
                # Fall back to text input if no namespaces available
                console.print("[dim]Tip: Run 'tu login' to select from your available namespaces[/dim]")
                namespace = questionary.text(
                    "Namespace slug (e.g., 'acme-corp'):",
                    validate=validate_slug_questionary,
                ).ask()
                if not namespace:
                    return
        else:
            # Validate CLI-provided namespace
            if not validate_slug(namespace):
                raise CLIError(
                    message=f"Invalid namespace '{namespace}'",
                    code=ErrorCode.VALIDATION_ERROR,
                    exit_code=ExitCode.USER_ERROR,
                    hint="Use only lowercase letters, numbers, and hyphens (e.g., 'my-namespace')",
                )
            console.print(f"[dim]Using namespace:[/dim] {namespace}")

        # Question 2: Agent name (skip if provided via CLI)
        if name is None:
            name = questionary.text(
                "Agent name (default: my-agent):",
                validate=lambda x: validate_slug_questionary(x) if x else True,
            ).ask()
            if name is None:
                return
            if not name:
                name = "my-agent"
        else:
            # Validate CLI-provided name
            if not validate_slug(name):
                raise CLIError(
                    message=f"Invalid agent name '{name}'",
                    code=ErrorCode.VALIDATION_ERROR,
                    exit_code=ExitCode.USER_ERROR,
                    hint="Use only lowercase letters, numbers, and hyphens (e.g., 'my-agent')",
                )
            console.print(f"[dim]Using agent name:[/dim] {name}")

        # Question 3: Description (skip if provided via CLI)
        if description is None:
            description = questionary.text("Description (optional):", default="My agent").ask()
            if description is None:
                return
    else:
        # Fully non-interactive mode - validate all inputs
        assert namespace is not None
        assert name is not None

        if not validate_slug(namespace):
            raise CLIError(
                message=f"Invalid namespace '{namespace}'",
                code=ErrorCode.VALIDATION_ERROR,
                exit_code=ExitCode.USER_ERROR,
                hint="Use only lowercase letters, numbers, and hyphens (e.g., 'my-namespace')",
            )

        if not validate_slug(name):
            raise CLIError(
                message=f"Invalid agent name '{name}'",
                code=ErrorCode.VALIDATION_ERROR,
                exit_code=ExitCode.USER_ERROR,
                hint="Use only lowercase letters, numbers, and hyphens (e.g., 'my-agent')",
            )

        if description is None:
            description = "My agent"

        console.print(
            Panel.fit(
                "[bold blue]Create New Agent[/bold blue]",
                border_style="blue",
            )
        )

    # Validate template type
    try:
        template_type = TemplateType(template.lower())
    except ValueError:
        raise CLIError(
            message=f"Invalid template '{template}'",
            code=ErrorCode.VALIDATION_ERROR,
            exit_code=ExitCode.USER_ERROR,
            hint="Valid templates: 'default' or 'temporal'",
        )

    # Combine into full agent name
    agent_name = f"{namespace}/{name}"
    namespace_slug = namespace
    agent_short_name = name

    # Use sensible defaults
    project_path_str = "."
    # Use the agent short name for directory
    agent_directory_name = agent_short_name
    use_uv = not no_uv

    answers: Dict[str, Any] = {
        "template_type": template_type,
        "project_path": project_path_str,
        "agent_name": agent_name,
        "namespace_slug": namespace_slug,
        "agent_short_name": agent_short_name,
        "agent_directory_name": agent_directory_name,
        "description": description,
        "use_uv": use_uv,
    }

    # Derive all names from agent_directory_name and path
    project_path = Path(project_path_str).resolve()
    manifest_root = Path("../../")

    # Get project context
    context = get_project_context(answers, project_path, manifest_root)
    context["template_type"] = template_type.value
    context["use_uv"] = use_uv

    # Create project structure
    create_project_structure(project_path, context, template_type, use_uv)

    # Show success message with quick start
    console.print()
    console.print(f"[bold green]Created {context['project_name']}/[/bold green]")
    console.print()

    # Simple next steps
    console.print("[bold]Get started:[/bold]")
    console.print(f"  [cyan]cd {context['project_name']}[/cyan]")
    console.print("  [cyan]tu deploy[/cyan]")
    console.print("  [cyan]tu env add ANTHROPIC_API_KEY[/cyan]")
    console.print()

    console.print("[dim]Edit [yellow]src/agent.py[/yellow] to customize your agent.[/dim]")
    console.print("[dim]Docs: https://docs.terminaluse.com[/dim]")
    console.print()
