from pathlib import Path

from pygeai.cli.commands import Command, Option, ArgumentsEnum
from pygeai.cli.commands.builders import build_help_text
from pygeai.cli.texts.help import GENERATE_HELP_TEXT
from pygeai.core.common.exceptions import MissingRequirementException
from pygeai.core.utils.console import Console
from pygeai.templates import TemplateEngine


def show_help():
    """
    Displays help text in stdout
    """
    help_text = build_help_text(generate_commands, GENERATE_HELP_TEXT)
    Console.write_stdout(help_text)


def list_templates(option_list: list):
    """
    List available project templates.
    """
    opts = {opt.name: arg for opt, arg in option_list}
    category = opts.get("category")

    engine = TemplateEngine()
    templates = engine.list_templates(category=category)

    if not templates:
        Console.write_stdout("No templates found.")
        return

    templates_by_category = {}
    for template in templates:
        cat = template["category"]
        if cat not in templates_by_category:
            templates_by_category[cat] = []
        templates_by_category[cat].append(template)

    Console.write_stdout("\nAvailable Templates:\n")
    for cat, cat_templates in sorted(templates_by_category.items()):
        Console.write_stdout(f"{cat.upper()} Templates:")
        for template in cat_templates:
            Console.write_stdout(f"  {template['name']} - {template['description']}")
        Console.write_stdout("")


def create_from_template(option_list: list):
    """
    Generate a project from a template.
    """
    opts = {opt.name: arg for opt, arg in option_list}
    template_name = opts.get("template")

    if not template_name:
        raise MissingRequirementException(
            "Template name is required. Use --template <name>"
        )

    interactive = "no_interactive" not in opts

    if not interactive and "output" not in opts:
        raise MissingRequirementException(
            "Output directory is required in non-interactive mode. Use --output <path>"
        )

    if "output" in opts:
        output_dir = opts["output"]
    elif interactive:
        name = opts.get("name")
        if name:
            output_dir = f"./{name}"
            Console.write_stdout(f"Output directory not specified, using: {output_dir}")
        else:
            output_dir = input("Output directory [./project]: ").strip() or "./project"
    else:
        output_dir = "./project"

    engine = TemplateEngine()

    try:
        template = engine.get_template(template_name)
    except FileNotFoundError as e:
        Console.write_stderr(f"Error: {e}")
        Console.write_stdout("\nAvailable templates:")
        list_templates([])
        return

    params = {}

    for param_spec in template.get("parameters", []):
        param_name = param_spec["name"]

        if param_name in opts:
            params[param_name] = opts[param_name]
        elif interactive:
            prompt = f"{param_spec['description']}"
            if "default" in param_spec:
                prompt += f" [{param_spec['default']}]"
            prompt += ": "

            user_input = input(prompt).strip()
            if user_input:
                params[param_name] = user_input
            elif "default" in param_spec:
                params[param_name] = param_spec["default"]
            elif param_spec.get("required", False):
                Console.write_stderr(f"Error: {param_spec['description']} is required")
                return

    try:
        validated_params = engine.validate_params(template, params)
        generated_files = engine.generate(template_name, Path(output_dir), params)

        Console.write_success(f"\nGenerated {template_name} in {output_dir}")
        Console.write_stdout("\nCreated files:")
        for file in generated_files:
            Console.write_stdout(f"  ✓ {file.relative_to(output_dir)}")

        if "next_steps" in template:
            Console.write_stdout("\nNext steps:")
            for step in template["next_steps"]:
                rendered_step = engine.render_template(step, validated_params)
                Console.write_stdout(f"  {rendered_step}")

    except ValueError as e:
        Console.write_stderr(f"Error: {e}")


def show_template_info(option_list: list):
    """
    Show detailed information about a specific template.
    """
    opts = {opt.name: arg for opt, arg in option_list}
    template_name = opts.get("template")

    if not template_name:
        raise MissingRequirementException(
            "Template name is required. Use --template <name>"
        )

    engine = TemplateEngine()

    try:
        template = engine.get_template(template_name)
    except FileNotFoundError as e:
        Console.write_stderr(f"Error: {e}")
        return

    Console.write_stdout(f"\nTemplate: {template['name']}")
    Console.write_stdout(f"Version: {template.get('version', '1.0.0')}")
    Console.write_stdout(f"Description: {template['description']}")
    Console.write_stdout(f"Category: {template['category']}")
    Console.write_stdout("\nParameters:")

    for param in template.get("parameters", []):
        required = "required" if param.get("required", False) else "optional"
        Console.write_stdout(f"  --{param['name']} ({required})")
        Console.write_stdout(f"    {param['description']}")
        if "default" in param:
            Console.write_stdout(f"    Default: {param['default']}")
        if "choices" in param:
            Console.write_stdout(f"    Choices: {', '.join(param['choices'])}")
        Console.write_stdout("")


list_templates_options = [
    Option(
        "category",
        ["--category", "-c"],
        "Filter templates by category (rag/lab/evaluation/workflow)",
        True,
    ),
]

create_template_options = [
    Option("template", ["--template", "-t"], "Template name (required)", True),
    Option(
        "output",
        ["--output", "-o"],
        "Output directory (required in non-interactive mode)",
        True,
    ),
    Option(
        "no_interactive",
        ["--no-interactive"],
        "Skip interactive prompts, use defaults or provided params",
        False,
    ),
    Option("name", ["--name", "-n"], "Project/assistant/agent name", True),
    Option("description", ["--description", "-d"], "Description", True),
    Option("model", ["--model", "-m"], "LLM model to use", True),
    Option(
        "documents_dir",
        ["--documents-dir"],
        "Directory containing source documents",
        True,
    ),
    Option("embedding_model", ["--embedding-model"], "Embedding model to use", True),
    Option("instructions", ["--instructions", "-i"], "Agent instructions/prompt", True),
    Option("temperature", ["--temperature"], "Sampling temperature (0.0-1.0)", True),
    Option("max_tokens", ["--max-tokens"], "Maximum tokens in response", True),
    Option("tool_name", ["--tool-name"], "Custom tool name", True),
    Option("tool_description", ["--tool-description"], "Custom tool description", True),
    Option(
        "assistant_name",
        ["--assistant-name"],
        "RAG assistant name for evaluation",
        True,
    ),
    Option("agent_name", ["--agent-name"], "Lab agent name for process", True),
    Option("task_name", ["--task-name"], "Task name for process", True),
]

template_info_options = [
    Option("template", ["--template", "-t"], "Template name (required)", True),
]

generate_commands = [
    Command(
        "help",
        ["help", "h"],
        "Display help text",
        show_help,
        ArgumentsEnum.NOT_AVAILABLE,
        [],
        [],
    ),
    Command(
        "list",
        ["list", "ls"],
        "List available templates",
        list_templates,
        ArgumentsEnum.OPTIONAL,
        [],
        list_templates_options,
    ),
    Command(
        "create",
        ["create", "new"],
        "Generate a project from a template",
        create_from_template,
        ArgumentsEnum.REQUIRED,
        [],
        create_template_options,
    ),
    Command(
        "info",
        ["info", "show"],
        "Show template information",
        show_template_info,
        ArgumentsEnum.REQUIRED,
        [],
        template_info_options,
    ),
]
