"""Command-line interface for ChromaSQL query execution."""

from __future__ import annotations

import argparse
import asyncio
import json
import logging
import sys
from pathlib import Path
from typing import Any, Optional, Sequence

from .executor import QueryExecutor

logger = logging.getLogger(__name__)


def setup_logging(verbose: bool = False) -> None:
    """Configure logging for CLI."""
    level = logging.DEBUG if verbose else logging.INFO
    logging.basicConfig(
        level=level,
        format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
        datefmt="%Y-%m-%d %H:%M:%S",
    )


def create_embed_function(model: Optional[str] = None) -> Optional[Any]:
    """Create embedding function for TEXT queries.

    Parameters
    ----------
    model:
        Optional embedding model name (e.g., "text-embedding-3-small").

    Returns
    -------
    Optional embedding function, or None if not configured.
    """
    if not model:
        return None

    try:
        from openai import OpenAI

        client = OpenAI()

        def embed_fn(text: str, model_override: Optional[str] = None):
            effective_model = model_override or model
            response = client.embeddings.create(input=text, model=effective_model)
            return response.data[0].embedding

        logger.info("Created OpenAI embedding function (model=%s)", model)
        return embed_fn

    except ImportError:
        logger.warning("OpenAI not installed; TEXT queries will not work")
        return None
    except Exception as e:
        logger.error("Failed to create embedding function: %s", e)
        return None


def format_result_table(rows: list[dict], limit: int = 100) -> str:
    """Format query results as a simple table.

    Parameters
    ----------
    rows:
        Result rows to format.
    limit:
        Maximum number of rows to display.

    Returns
    -------
    Formatted table string.
    """
    if not rows:
        return "No results"

    # Get column names from first row
    columns = list(rows[0].keys())

    # Calculate column widths
    widths = {col: len(col) for col in columns}
    for row in rows[:limit]:
        for col in columns:
            value_str = str(row.get(col, ""))
            widths[col] = max(widths[col], len(value_str))

    # Build header
    header = " | ".join(col.ljust(widths[col]) for col in columns)
    separator = "-+-".join("-" * widths[col] for col in columns)

    # Build rows
    lines = [header, separator]
    for row in rows[:limit]:
        line = " | ".join(str(row.get(col, "")).ljust(widths[col]) for col in columns)
        lines.append(line)

    if len(rows) > limit:
        lines.append(f"\n... {len(rows) - limit} more row(s)")

    return "\n".join(lines)


async def execute_query(args: argparse.Namespace) -> int:
    """Execute a ChromaSQL query.

    Parameters
    ----------
    args:
        Parsed command-line arguments.

    Returns
    -------
    Exit code (0 for success, 1 for failure).
    """
    try:
        # Create embedding function if needed
        embed_fn = None
        if args.embedding_model:
            embed_fn = create_embed_function(args.embedding_model)

        # Initialize executor
        executor = QueryExecutor(
            query_config_path=args.query_config,
            discriminator_field=args.discriminator_field,
            client_type=args.client_type,
            http_host=args.http_host,
            http_port=args.http_port,
            http_ssl=args.http_ssl,
            cloud_api_key=args.cloud_api_key,
            cloud_tenant=args.cloud_tenant,
            cloud_database=args.cloud_database,
            embed_fn=embed_fn,
            fallback_to_all=args.fallback_to_all,
        )

        async with executor:
            # Execute query
            result = await executor.execute(args.cql)

            # Display results
            if args.output_format == "json":
                output = {
                    "query": result.query,
                    "total_rows": result.total_rows,
                    "collections_queried": result.collections_queried,
                    "rows": result.rows,
                }
                print(json.dumps(output, indent=2))
            else:
                # Table format
                print(f"\nQuery: {result.query}")
                print(f"Collections queried: {result.collections_queried}")
                print(f"Total rows: {result.total_rows}\n")
                print(format_result_table(result.rows, limit=args.limit))

        return 0

    except Exception as e:
        logger.error("Query execution failed: %s", e, exc_info=args.verbose)
        return 1


def main(argv: Optional[Sequence[str]] = None) -> int:
    """Main CLI entry point.

    Parameters
    ----------
    argv:
        Command-line arguments (defaults to sys.argv).

    Returns
    -------
    Exit code.
    """
    parser = argparse.ArgumentParser(
        description="Execute ChromaSQL queries against multi-collection ChromaDB",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
examples:
  # Query specific models
  %(prog)s --query-config query_config.json \\
    --cql "SELECT id, distance FROM demo WHERE metadata.model = 'Table' \\
           USING EMBEDDING (TEXT 'search query') TOPK 10;"

  # Query all collections
  %(prog)s --query-config query_config.json \\
    --cql "SELECT * FROM demo WHERE metadata.status = 'active';"

  # Output as JSON
  %(prog)s --query-config query_config.json \\
    --cql "SELECT id FROM demo LIMIT 5" --format json

  # Use HTTP client
  %(prog)s --query-config query_config.json \\
    --client-type http --http-host localhost --http-port 8000 \\
    --cql "SELECT * FROM demo LIMIT 10;"
        """,
    )

    # Required arguments
    parser.add_argument(
        "--query-config",
        type=Path,
        required=True,
        help="Path to query_config.json (maps models to collections)",
    )
    parser.add_argument(
        "--cql",
        type=str,
        required=True,
        help="ChromaSQL query to execute",
    )

    # Client configuration
    parser.add_argument(
        "--client-type",
        choices=["http", "cloud", "memory"],
        default="http",
        help="ChromaDB client type (default: http)",
    )
    parser.add_argument(
        "--http-host",
        type=str,
        help="HTTP client host",
    )
    parser.add_argument(
        "--http-port",
        type=int,
        default=8000,
        help="HTTP client port (default: 8000)",
    )
    parser.add_argument(
        "--http-ssl",
        action="store_true",
        help="Use SSL for HTTP client",
    )
    parser.add_argument(
        "--cloud-api-key",
        type=str,
        help="Chroma Cloud API key",
    )
    parser.add_argument(
        "--cloud-tenant",
        type=str,
        default="default_tenant",
        help="Chroma Cloud tenant (default: default_tenant)",
    )
    parser.add_argument(
        "--cloud-database",
        type=str,
        default="default_database",
        help="Chroma Cloud database (default: default_database)",
    )

    # Routing configuration
    parser.add_argument(
        "--discriminator-field",
        type=str,
        default="model",
        help="Metadata field for routing (default: model)",
    )
    parser.add_argument(
        "--no-fallback",
        dest="fallback_to_all",
        action="store_false",
        help="Require discriminator field (don't query all collections as fallback)",
    )

    # Embedding configuration
    parser.add_argument(
        "--embedding-model",
        type=str,
        help="OpenAI embedding model for TEXT queries (e.g., text-embedding-3-small)",
    )

    # Output configuration
    parser.add_argument(
        "--format",
        dest="output_format",
        choices=["table", "json"],
        default="table",
        help="Output format (default: table)",
    )
    parser.add_argument(
        "--limit",
        type=int,
        default=100,
        help="Maximum rows to display in table format (default: 100)",
    )

    # Logging
    parser.add_argument(
        "-v",
        "--verbose",
        action="store_true",
        help="Enable verbose logging",
    )

    args = parser.parse_args(argv)

    # Setup logging
    setup_logging(args.verbose)

    # Execute query
    return asyncio.run(execute_query(args))


if __name__ == "__main__":
    sys.exit(main())
