"""Command-line interface for terraform-ingest."""

import os
import click
import json
import shutil
import yaml

from pathlib import Path
from terraform_ingest.models import RepositoryConfig
from terraform_ingest.ingest import TerraformIngest
from terraform_ingest.models import IngestConfig
from terraform_ingest import __version__, CONFIG_PATH
from terraform_ingest.mcp_service import start as mcp_main
from terraform_ingest.mcp_service import _get_module_resource_impl, ModuleQueryService
from terraform_ingest.indexer import ModuleIndexer


@click.group()
@click.version_option(version=__version__)
def cli():
    """Terraform Ingest - A terraform multi-repo module AI RAG ingestion engine.

    This tool accepts a YAML file of terraform git repository sources,
    downloads them locally, and creates JSON summaries for RAG ingestion.
    """
    pass


@cli.command()
@click.argument("config_file", type=click.Path(exists=True), default=CONFIG_PATH)
@click.option(
    "--output-dir",
    "-o",
    default=None,
    help="Directory to save JSON summaries",
)
@click.option(
    "--clone-dir",
    "-c",
    default=None,
    help="Directory to clone repositories",
)
@click.option(
    "--cleanup/--no-cleanup",
    default=False,
    help="Clean up cloned repositories after ingestion",
)
@click.option(
    "--no-cache/--cache",
    default=False,
    help="Disable caching for module analysis",
)
@click.option(
    "--enable-embeddings/--no-embeddings",
    default=None,
    help="Enable or disable vector database embeddings (overrides config)",
)
@click.option(
    "--embedding-strategy",
    type=click.Choice(
        ["openai", "claude", "sentence-transformers", "chromadb-default"]
    ),
    default=None,
    help="Embedding strategy to use (overrides config)",
)
@click.option(
    "--auto-install-deps/--no-auto-install-deps",
    default=True,
    help="Automatically install missing embedding dependencies",
)
def ingest(
    config_file,
    output_dir,
    clone_dir,
    cleanup,
    no_cache,
    enable_embeddings,
    embedding_strategy,
    auto_install_deps,
):
    """Ingest terraform repositories from a YAML configuration file.

    CONFIG_FILE: Path to the YAML configuration file containing repository sources.

    Example:

        terraform-ingest ingest config.yaml

        terraform-ingest ingest config.yaml -o ./my-output -c ./my-repos

        terraform-ingest ingest config.yaml --enable-embeddings --embedding-strategy sentence-transformers
    """
    click.echo(f"Loading configuration from {config_file}")

    try:
        ingester = TerraformIngest.from_yaml(
            config_file, auto_install_deps=auto_install_deps
        )

        # Override config if command-line options are provided
        if output_dir is not None:
            ingester.config.output_dir = output_dir
            ingester.output_dir = Path(output_dir)

        if clone_dir is not None:
            ingester.config.clone_dir = clone_dir
            ingester.repo_manager.clone_dir = Path(clone_dir)

        # Override embedding config if provided
        if enable_embeddings is not None:
            if ingester.config.embedding is None:
                from terraform_ingest.models import EmbeddingConfig

                ingester.config.embedding = EmbeddingConfig()
            ingester.config.embedding.enabled = enable_embeddings

        if embedding_strategy is not None:
            if ingester.config.embedding is None:
                from terraform_ingest.models import EmbeddingConfig

                ingester.config.embedding = EmbeddingConfig()
            ingester.config.embedding.strategy = embedding_strategy

        # Reinitialize vector DB if embedding config was overridden
        if (
            (enable_embeddings is not None or embedding_strategy is not None)
            and ingester.config.embedding
            and ingester.config.embedding.enabled
        ):
            from terraform_ingest.embeddings import VectorDBManager
            from terraform_ingest.dependency_installer import (
                ensure_embeddings_available,
            )

            # Ensure dependencies for the new strategy are installed
            ensure_embeddings_available(
                ingester.config.embedding,
                logger=ingester.logger,
                auto_install=auto_install_deps,
            )
            ingester.vector_db = VectorDBManager(ingester.config.embedding)

        if no_cache:
            shutil.rmtree(ingester.output_dir, ignore_errors=True)
            shutil.rmtree(ingester.repo_manager.clone_dir, ignore_errors=True)

        ingester.output_dir.mkdir(parents=True, exist_ok=True)
        ingester.repo_manager.clone_dir.mkdir(parents=True, exist_ok=True)

        click.echo("Starting ingestion...")
        summaries = ingester.ingest()

        click.echo("\nIngestion complete!")
        click.echo(f"Processed {len(summaries)} module(s)")
        click.echo(f"Summaries saved to {ingester.output_dir}")

        # Show vector DB stats if enabled
        if ingester.vector_db:
            stats = ingester.get_vector_db_stats()
            if stats.get("enabled"):
                click.echo("\nVector Database Statistics:")
                click.echo(f"  Collection: {stats.get('collection_name')}")
                click.echo(f"  Documents: {stats.get('document_count')}")
                click.echo(f"  Strategy: {stats.get('embedding_strategy')}")

        if cleanup:
            click.echo("Cleaning up cloned repositories...")
            ingester.cleanup()

    except Exception as e:
        click.echo(f"Error during ingestion: {e}", err=True)
        raise click.Abort()


@cli.command()
@click.argument("repository_url")
@click.option(
    "--branch",
    "-b",
    default="main",
    help="Branch to analyze",
)
@click.option(
    "--output",
    "-o",
    type=click.Path(),
    help="Output file for the summary (default: stdout)",
)
@click.option(
    "--include-tags/--no-tags",
    default=False,
    help="Include git tags in analysis",
)
@click.option(
    "--max-tags",
    type=int,
    default=10,
    help="Maximum number of tags to process",
)
@click.option(
    "--recursive/--no-recursive",
    default=True,
    help="Recursively search for terraform modules in subdirectories",
)
def analyze(
    repository_url,
    branch,
    output,
    include_tags,
    max_tags,
    recursive,
):
    """Analyze a single terraform repository.

    REPOSITORY_URL: Git URL of the repository to analyze.

    Example:

        terraform-ingest analyze https://github.com/user/terraform-module

        terraform-ingest analyze https://github.com/user/terraform-module -b develop --include-tags
    """
    click.echo(f"Analyzing repository: {repository_url}")

    try:
        # Create a temporary config
        repo_config = RepositoryConfig(
            url=repository_url,
            branches=[branch],
            include_tags=include_tags,
            max_tags=max_tags,
            recursive=recursive,
        )

        config = IngestConfig(
            repositories=[repo_config],
            output_dir="./output",
            clone_dir="./repos",
        )

        ingester = TerraformIngest(config)
        summaries = ingester.ingest()

        if output:
            output_path = Path(output)
            with open(output_path, "w") as f:
                json.dump([s.model_dump() for s in summaries], f, indent=2, default=str)
            click.echo(f"\nAnalysis saved to {output_path}")
        else:
            for summary in summaries:
                click.echo(json.dumps(summary.model_dump(), indent=2, default=str))

        click.echo(f"\nAnalyzed {len(summaries)} module version(s)")

    except Exception as e:
        click.echo(f"Error during analysis: {e}", err=True)
        raise click.Abort()


@cli.command()
@click.argument("config_file", type=click.Path())
def init(config_file):
    """Initialize a sample configuration file.

    CONFIG_FILE: Path where the configuration file should be created.

    Example:

        terraform-ingest init config.yaml
    """
    config_path = Path(config_file)

    if config_path.exists():
        click.echo(f"Error: {config_file} already exists", err=True)
        raise click.Abort()

    sample_config = {
        "repositories": [
            {
                "url": "https://github.com/terraform-aws-modules/terraform-aws-vpc",
                "name": "terraform-aws-vpc",
                "branches": ["main", "develop"],
                "include_tags": True,
                "max_tags": 1,
                "path": ".",
                "recursive": False,
            },
            {
                "url": "https://github.com/terraform-aws-modules/terraform-aws-ec2-instance",
                "name": "terraform-aws-ec2-instance",
                "branches": ["main"],
                "include_tags": True,
                "max_tags": 3,
                "path": ".",
                "recursive": True,
            },
        ],
        "output_dir": "./output",
        "clone_dir": "./repos",
        "mcp": {
            "auto_ingest": False,
            "ingest_on_startup": False,
            "refresh_interval_hours": None,
        },
        "embedding": {
            "enabled": False,
            "strategy": "chromadb-default",
            "openai_api_key": None,
            "anthropic_api_key": None,
            "openai_model": "text-embedding-3-small",
            "anthropic_model": "claude-3-haiku-20240307",
            "sentence_transformers_model": "all-MiniLM-L6-v2",
            "chromadb_host": None,
            "chromadb_port": 8000,
            "chromadb_path": "./chromadb",
            "collection_name": "terraform_modules",
            "include_description": True,
            "include_readme": True,
            "include_variables": True,
            "include_outputs": True,
            "include_resource_types": True,
            "enable_hybrid_search": True,
            "keyword_weight": 0.3,
            "vector_weight": 0.7,
        },
    }

    with open(config_path, "w") as f:
        yaml.dump(sample_config, f, default_flow_style=False, sort_keys=False)

    click.echo(f"Created sample configuration at {config_file}")
    click.echo("\nConfiguration includes:")
    click.echo("  • Multiple repositories with different branch/tag settings")
    click.echo("  • Output and clone directories")
    click.echo("  • MCP service configuration options")
    click.echo("  • Comprehensive embedding configuration with multiple strategies")
    click.echo("\nEdit this file to customize for your needs, then run:")
    click.echo(f"  terraform-ingest ingest {config_file}")


@cli.command()
@click.argument("query")
@click.option(
    "--config",
    "-c",
    type=click.Path(exists=True),
    default="config.yaml",
    help="Configuration file with vector DB settings",
)
@click.option(
    "--provider",
    "-p",
    default=None,
    help="Filter by provider",
)
@click.option(
    "--repository",
    "-r",
    default=None,
    help="Filter by repository",
)
@click.option(
    "--limit",
    "-l",
    type=int,
    default=10,
    help="Number of results to return",
)
@click.option(
    "--json",
    "-j",
    "output_json",
    is_flag=True,
    default=False,
    help="Output results in JSON format",
)
def search(query, config, provider, repository, limit, output_json):
    """Search the vector database for Terraform modules.

    QUERY: Search query (natural language or keywords)

    Example:

        terraform-ingest search "vpc module for aws"

        terraform-ingest search "kubernetes" --provider aws --limit 5

        terraform-ingest search "vpc" --json
    """
    try:
        # Load config to get vector DB settings
        ingester = TerraformIngest.from_yaml(config)

        if not ingester.vector_db:
            error_msg = "Error: Vector database is not enabled in the configuration"
            if output_json:
                click.echo(json.dumps({"error": error_msg}))
            else:
                click.echo(error_msg, err=True)
                click.echo(
                    "Enable it by setting 'embedding.enabled: true' in your config file",
                    err=True,
                )
            raise click.Abort()

        # Prepare filters
        filters = {}
        if provider:
            filters["provider"] = provider
        if repository:
            filters["repository"] = repository

        # Search
        results = ingester.search_vector_db(
            query, filters=filters if filters else None, n_results=limit
        )

        if not results:
            if output_json:
                click.echo(json.dumps({"results": [], "count": 0}))
            else:
                click.echo("No results found")
            return

        if output_json:
            # Output as JSON
            json_output = {
                "query": query,
                "count": len(results),
                "results": results,
            }
            click.echo(json.dumps(json_output, indent=2))
        else:
            # Output as formatted text
            click.echo(f"\nFound {len(results)} result(s):\n")

            for i, result in enumerate(results, 1):
                metadata = result.get("metadata", {})
                click.echo(f"{i}. {metadata.get('repository', 'Unknown')}")
                click.echo(f"   Ref: {metadata.get('ref', 'Unknown')}")
                click.echo(f"   Path: {metadata.get('path', '.')}")
                click.echo(f"   Provider: {metadata.get('provider', 'Unknown')}")
                if result.get("distance") is not None:
                    click.echo(f"   Relevance: {1.0 - result['distance']:.3f}")
                click.echo()

    except Exception as e:
        if output_json:
            click.echo(json.dumps({"error": str(e)}))
        else:
            click.echo(f"Error during search: {e}", err=True)
        raise click.Abort()


@cli.command()
@click.argument("repository")
@click.argument("ref")
@click.option(
    "--path",
    "-p",
    default=".",
    help="Module path within the repository (default: root)",
)
@click.option(
    "--json",
    "-j",
    "output_json",
    is_flag=True,
    default=False,
    help="Output results in JSON format",
)
@click.option(
    "--output-dir",
    "-o",
    default=None,
    help="Directory containing ingested JSON modules (default: ./output)",
)
def module(repository, ref, path, output_json, output_dir):
    """Display ingested module data.

    REPOSITORY: Repository name or URL identifier
    REF: Git reference (branch, tag, or commit)

    Example:

        terraform-ingest module terraform-aws-vpc v5.0.0

        terraform-ingest module aws-s3 main --path ./modules/bucket

        terraform-ingest module terraform-aws-vpc v5.0.0 --json

        terraform-ingest module terraform-aws-vpc v5.0.0 -o /path/to/output
    """
    try:
        # Determine output directory
        if output_dir is None:
            output_dir = os.getenv("TERRAFORM_INGEST_OUTPUT_DIR", "./output")

        output_path = Path(output_dir)
        if not output_path.exists():
            error_msg = f"Output directory not found: {output_dir}"
            if output_json:
                click.echo(json.dumps({"error": error_msg}))
            else:
                click.echo(error_msg, err=True)
            raise click.Abort()

        # Create a module query service
        service = ModuleQueryService(output_dir=output_dir)

        # Get the module data
        module_data = service.get_module(repository, ref, path, include_readme=False)

        if module_data is None:
            error_msg = f"Module not found: {repository} @ {ref} (path: {path})"
            if output_json:
                click.echo(json.dumps({"error": error_msg}))
            else:
                click.echo(error_msg, err=True)
            raise click.Abort()

        if output_json:
            # Output as JSON
            click.echo(json.dumps(module_data, indent=2, default=str))
        else:
            # Output as formatted text
            # Display module information
            click.echo(f"\n📦 Module: {module_data.get('repository', 'Unknown')}")
            click.echo(f"📍 Ref: {module_data.get('ref', 'Unknown')}")
            if module_data.get("path") and module_data.get("path") != ".":
                click.echo(f"📂 Path: {module_data.get('path', 'Unknown')}")
            click.echo()

            # Display description if available
            if "description" in module_data and module_data["description"]:
                click.echo(f"Description: {module_data['description']}\n")

            # Display providers
            if "providers" in module_data and module_data["providers"]:
                click.echo("Providers:")
                for provider in module_data["providers"]:
                    provider_name = provider.get("name", "Unknown")
                    provider_version = provider.get("version", "Unknown")
                    click.echo(f"  - {provider_name} ({provider_version})")
                click.echo()

            # Display variables
            if "variables" in module_data and module_data["variables"]:
                click.echo("Input Variables:")
                for var in module_data["variables"]:
                    var_name = var.get("name", "Unknown")
                    var_type = var.get("type", "Unknown")
                    var_desc = var.get("description", "")
                    default = var.get("default")
                    required = var.get("required", False)
                    click.echo(
                        f"  - {var_name} ({var_type})"
                        + (" [required]" if required else "")
                    )
                    if var_desc:
                        click.echo(f"    {var_desc}")
                    if default is not None:
                        click.echo(f"    Default: {default}")
                click.echo()

            # Display outputs
            if "outputs" in module_data and module_data["outputs"]:
                click.echo("Outputs:")
                for output in module_data["outputs"]:
                    output_name = output.get("name", "Unknown")
                    output_desc = output.get("description", "")
                    click.echo(f"  - {output_name}")
                    if output_desc:
                        click.echo(f"    {output_desc}")
                click.echo()

            # Display modules (sub-modules)
            if "modules" in module_data and module_data["modules"]:
                click.echo("Sub-modules:")
                for submod in module_data["modules"]:
                    submod_name = submod.get("name", "Unknown")
                    submod_source = submod.get("source", "Unknown")
                    click.echo(f"  - {submod_name}: {submod_source}")
                click.echo()

            # Display resources summary
            if "resources" in module_data and module_data["resources"]:
                click.echo(
                    f"Resources: {len(module_data['resources'])} managed resource(s)"
                )
                click.echo()

    except Exception as e:
        error_msg = f"Error retrieving module: {e}"
        if output_json:
            click.echo(json.dumps({"error": error_msg}))
        else:
            click.echo(error_msg, err=True)
        raise click.Abort()


@cli.command()
@click.option(
    "--host",
    default="0.0.0.0",
    help="Host to bind the server to",
)
@click.option(
    "--port",
    "-p",
    default=8000,
    type=int,
    help="Port to bind the server to",
)
def serve(host, port):
    """Start the FastAPI server.

    Example:

        terraform-ingest serve

        terraform-ingest serve --host 127.0.0.1 --port 8080
    """
    import uvicorn
    from .api import app

    click.echo(f"Starting Terraform Ingest API server on {host}:{port}")
    click.echo("Press CTRL+C to quit")

    uvicorn.run(app, host=host, port=port)


@cli.command()
@click.option(
    "--config",
    "-c",
    default=None,
    help="Configuration file for auto-ingestion settings",
)
@click.option(
    "--transport",
    "-t",
    type=click.Choice(["stdio", "streamable-http", "sse"]),
    default=None,
    help="Transport mode (stdio, streamable-http, or sse)",
)
@click.option(
    "--host",
    "-h",
    default=None,
    help="Host to bind to (for streamable-http and sse transports)",
)
@click.option(
    "--port",
    "-p",
    type=int,
    default=None,
    help="Port to bind to (for streamable-http and sse transports)",
)
@click.option(
    "--ingest-on-startup/--no-ingest-on-startup",
    default=None,
    help="Run ingestion immediately on startup (overrides config)",
)
def mcp(config, transport, host, port, ingest_on_startup):
    """Start the MCP (Model Context Protocol) server.

    The MCP server exposes ingested Terraform modules to AI agents and supports
    auto-ingestion based on configuration settings.

    Supported transports:
    - stdio (default): Standard input/output communication
    - streamable-http: HTTP with streaming support
    - sse: Server-Sent Events over HTTP

    Example:

        terraform-ingest mcp

        terraform-ingest mcp --config my-config.yaml

        terraform-ingest mcp --ingest-on-startup

        terraform-ingest mcp --transport streamable-http --host 0.0.0.0 --port 3000

        terraform-ingest mcp --transport sse --host localhost --port 8000 --ingest-on-startup
    """
    # Set the config file environment variable if provided
    if config is not None:
        os.environ["TERRAFORM_INGEST_CONFIG"] = config
    else:
        config = os.getenv("TERRAFORM_INGEST_CONFIG", "config.yaml")
        os.environ["TERRAFORM_INGEST_CONFIG"] = config

    # if transport != "stdio" and transport is not None:
    #     click.echo(f"Transport: {transport}")
    #     click.echo(f"Address: {host or '127.0.0.1'}:{port or 3000}")
    #     if ingest_on_startup is not None:
    #         click.echo(f"Ingest on startup: {ingest_on_startup}")
    #     click.echo("Press CTRL+C to quit")

    try:
        mcp_main(
            config_file=config,
            transport=transport,
            host=host,
            port=port,
            ingest_on_startup=ingest_on_startup,
        )
    except KeyboardInterrupt:
        click.echo("\nMCP server stopped")


@cli.group()
def function():
    """Manage MCP functions and operations.

    This group provides commands to interact with MCP (Model Context Protocol)
    functions, including showing available functions and executing them.

    Example:

        terraform-ingest function show

        terraform-ingest function exec my-function --arg value
    """
    pass


@function.command()
@click.option(
    "--output-dir",
    "-o",
    default="./output",
    help="Directory containing ingested module summaries",
)
@click.option(
    "--format",
    "-f",
    type=click.Choice(["table", "json", "list"]),
    default="table",
    help="Output format for function list",
)
def show(output_dir, format):
    """Show available MCP functions and their details.

    This command displays information about all available MCP functions
    that can be executed against ingested Terraform modules.

    Example:

        terraform-ingest function show

        terraform-ingest function show --output-dir ./my-output --format json
    """
    try:
        # Import the MCP instance to get exposed functions
        from terraform_ingest.mcp_service import mcp

        # Dynamically detect exposed MCP functions from the tool manager
        functions = []
        if hasattr(mcp, "_tool_manager") and hasattr(mcp._tool_manager, "_tools"):
            tools_dict = mcp._tool_manager._tools
            for tool_name, tool in tools_dict.items():
                # Extract function information from the tool
                func_info = {
                    "name": tool_name,
                    "description": tool.description or "No description available",
                    "parameters": [],
                }

                # Extract parameters from tool's parameter schema
                if hasattr(tool, "parameters") and tool.parameters:
                    params_schema = tool.parameters
                    if (
                        isinstance(params_schema, dict)
                        and "properties" in params_schema
                    ):
                        func_info["parameters"] = list(
                            params_schema["properties"].keys()
                        )

                functions.append(func_info)

        # Sort functions by name for consistent output
        functions.sort(key=lambda x: x["name"])

        if format == "json":
            click.echo(json.dumps(functions, indent=2))
        elif format == "list":
            if functions:
                for func in functions:
                    click.echo(f"• {func['name']}")
            else:
                click.echo("No MCP functions found")
        else:  # table format
            if functions:
                click.echo("Available MCP Functions:")
                click.echo("-" * 80)
                for func in functions:
                    click.echo(f"\nFunction: {func['name']}")
                    click.echo(f"Description: {func['description']}")
                    if func["parameters"]:
                        click.echo(f"Parameters: {', '.join(func['parameters'])}")
                    else:
                        click.echo("Parameters: (none)")
            else:
                click.echo("No MCP functions found")

    except Exception as e:
        click.echo(f"Error showing functions: {e}", err=True)
        raise click.Abort()


@cli.command()
@click.argument("resource_path")
@click.option(
    "--output-dir",
    "-o",
    default="./output",
    help="Directory containing ingested module summaries",
)
def resource(resource_path, output_dir):
    """Retrieve and display an MCP resource by its path.

    RESOURCE_PATH: MCP resource path in the format 'module://repository/ref/path'

    This command retrieves a specific Terraform module resource and returns
    its JSON summary including variables, outputs, providers, and README.

    Example:

        terraform-ingest resource module://terraform-aws-vpc/v5.0.0

        terraform-ingest resource module://terraform-aws-ec2/main

        terraform-ingest resource module://terraform-aws-vpc/v5.0.0/modules-networking
    """
    try:
        # Parse the resource path
        # Expected format: module://repository/ref/path
        if not resource_path.startswith("module://"):
            click.echo(
                "Error: Resource path must start with 'module://' (e.g., module://terraform-aws-vpc/v5.0.0)",
                err=True,
            )
            raise click.Abort()

        # Remove the 'module://' prefix and split
        path_parts = resource_path[9:].split("/")

        if len(path_parts) < 2:
            click.echo(
                "Error: Resource path must include repository and ref (e.g., module://repository/ref)",
                err=True,
            )
            click.echo(
                "Optional path parameter: module://repository/ref/path",
                err=True,
            )
            raise click.Abort()

        repository_name = path_parts[0]
        ref = path_parts[1]
        resource_subpath = "/".join(path_parts[2:]) if len(path_parts) > 2 else "-"

        # Use the MCP implementation to get the resource
        result = _get_module_resource_impl(repository_name, ref, resource_subpath)

        click.echo(result)

    except click.Abort:
        raise
    except Exception as e:
        click.echo(f"Error retrieving resource: {e}", err=True)
        raise click.Abort()


@function.command()
@click.argument("function_name")
@click.option(
    "--arg",
    "-a",
    multiple=True,
    nargs=2,
    help="Function arguments as key-value pairs (use multiple times for multiple args)",
)
@click.option(
    "--output-dir",
    "-o",
    default="./output",
    help="Directory containing ingested module summaries",
)
@click.option(
    "--format",
    "-f",
    type=click.Choice(["json", "text"]),
    default="json",
    help="Output format for function results",
)
def exec(function_name, arg, output_dir, format):
    """Execute an MCP function.

    FUNCTION_NAME: Name of the function to execute.

    This command executes a specific MCP function with the provided arguments
    and returns the results.

    Example:

        terraform-ingest function exec list_modules

        terraform-ingest function exec search_modules -a query "aws vpc" -a provider "aws"

        terraform-ingest function exec get_module_details -a repository "https://github.com/..." -a ref "main"
    """
    try:
        # Convert arguments to dictionary
        args_dict = {}
        for key, value in arg:
            args_dict[key] = value

        # Add output_dir to arguments if not already present
        if "output_dir" not in args_dict and function_name != "search_modules_vector":
            args_dict["output_dir"] = output_dir

        # click.echo(f"Executing function: {function_name}")
        # click.echo(f"Arguments: {args_dict}")

        # Import the ModuleQueryService
        from terraform_ingest.mcp_service import ModuleQueryService, MCPContext

        # Map function names to methods
        if function_name == "search_modules_vector":
            # This function needs the MCPContext for vector DB access
            ctx = MCPContext.get_instance()
            if not ctx.ingester or not ctx.ingester.vector_db:
                click.echo("Error: Vector database is not enabled", err=True)
                raise click.Abort()
            result = ctx.ingester.search_vector_db(
                args_dict.get("query", ""),
                filters={
                    k: v
                    for k, v in args_dict.items()
                    if k in ["provider", "repository"]
                },
                n_results=int(args_dict.get("limit", 10)),
            )
        else:
            # Use ModuleQueryService for other functions
            service = ModuleQueryService(
                output_dir=args_dict.get("output_dir", "./output")
            )

            if function_name == "list_repositories":
                result = service.list_repositories(
                    filter_keyword=args_dict.get("filter"),
                    limit=int(args_dict.get("limit", 50)),
                )
            elif function_name == "search_modules":
                repo_urls = None
                if "repo_urls" in args_dict:
                    # Handle comma-separated or list-style repo URLs
                    repo_urls = (
                        args_dict["repo_urls"].split(",")
                        if isinstance(args_dict["repo_urls"], str)
                        else args_dict["repo_urls"]
                    )
                result = service.search_modules(
                    query=args_dict.get("query", ""),
                    repo_urls=repo_urls,
                    provider=args_dict.get("provider"),
                )
            elif function_name == "get_module_details":
                # Parse 'all' argument as boolean (default: False)
                include_readme = args_dict.get("all", "false").lower() in (
                    "true",
                    "1",
                    "yes",
                )
                result = service.get_module(
                    repository=args_dict.get("repository", ""),
                    ref=args_dict.get("ref", ""),
                    path=args_dict.get("path", "."),
                    include_readme=include_readme,
                )
            elif function_name == "list_modules":
                result = service.list_modules(limit=int(args_dict.get("limit", 100)))
            elif function_name == "list_module_resources":
                result = service.list_module_resources(
                    repository=args_dict.get("repository", ""),
                    ref=args_dict.get("ref", ""),
                    path=args_dict.get("path", "."),
                )
            else:
                click.echo(f"Error: Unknown function '{function_name}'", err=True)
                raise click.Abort()

        # Output the result
        if format == "json":
            click.echo(json.dumps(result, indent=2, default=str))
        else:  # text format
            click.echo(json.dumps(result, indent=2, default=str))

    except Exception as e:
        click.echo(f"Error executing function '{function_name}': {e}", err=True)
        raise click.Abort()


@cli.group()
def index():
    """Manage the module index for fast lookups."""
    pass


@index.command()
@click.option(
    "--output-dir",
    default="./output",
    type=click.Path(),
    help="Output directory with module JSON files",
)
def rebuild(output_dir):
    """Rebuild the module index from all JSON files."""
    try:
        indexer = ModuleIndexer(output_dir)
        count = indexer.rebuild_from_files()
        click.echo(f"✓ Index rebuilt with {count} modules")
    except Exception as e:
        click.echo(f"Error rebuilding index: {e}", err=True)
        raise click.Abort()


@index.command()
@click.option(
    "--output-dir",
    default="./output",
    type=click.Path(),
    help="Output directory with module JSON files",
)
def stats(output_dir):
    """Show module index statistics."""
    try:
        indexer = ModuleIndexer(output_dir)
        stats_data = indexer.get_stats()
        click.echo("\n📊 Module Index Statistics:")
        click.echo(f"  Total Modules: {stats_data['total_modules']}")
        click.echo(f"  Unique Providers: {stats_data['unique_providers']}")
        if stats_data["providers"]:
            click.echo(f"  Providers: {', '.join(stats_data['providers'])}")
        click.echo(f"  Unique Tags: {stats_data['unique_tags']}")
        click.echo(f"  Index File: {stats_data['index_file']}\n")
    except Exception as e:
        click.echo(f"Error getting index stats: {e}", err=True)
        raise click.Abort()


@index.command()
@click.argument("doc_id")
@click.option(
    "--output-dir",
    default="./output",
    type=click.Path(),
    help="Output directory with module JSON files",
)
@click.option(
    "--json",
    "output_json",
    is_flag=True,
    help="Output as JSON",
)
def lookup(doc_id, output_dir, output_json):
    """Look up a module by its document ID."""
    try:
        indexer = ModuleIndexer(output_dir)
        module = indexer.get_module(doc_id)

        if not module:
            click.echo(f"Error: Module with ID '{doc_id}' not found", err=True)
            raise click.Abort()

        if output_json:
            click.echo(json.dumps(module, indent=2))
        else:
            click.echo(f"\n📦 Module: {doc_id}")
            click.echo(f"  Repository: {module['repository']}")
            click.echo(f"  Ref: {module['ref']}")
            click.echo(f"  Path: {module['path']}")
            click.echo(f"  Provider: {module['provider']}")
            click.echo(f"  Summary File: {module['summary_file']}")
            if module.get("tags"):
                click.echo(f"  Tags: {', '.join(module['tags'])}")
            click.echo()
    except Exception as e:
        click.echo(f"Error looking up module: {e}", err=True)
        raise click.Abort()


@index.command()
@click.argument("provider")
@click.option(
    "--output-dir",
    default="./output",
    type=click.Path(),
    help="Output directory with module JSON files",
)
@click.option(
    "--json",
    "output_json",
    is_flag=True,
    help="Output as JSON",
)
def by_provider(provider, output_dir, output_json):
    """Search modules by provider."""
    try:
        indexer = ModuleIndexer(output_dir)
        results = indexer.search_by_provider(provider)

        if not results:
            click.echo(f"No modules found for provider '{provider}'")
            return

        if output_json:
            click.echo(json.dumps(results, indent=2))
        else:
            click.echo(
                f"\n🔍 Found {len(results)} module(s) for provider '{provider}':\n"
            )
            for module in results:
                click.echo(f"  • {module['repository']} ({module['ref']})")
                click.echo(f"    Path: {module['path']}")
                click.echo(f"    ID: {module['id']}\n")
    except Exception as e:
        click.echo(f"Error searching by provider: {e}", err=True)
        raise click.Abort()


@index.command()
@click.argument("tag")
@click.option(
    "--output-dir",
    default="./output",
    type=click.Path(),
    help="Output directory with module JSON files",
)
@click.option(
    "--json",
    "output_json",
    is_flag=True,
    help="Output as JSON",
)
def by_tag(tag, output_dir, output_json):
    """Search modules by tag."""
    try:
        indexer = ModuleIndexer(output_dir)
        results = indexer.search_by_tag(tag)

        if not results:
            click.echo(f"No modules found with tag '{tag}'")
            return

        if output_json:
            click.echo(json.dumps(results, indent=2))
        else:
            click.echo(f"\n🏷️  Found {len(results)} module(s) with tag '{tag}':\n")
            for module in results:
                click.echo(f"  • {module['repository']} ({module['ref']})")
                click.echo(f"    Path: {module['path']}")
                click.echo(f"    ID: {module['id']}\n")
    except Exception as e:
        click.echo(f"Error searching by tag: {e}", err=True)
        raise click.Abort()


@index.command()
@click.argument("doc_id")
@click.option(
    "--output-dir",
    default="./output",
    type=click.Path(),
    help="Output directory with module JSON files",
)
@click.option(
    "--json",
    "output_json",
    is_flag=True,
    help="Output as JSON",
)
def get(doc_id, output_dir, output_json):
    """Get full module summary by index ID."""
    try:
        indexer = ModuleIndexer(output_dir)
        module_entry = indexer.get_module(doc_id)

        if not module_entry:
            click.echo(f"Error: Module with ID '{doc_id}' not found in index", err=True)
            raise click.Abort()

        # Get the path to the summary file
        summary_path = indexer.get_module_summary_path(doc_id)

        if not summary_path or not summary_path.exists():
            click.echo(
                f"Error: Module index entry found but summary file not found at {summary_path}",
                err=True,
            )
            raise click.Abort()

        # Load the full module summary
        with open(summary_path, "r", encoding="utf-8") as f:
            summary = json.load(f)

        if output_json:
            click.echo(json.dumps(summary, indent=2))
        else:
            click.echo(f"\n📄 Module Summary for ID: {doc_id}\n")
            click.echo(f"Repository: {summary.get('repository', 'N/A')}")
            click.echo(f"Ref: {summary.get('ref', 'N/A')}")
            click.echo(f"Path: {summary.get('path', 'N/A')}")
            click.echo(f"Description: {summary.get('description', 'N/A')}\n")

            # Show providers
            if summary.get("providers"):
                click.echo("Providers:")
                for provider in summary["providers"]:
                    click.echo(
                        f"  • {provider.get('name', 'unknown')} "
                        f"({provider.get('source', 'unknown')})"
                    )
                click.echo()

            # Show variables
            if summary.get("variables"):
                click.echo(f"Variables ({len(summary['variables'])}):")
                for var in summary["variables"]:
                    required = " (required)" if var.get("required") else ""
                    click.echo(f"  • {var.get('name', 'unknown')}{required}")
                    if var.get("description"):
                        click.echo(f"    Description: {var['description']}")
                    if var.get("type"):
                        click.echo(f"    Type: {var['type']}")
                click.echo()

            # Show outputs
            if summary.get("outputs"):
                click.echo(f"Outputs ({len(summary['outputs'])}):")
                for output in summary["outputs"]:
                    click.echo(f"  • {output.get('name', 'unknown')}")
                    if output.get("description"):
                        click.echo(f"    Description: {output['description']}")
                click.echo()

            # Show README preview
            if summary.get("readme_content"):
                readme = summary["readme_content"]
                lines = readme.split("\n")[:10]
                click.echo("README Preview:")
                for line in lines:
                    click.echo(f"  {line}")
                if len(summary["readme_content"].split("\n")) > 10:
                    click.echo("  ...")
                click.echo()

    except json.JSONDecodeError:
        click.echo("Error: Invalid JSON in module summary file", err=True)
        raise click.Abort()
    except Exception as e:
        click.echo(f"Error retrieving module summary: {e}", err=True)
        raise click.Abort()


def main():
    """Entry point for the CLI."""
    cli()


if __name__ == "__main__":
    main()
