"""
vMCP CLI - Command Line Interface
==================================

Provides command-line tools for managing vMCP servers and configurations.
"""

import typer
from rich.console import Console
from rich.table import Table
from rich.panel import Panel
from rich import print as rprint
from typing import Optional
import asyncio

app = typer.Typer(
    name="vmcp",
    help="vMCP - Virtual Model Context Protocol CLI",
    add_completion=False
)

console = Console()


# ============================================================================
# Server Commands
# ============================================================================

@app.command("run")
def run(
    host: str = typer.Option("0.0.0.0", "--host", "-h", help="Host to bind to"),
    port: int = typer.Option(8000, "--port", "-p", help="Port to bind to"),
    skip_db_check: bool = typer.Option(False, "--skip-db-check", help="Skip database connectivity check"),
    open_browser: bool = typer.Option(True, "--open/--no-open", help="Open browser after starting")
):
    """
    Run vMCP with automatic setup (single command start).

    This command:
    - Checks database connectivity
    - Runs database migrations if needed
    - Starts the FastAPI server
    - Opens the web interface in your browser

    Example:
        uvx vmcp run
        vmcp run --port 8080
        vmcp run --no-open
    """
    import subprocess
    import time
    import webbrowser
    import sys

    console.print(Panel.fit(
        "[bold green]vMCP - Virtual Model Context Protocol[/bold green]\n\n"
        "[cyan]Starting complete vMCP environment...[/cyan]",
        title="vMCP Run",
        border_style="green"
    ))

    # Step 1: Check database connectivity
    if not skip_db_check:
        console.print("\n[yellow]1. Checking database connectivity...[/yellow]")
        try:
            import os
            from pathlib import Path
            from sqlalchemy import create_engine, text

            # Default to SQLite for zero-config setup (like Langflow)
            default_db_dir = Path.home() / ".vmcp"
            default_db_dir.mkdir(parents=True, exist_ok=True)
            default_db_path = default_db_dir / "vmcp.db"

            db_url = os.getenv("DATABASE_URL", f"sqlite:///{default_db_path}")

            # Handle PostgreSQL connection string format
            if db_url.startswith("postgresql://"):
                engine = create_engine(db_url.replace("postgresql://", "postgresql+psycopg2://"))
            else:
                engine = create_engine(db_url)

            with engine.connect() as conn:
                conn.execute(text("SELECT 1"))

            if "sqlite" in db_url:
                console.print(f"[green]✓[/green] Database ready (SQLite: {default_db_path})")
            else:
                console.print("[green]✓[/green] Database connected!")
        except Exception as e:
            console.print(f"[red]✗[/red] Database connection failed: {e}")
            console.print("\n[yellow]To use PostgreSQL instead of SQLite:[/yellow]")
            console.print("  docker run -d --name vmcp-postgres \\")
            console.print("    -e POSTGRES_USER=vmcp \\")
            console.print("    -e POSTGRES_PASSWORD=vmcp \\")
            console.print("    -e POSTGRES_DB=vmcp \\")
            console.print("    -p 5432:5432 postgres:16")
            console.print("  export DATABASE_URL=postgresql://vmcp:vmcp@localhost:5432/vmcp")
            raise typer.Exit(code=1)

    # Step 2: Initialize database tables
    console.print("\n[yellow]2. Initializing database...[/yellow]")
    try:
        from vmcp.backend.storage.database import init_db, SessionLocal
        from vmcp.backend.storage.dummy_user import ensure_dummy_user
        from vmcp.backend.storage.models import GlobalMCPServerRegistry

        # Create tables if they don't exist
        init_db()

        # Ensure dummy user exists
        ensure_dummy_user()

        console.print("[green]✓[/green] Database initialized!")

        # Always load/update the MCP registry
        console.print("\n[yellow]3. Loading preconfigured MCP servers...[/yellow]")
        from vmcp.backend.scripts.upload_preconfigured_servers import main as upload_main
        upload_main()

    except Exception as e:
        console.print(f"[yellow]⚠[/yellow] Database initialization warning: {e}")
        console.print("    Continuing anyway (database may already be initialized)")

    # Step 4: Start the server
    console.print("\n[yellow]4. Starting vMCP server...[/yellow]")
    console.print(Panel.fit(
        f"[bold cyan]Server Configuration[/bold cyan]\n\n"
        f"URL: [green]http://localhost:{port}[/green]\n"
        f"API: [green]http://localhost:{port}/api[/green]\n"
        f"Docs: [green]http://localhost:{port}/docs[/green]\n"
        f"Documentation: [green]http://localhost:{port}/documentation[/green]",
        border_style="cyan"
    ))

    # Open browser after server health check
    if open_browser:
        def open_browser_when_ready():
            import httpx
            max_attempts = 30  # 15 seconds total (30 * 0.5s)
            for attempt in range(max_attempts):
                try:
                    response = httpx.get(f"http://localhost:{port}/api/mcps/health", timeout=1.0)
                    if response.status_code == 200:
                        # Server is ready!
                        time.sleep(0.5)  # Small buffer for final setup
                        webbrowser.open(f"http://localhost:{port}")
                        return
                except (httpx.ConnectError, httpx.TimeoutException):
                    pass
                time.sleep(0.5)
            # Fallback: open anyway after timeout
            webbrowser.open(f"http://localhost:{port}")

        import threading
        browser_thread = threading.Thread(target=open_browser_when_ready, daemon=True)
        browser_thread.start()

    # Start uvicorn
    import uvicorn
    from vmcp.main import app as fastapi_app

    console.print("\n[green]✓[/green] vMCP is running!\n")
    console.print("[dim]Press Ctrl+C to stop[/dim]\n")

    try:
        uvicorn.run(
            fastapi_app,
            host=host,
            port=port,
            log_level="info"
        )
    except KeyboardInterrupt:
        console.print("\n\n[yellow]Shutting down vMCP...[/yellow]")
        console.print("[green]✓[/green] Goodbye!")


@app.command("serve")
def serve(
    host: str = typer.Option("0.0.0.0", "--host", "-h", help="Host to bind to"),
    port: int = typer.Option(8000, "--port", "-p", help="Port to bind to"),
    reload: bool = typer.Option(False, "--reload", "-r", help="Enable auto-reload (development)"),
    log_level: str = typer.Option("info", "--log-level", "-l", help="Log level (debug, info, warning, error)")
):
    """
    Start the vMCP server (without automatic setup).

    Example:
        vmcp serve
        vmcp serve --port 8080 --reload
    """
    import uvicorn
    from vmcp.main import app as fastapi_app

    console.print(Panel.fit(
        f"[bold green]Starting vMCP Server[/bold green]\n\n"
        f"Host: [cyan]{host}[/cyan]\n"
        f"Port: [cyan]{port}[/cyan]\n"
        f"Reload: [cyan]{reload}[/cyan]\n"
        f"Log Level: [cyan]{log_level}[/cyan]",
        title="vMCP Server",
        border_style="green"
    ))

    uvicorn.run(
        fastapi_app,
        host=host,
        port=port,
        reload=reload,
        log_level=log_level
    )


@app.command("version")
def version():
    """Show vMCP version information."""
    rprint(Panel.fit(
        "[bold cyan]vMCP - Virtual Model Context Protocol[/bold cyan]\n\n"
        "Version: [green]0.1.0[/green]\n"
        "Python Package: [green]vmcp[/green]",
        title="Version Info",
        border_style="cyan"
    ))


@app.command("info")
def info():
    """Show vMCP system information."""
    rprint(Panel.fit(
        "[bold cyan]vMCP - Virtual Model Context Protocol[/bold cyan]\n\n"
        "[bold]Features:[/bold]\n"
        "  • MCP Server Management\n"
        "  • Virtual MCP Aggregation\n"
        "  • Custom Tools (Prompt/Python/HTTP)\n"
        "  • Variable Substitution (@param, @config, @tool, @resource, @prompt)\n"
        "  • Jinja2 Templates\n"
        "  • OpenAI Apps SDK Widgets\n"
        "  • Multiple Auth Types (Bearer, API Key, Basic, Custom)\n"
        "  • Transport Types (stdio, SSE, HTTP)\n\n"
        "[bold]Documentation:[/bold] https://docs.vmcp.dev\n"
        "[bold]Repository:[/bold] https://github.com/vmcp/vmcp",
        title="System Info",
        border_style="cyan"
    ))


# ============================================================================
# Database Commands
# ============================================================================

db_app = typer.Typer(help="Database management commands")
app.add_typer(db_app, name="db")


@db_app.command("init")
def db_init():
    """
    Initialize the database schema.

    Example:
        vmcp db init
    """
    try:
        from vmcp.backend.storage.database import init_db

        console.print("[yellow]Initializing database...[/yellow]")
        init_db()
        console.print("[green]✓[/green] Database initialized successfully!")

    except Exception as e:
        console.print(f"[red]✗[/red] Failed to initialize database: {e}")
        raise typer.Exit(code=1)


@db_app.command("upgrade")
def db_upgrade(
    revision: str = typer.Option("head", "--revision", "-r", help="Alembic revision to upgrade to")
):
    """
    Run database migrations.

    Example:
        vmcp db upgrade
        vmcp db upgrade --revision head
    """
    try:
        import subprocess

        console.print(f"[yellow]Running database migrations to {revision}...[/yellow]")
        result = subprocess.run(
            ["alembic", "upgrade", revision],
            capture_output=True,
            text=True
        )

        if result.returncode == 0:
            console.print("[green]✓[/green] Database upgraded successfully!")
            if result.stdout:
                console.print(result.stdout)
        else:
            console.print(f"[red]✗[/red] Database upgrade failed:")
            console.print(result.stderr)
            raise typer.Exit(code=1)

    except Exception as e:
        console.print(f"[red]✗[/red] Failed to upgrade database: {e}")
        raise typer.Exit(code=1)


@db_app.command("status")
def db_status():
    """
    Show database migration status.

    Example:
        vmcp db status
    """
    try:
        import subprocess

        result = subprocess.run(
            ["alembic", "current"],
            capture_output=True,
            text=True
        )

        if result.returncode == 0:
            console.print("[bold]Database Migration Status:[/bold]")
            console.print(result.stdout)
        else:
            console.print(f"[red]✗[/red] Failed to get database status:")
            console.print(result.stderr)
            raise typer.Exit(code=1)

    except Exception as e:
        console.print(f"[red]✗[/red] Failed to get database status: {e}")
        raise typer.Exit(code=1)


# ============================================================================
# MCP Commands
# ============================================================================

mcp_app = typer.Typer(help="MCP server management commands")
app.add_typer(mcp_app, name="mcp")


@mcp_app.command("list")
def mcp_list():
    """
    List all installed MCP servers.

    Example:
        vmcp mcp list
    """
    try:
        from vmcp.backend.mcps.mcp_config_manager import MCPConfigManager

        config_manager = MCPConfigManager(user_id=1)
        servers = config_manager.list_servers()

        if not servers:
            console.print("[yellow]No MCP servers installed.[/yellow]")
            return

        table = Table(title="Installed MCP Servers", show_header=True, header_style="bold cyan")
        table.add_column("Name", style="cyan")
        table.add_column("Type", style="green")
        table.add_column("Status", style="yellow")
        table.add_column("Auto-Connect", style="magenta")
        table.add_column("Tools", justify="right", style="blue")

        for server in servers:
            table.add_row(
                server.name,
                server.transport_type.value,
                server.status.value,
                "✓" if server.auto_connect else "✗",
                str(len(server.tools or []))
            )

        console.print(table)

    except Exception as e:
        console.print(f"[red]✗[/red] Failed to list MCP servers: {e}")
        raise typer.Exit(code=1)


@mcp_app.command("load-registry")
def mcp_load_registry(
    force: bool = typer.Option(False, "--force", "-f", help="Force reload even if registry already populated")
):
    """
    Load preconfigured MCP servers into the global registry.

    This command reads the preconfigured-servers.json file and populates
    the global_mcp_server_registry table with publicly available MCP servers.

    Example:
        vmcp mcp load-registry
        vmcp mcp load-registry --force
    """
    try:
        from vmcp.backend.scripts.upload_preconfigured_servers import main as upload_main

        console.print(Panel.fit(
            "[bold cyan]Loading MCP Server Registry[/bold cyan]\n\n"
            "Populating database with preconfigured MCP servers...",
            title="MCP Registry",
            border_style="cyan"
        ))

        upload_main()

        console.print("\n[green]✓[/green] MCP server registry loaded successfully!")

    except Exception as e:
        console.print(f"[red]✗[/red] Failed to load MCP registry: {e}")
        import traceback
        traceback.print_exc()
        raise typer.Exit(code=1)


# ============================================================================
# vMCP Commands
# ============================================================================

vmcp_app = typer.Typer(help="Virtual MCP management commands")
app.add_typer(vmcp_app, name="vmcp")


@vmcp_app.command("list")
def vmcp_list():
    """
    List all vMCPs.

    Example:
        vmcp vmcp list
    """
    try:
        from vmcp.backend.vmcps.config_manager import VMCPConfigManager

        manager = VMCPConfigManager(user_id=1)
        vmcps = manager.list_available_vmcps()

        if not vmcps:
            console.print("[yellow]No vMCPs configured.[/yellow]")
            return

        table = Table(title="Configured vMCPs", show_header=True, header_style="bold cyan")
        table.add_column("Name", style="cyan")
        table.add_column("Description", style="white")
        table.add_column("Tools", justify="right", style="blue")
        table.add_column("Resources", justify="right", style="green")
        table.add_column("Prompts", justify="right", style="yellow")

        for vmcp in vmcps:
            table.add_row(
                vmcp.get("name", ""),
                vmcp.get("description", "")[:50] + "..." if len(vmcp.get("description", "")) > 50 else vmcp.get("description", ""),
                str(vmcp.get("total_tools", 0)),
                str(vmcp.get("total_resources", 0)),
                str(vmcp.get("total_prompts", 0))
            )

        console.print(table)

    except Exception as e:
        console.print(f"[red]✗[/red] Failed to list vMCPs: {e}")
        raise typer.Exit(code=1)


@vmcp_app.command("info")
def vmcp_info(
    vmcp_id: str = typer.Argument(..., help="vMCP ID")
):
    """
    Show detailed information about a vMCP.

    Example:
        vmcp vmcp info my-vmcp-id
    """
    try:
        from vmcp.backend.vmcps.config_manager import VMCPConfigManager

        manager = VMCPConfigManager(user_id=1, vmcp_id=vmcp_id)
        config = manager.load_vmcp_config(vmcp_id)

        if not config:
            console.print(f"[red]✗[/red] vMCP not found: {vmcp_id}")
            raise typer.Exit(code=1)

        info_text = (
            f"[bold cyan]Name:[/bold cyan] {config.name}\n"
            f"[bold cyan]ID:[/bold cyan] {config.id}\n"
            f"[bold cyan]Description:[/bold cyan] {config.description or 'N/A'}\n\n"
            f"[bold]Capabilities:[/bold]\n"
            f"  Tools: {config.total_tools}\n"
            f"  Resources: {config.total_resources}\n"
            f"  Resource Templates: {config.total_resource_templates}\n"
            f"  Prompts: {config.total_prompts}\n\n"
            f"[bold]Custom:[/bold]\n"
            f"  Custom Tools: {len(config.custom_tools or [])}\n"
            f"  Custom Prompts: {len(config.custom_prompts or [])}\n"
            f"  Custom Resources: {len(config.custom_resources or [])}\n\n"
            f"[bold cyan]Created:[/bold cyan] {config.created_at}\n"
            f"[bold cyan]Updated:[/bold cyan] {config.updated_at}"
        )

        rprint(Panel.fit(info_text, title=f"vMCP: {config.name}", border_style="cyan"))

    except Exception as e:
        console.print(f"[red]✗[/red] Failed to get vMCP info: {e}")
        raise typer.Exit(code=1)


# ============================================================================
# Docs Commands
# ============================================================================

docs_app = typer.Typer(help="Documentation commands")
app.add_typer(docs_app, name="docs")


@docs_app.command("start")
def docs_start(
    port: int = typer.Option(3000, "--port", "-p", help="Port for documentation server"),
    open_browser: bool = typer.Option(True, "--open/--no-open", help="Open browser after starting")
):
    """
    Start the documentation server.

    Example:
        vmcp docs start
        vmcp docs start --port 3001
        vmcp docs start --no-open
    """
    import subprocess
    import os
    import time
    import webbrowser
    from pathlib import Path

    # Find the docs directory
    vmcp_root = Path(__file__).parent.parent.parent.parent
    docs_dir = vmcp_root / "docs"

    if not docs_dir.exists():
        console.print(f"[red]✗[/red] Documentation directory not found: {docs_dir}")
        console.print("\n[yellow]The documentation should be in the 'docs' directory at the project root.[/yellow]")
        raise typer.Exit(code=1)

    console.print(Panel.fit(
        "[bold cyan]Starting vMCP Documentation Server[/bold cyan]\n\n"
        f"Directory: [green]{docs_dir}[/green]\n"
        f"URL: [green]http://localhost:{port}[/green]",
        title="Documentation",
        border_style="cyan"
    ))

    # Check if node_modules exists
    if not (docs_dir / "node_modules").exists():
        console.print("\n[yellow]Installing documentation dependencies...[/yellow]")
        try:
            result = subprocess.run(
                ["npm", "install"],
                cwd=str(docs_dir),
                capture_output=True,
                text=True
            )
            if result.returncode == 0:
                console.print("[green]✓[/green] Dependencies installed!")
            else:
                console.print(f"[red]✗[/red] Failed to install dependencies:")
                console.print(result.stderr)
                raise typer.Exit(code=1)
        except FileNotFoundError:
            console.print("[red]✗[/red] npm not found. Please install Node.js first.")
            raise typer.Exit(code=1)

    # Open browser after server is ready
    if open_browser:
        def open_browser_when_ready():
            import httpx
            max_attempts = 30  # 15 seconds total
            for attempt in range(max_attempts):
                try:
                    response = httpx.get(f"http://localhost:{port}", timeout=1.0)
                    if response.status_code == 200:
                        time.sleep(0.3)  # Small buffer
                        webbrowser.open(f"http://localhost:{port}")
                        return
                except (httpx.ConnectError, httpx.TimeoutException):
                    pass
                time.sleep(0.5)
            # Fallback: open anyway after timeout
            webbrowser.open(f"http://localhost:{port}")

        import threading
        browser_thread = threading.Thread(target=open_browser_when_ready, daemon=True)
        browser_thread.start()

    console.print("\n[green]✓[/green] Starting documentation server...\n")
    console.print("[dim]Press Ctrl+C to stop[/dim]\n")

    # Start the docs server
    try:
        env = os.environ.copy()
        env["PORT"] = str(port)
        subprocess.run(
            ["npm", "start"],
            cwd=str(docs_dir),
            env=env
        )
    except KeyboardInterrupt:
        console.print("\n\n[yellow]Shutting down documentation server...[/yellow]")
        console.print("[green]✓[/green] Goodbye!")


@docs_app.command("build")
def docs_build():
    """
    Build the documentation for production.

    Example:
        vmcp docs build
    """
    import subprocess
    from pathlib import Path

    vmcp_root = Path(__file__).parent.parent.parent.parent
    docs_dir = vmcp_root / "docs"

    if not docs_dir.exists():
        console.print(f"[red]✗[/red] Documentation directory not found: {docs_dir}")
        raise typer.Exit(code=1)

    console.print("[yellow]Building documentation...[/yellow]")

    try:
        result = subprocess.run(
            ["npm", "run", "build"],
            cwd=str(docs_dir),
            capture_output=True,
            text=True
        )

        if result.returncode == 0:
            console.print("[green]✓[/green] Documentation built successfully!")
            console.print(f"\nOutput directory: [cyan]{docs_dir / 'build'}[/cyan]")
        else:
            console.print("[red]✗[/red] Build failed:")
            console.print(result.stderr)
            raise typer.Exit(code=1)

    except FileNotFoundError:
        console.print("[red]✗[/red] npm not found. Please install Node.js first.")
        raise typer.Exit(code=1)


# ============================================================================
# Config Commands
# ============================================================================

config_app = typer.Typer(help="Configuration management commands")
app.add_typer(config_app, name="config")


@config_app.command("show")
def config_show():
    """
    Show current configuration.

    Example:
        vmcp config show
    """
    try:
        from vmcp.backend.config import settings

        config_text = (
            f"[bold]Environment:[/bold] {settings.ENV}\n\n"
            f"[bold]Server:[/bold]\n"
            f"  Host: {settings.HOST}\n"
            f"  Port: {settings.PORT}\n\n"
            f"[bold]Database:[/bold]\n"
            f"  Type: PostgreSQL\n"
            f"  Host: {settings.DATABASE_HOST}\n"
            f"  Port: {settings.DATABASE_PORT}\n"
            f"  Name: {settings.DATABASE_NAME}\n\n"
            f"[bold]Logging:[/bold]\n"
            f"  Level: {settings.LOG_LEVEL}\n"
            f"  Format: {settings.LOG_FORMAT}"
        )

        rprint(Panel.fit(config_text, title="Configuration", border_style="cyan"))

    except Exception as e:
        console.print(f"[red]✗[/red] Failed to show configuration: {e}")
        raise typer.Exit(code=1)


@config_app.command("validate")
def config_validate():
    """
    Validate configuration.

    Example:
        vmcp config validate
    """
    try:
        from vmcp.backend.config import settings

        console.print("[yellow]Validating configuration...[/yellow]")

        # Check required settings
        errors = []

        if not settings.DATABASE_URL:
            errors.append("DATABASE_URL is not set")

        if not settings.SECRET_KEY or settings.SECRET_KEY == "your-secret-key-here":
            errors.append("SECRET_KEY is not properly configured")

        if errors:
            console.print(f"[red]✗[/red] Configuration validation failed:")
            for error in errors:
                console.print(f"  • {error}")
            raise typer.Exit(code=1)
        else:
            console.print("[green]✓[/green] Configuration is valid!")

    except Exception as e:
        console.print(f"[red]✗[/red] Failed to validate configuration: {e}")
        raise typer.Exit(code=1)


# ============================================================================
# Main Entry Point
# ============================================================================

def main():
    """Main CLI entry point."""
    app()


if __name__ == "__main__":
    main()
