import argparse
import json
import logging
import os
import sys
import time
from getpass import getpass
from pathlib import Path
from typing import Optional

logger = logging.getLogger(__name__)
import socket

from kvict.keychain import delete_api_key, get_api_key, store_api_key
from kvict import __version__


def _validate_api_key(api_key: str, provider: str) -> tuple[bool, str]:
    """Validate API key format based on provider."""
    if not api_key or len(api_key) < 20:
        return False, "API key is too short"

    if provider == "openai":
        if not api_key.startswith("sk-"):
            return False, "OpenAI keys should start with 'sk-'"
    elif provider == "anthropic":
        if not api_key.startswith("sk-ant-"):
            return False, "Anthropic keys should start with 'sk-ant-'"
    # vllm and generic providers don't have specific key formats

    return True, ""


def _check_port_available(port: int, host: str = "127.0.0.1") -> bool:
    """Check if a port is available for binding."""
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        try:
            s.bind((host, port))
            return True
        except OSError:
            return False


def _mask_api_key(api_key: str) -> str:
    """Mask API key for display, showing only first 7 and last 4 chars."""
    if not api_key or len(api_key) < 15:
        return "***"
    return f"{api_key[:7]}...{api_key[-4:]}"


def _print_startup_banner(port: int, provider: str, api_key: str | None, dashboard_port: int | None = None):
    """Print a professional startup banner."""
    from kvict import __version__

    print()
    print("=" * 50)
    print(f"  kvict Proxy v{__version__}")
    print("=" * 50)
    print(f"  Proxy:     http://localhost:{port}")
    print(f"  Provider:  {provider}")
    if api_key:
        print(f"  API Key:   {_mask_api_key(api_key)} (from keychain)")
    else:
        print("  API Key:   Not configured")
    if dashboard_port:
        print(f"  Dashboard: http://localhost:{dashboard_port}")
    print("=" * 50)
    print("  Press Ctrl+C to stop")
    print()

# repo root: packages/kvict/cli.py -> packages/kvict -> packages -> repo
ROOT_DIR = Path(__file__).resolve().parents[3]
DEFAULT_CONFIG = ROOT_DIR / "infra" / "deployment" / "phase1_production_config.json"
DEFAULT_RESULTS_DIR = ROOT_DIR / "data"


def _advisor_requirements_message(extra: str) -> str:
    return (
        f"{extra} support is not installed. "
        "Reinstall with: pip install kvict[advisor]"
    )


def _proxy_requirements_message(extra: str) -> str:
    return (
        f"{extra} support is not installed. "
        "Reinstall with: pip install kvict[proxy]"
    )


def advisor_serve(args: argparse.Namespace) -> int:
    try:
        import uvicorn  # type: ignore
        from caae.caae_service import app  # type: ignore
    except ImportError as exc:  # pragma: no cover - handled at runtime
        print(_advisor_requirements_message("Advisor service"))
        print(f"Missing import: {exc}")
        return 1

    uvicorn.run(
        app,
        host=args.host,
        port=args.port,
        workers=args.workers,
        reload=args.reload,
        log_level=args.log_level.lower(),
    )
    return 0


def advisor_health(args: argparse.Namespace) -> int:
    try:
        import httpx
    except ImportError as exc:  # pragma: no cover - handled at runtime
        print(_advisor_requirements_message("Health probe"))
        print(f"Missing import: {exc}")
        return 1

    url = args.url or f"http://{args.host}:{args.port}/v1/health"
    try:
        response = httpx.get(url, timeout=args.timeout)
    except httpx.HTTPError as exc:
        print(f"[FAIL] Request error: {exc}")
        return 1

    status_ok = response.status_code == 200
    print(f"[{response.status_code}] {url}")
    try:
        payload = response.json()
        print(json.dumps(payload, indent=2))
    except Exception:  # noqa: BLE001
        print(response.text)

    return 0 if status_ok else 1


def advisor_metrics(args: argparse.Namespace) -> int:
    try:
        import httpx
    except ImportError as exc:  # pragma: no cover - handled at runtime
        print(_advisor_requirements_message("Metrics"))
        print(f"Missing import: {exc}")
        return 1

    url = args.url or f"http://{args.host}:{args.port}/metrics"
    iterations = 0

    while True:
        iterations += 1
        try:
            response = httpx.get(url, timeout=args.timeout)
            print(response.text)
            if response.status_code == 200:
                status = 0
            else:
                status = 1
        except httpx.HTTPError as exc:
            print(f"[FAIL] Request error: {exc}")
            status = 1

        if not args.follow or iterations >= args.max_iterations:
            return status

        time.sleep(args.interval)


def deploy_phase1(args: argparse.Namespace) -> int:
    try:
        from infra.deployment.phase1_deploy import Phase1Deployment  # type: ignore
    except ImportError as exc:  # pragma: no cover - handled at runtime
        print("Deployment modules are unavailable. Is the repository present?")
        print(f"Missing import: {exc}")
        return 1

    config_path = Path(args.config)
    deployment = Phase1Deployment(config_path)

    if not deployment.validate_environment():
        print("[FAIL] Environment validation failed.")
        return 1

    if args.status:
        status = deployment.get_status()
        print(json.dumps(status, indent=2))
        return 0

    if args.rollback:
        experiment_id = None if args.rollback == "all" else args.rollback
        success = deployment.rollback(experiment_id)
        return 0 if success else 1

    if args.stage:
        success = deployment.deploy_stage(args.stage, args.auto_approve)
        return 0 if success else 1

    print("No stage provided. Use --stage to run a rollout stage.")
    return 1


def validate_phase1(args: argparse.Namespace) -> int:
    try:
        from infra.deployment.phase1_validate import Phase1Validator  # type: ignore
    except ImportError as exc:  # pragma: no cover - handled at runtime
        print("Validation modules are unavailable. Is the repository present?")
        print(f"Missing import: {exc}")
        return 1

    validator = Phase1Validator(Path(args.config), Path(args.results_dir))

    config_valid, config_issues = validator.validate_configuration()
    if not config_valid:
        print("[FAIL] Configuration validation failed:")
        for issue in config_issues:
            print(f" - {issue}")
        return 1

    if args.config_only:
        print("[PASS] Configuration validation passed")
        return 0

    if args.experiment:
        valid, issues = validator.validate_experiment(args.experiment)
        if valid:
            print(f"[PASS] Experiment {args.experiment} validation passed")
            return 0

        print(f"[FAIL] Experiment {args.experiment} validation failed:")
        for issue in issues:
            print(f" - {issue}")
        return 1

    all_valid, issues = validator.validate_all()
    if all_valid:
        print("[PASS] All experiments validated")
        return 0

    print("[FAIL] One or more experiments failed validation:")
    for exp, exp_issues in issues.items():
        if exp_issues:
            print(f"- {exp}:")
            for issue in exp_issues:
                print(f"   - {issue}")
    return 1


def proxy_serve(args: argparse.Namespace) -> int:
    """Run the kvict Proxy service."""
    try:
        import uvicorn  # type: ignore
        from proxy.app import app  # type: ignore
        from proxy.config import set_config, ProxyConfig  # type: ignore
    except ImportError as exc:  # pragma: no cover - handled at runtime
        print(_proxy_requirements_message("Proxy service"))
        print(f"Missing import: {exc}")
        return 1

    # Load user config if exists (for consumer use)
    config_path = Path.home() / ".kvict" / "config.json"
    user_config = {}
    if config_path.exists():
        try:
            with open(config_path) as f:
                user_config = json.load(f)
        except Exception as e:
            logger.warning(f"Failed to load user config: {e}")

    # Use command-line args if provided, otherwise use config, otherwise defaults
    host = (
        args.host
        if args.host != "0.0.0.0" or "host" not in user_config
        else user_config.get("host", "0.0.0.0")
    )
    port = (
        args.port
        if args.port != 8080 or "port" not in user_config
        else user_config.get("port", 8080)
    )
    provider = (
        args.provider
        if args.provider != "openai" or "provider" not in user_config
        else user_config.get("provider", "openai")
    )

    # Get API key from keychain (for consumer mode)
    # Note: The proxy itself doesn't store keys, but we can set env var for convenience
    api_key = get_api_key("kvict")
    if api_key:
        # Set as environment variable (proxy will use it if needed)
        # But note: proxy expects keys in Authorization header from clients
        # This is just for reference - actual keys come from request headers
        os.environ["OPENAI_API_KEY"] = api_key

    # Check if port is available
    if not _check_port_available(port, "127.0.0.1" if host == "0.0.0.0" else host):
        print(f"[ERROR] Port {port} is already in use.")
        print(f"  Try: kvict proxy serve --port {port + 1}")
        return 1

    os.environ["PROXY_HOST"] = host
    os.environ["PROXY_PORT"] = str(port)
    os.environ["PROXY_PROVIDER"] = provider
    if args.provider_url:
        os.environ["PROXY_PROVIDER_BASE_URL"] = args.provider_url
    elif user_config.get("provider_url"):
        os.environ["PROXY_PROVIDER_BASE_URL"] = user_config["provider_url"]

    # Get dashboard port for banner
    dashboard_port = user_config.get("dashboard_port") if user_config.get("dashboard_enabled") else None

    _print_startup_banner(port, provider, api_key, dashboard_port)

    uvicorn.run(
        app,
        host=host,
        port=port,
        workers=args.workers,
        reload=args.reload,
        log_level=args.log_level.lower(),
    )
    return 0


def proxy_health(args: argparse.Namespace) -> int:
    """Probe proxy health endpoint."""
    try:
        import httpx
    except ImportError as exc:  # pragma: no cover - handled at runtime
        print(_proxy_requirements_message("Health probe"))
        print(f"Missing import: {exc}")
        return 1

    url = args.url or f"http://{args.host}:{args.port}/health"
    try:
        response = httpx.get(url, timeout=args.timeout)
    except httpx.HTTPError as exc:
        print(f"[FAIL] Request error: {exc}")
        return 1

    print(f"[{response.status_code}] {url}")
    try:
        print(json.dumps(response.json(), indent=2))
    except Exception:  # noqa: BLE001
        print(response.text)
    return 0 if response.status_code == 200 else 1


def proxy_stats(args: argparse.Namespace) -> int:
    """Fetch proxy savings summary."""
    try:
        import httpx
    except ImportError as exc:  # pragma: no cover - handled at runtime
        print(_proxy_requirements_message("Stats"))
        print(f"Missing import: {exc}")
        return 1

    url = args.url or f"http://{args.host}:{args.port}/api/v1/savings/summary"
    try:
        response = httpx.get(url, timeout=args.timeout)
    except httpx.HTTPError as exc:
        print(f"[FAIL] Request error: {exc}")
        return 1

    if response.status_code == 200:
        data = response.json()
        print(f"Tokens Saved:     {data.get('tokens_saved_total', 0):,}")
        print(f"Messages Gained:  {data.get('messages_gained', 0):,}")
        print(f"Dollars Saved:    ${data.get('dollars_saved', 0.0):.4f}")
        print(f"Cache Hit Rate:   {data.get('cache_hit_rate', 0.0):.1%}")
        print(f"Total Requests:   {data.get('total_requests', 0):,}")
    else:
        print(f"[{response.status_code}] {response.text}")

    return 0 if response.status_code == 200 else 1


def proxy_setup(args: argparse.Namespace) -> int:
    """Interactive setup wizard for consumer proxy."""
    print()
    print("kvict Setup Wizard")
    print("=" * 50)

    # Step 1: Choose provider FIRST (so we can validate key format)
    print("\n1. Choose your AI provider:")
    print("   [1] OpenAI (default)")
    print("   [2] Anthropic")
    print("   [3] vLLM (self-hosted)")
    print("   [4] Other (generic)")
    provider_choice = input("   Choice [1]: ").strip() or "1"
    provider_map = {"1": "openai", "2": "anthropic", "3": "vllm", "4": "generic"}
    selected_provider = provider_map.get(provider_choice, "openai")

    # Step 2: Get API key with provider-specific validation
    print(f"\n2. Enter your {selected_provider.title()} API key:")
    print("   (Your key is stored securely in your OS keychain)")
    api_key = getpass("   API Key: ").strip()

    valid, error_msg = _validate_api_key(api_key, selected_provider)
    if not valid:
        print(f"[ERROR] {error_msg}")
        return 1

    # Store in keyring
    if store_api_key("kvict", api_key):
        print(f"[OK] API key stored securely ({_mask_api_key(api_key)})")
    else:
        print("[ERROR] Failed to store key securely. Check keychain access.")
        return 1

    # Step 2b: Provider URL (for vLLM and generic)
    provider_url = ""
    if selected_provider in ("vllm", "generic"):
        print(f"\n2b. Enter your {selected_provider} server URL:")
        print("    Example: http://localhost:8000/v1")
        provider_url = input("    URL: ").strip()
        if not provider_url:
            print("[ERROR] Server URL is required for this provider.")
            return 1

    # Step 3: Choose port
    print("\n3. Choose proxy port:")
    port_input = input("   Port [8080]: ").strip() or "8080"
    try:
        port = int(port_input)
        if port < 1024 or port > 65535:
            print("[ERROR] Port must be between 1024 and 65535. Use a port like 8080.")
            return 1
    except ValueError:
        print("[ERROR] Invalid port number. Enter a number like 8080.")
        return 1

    # Check if port is available
    if not _check_port_available(port):
        print(f"[WARN] Port {port} appears to be in use. You may need to choose another.")

    # Step 4: Enable dashboard
    print("\n4. Enable local dashboard?")
    dashboard_input = input("   Enable dashboard [Y/n]: ").strip().lower()
    enable_dashboard = dashboard_input != "n"
    dashboard_port = 3000 if enable_dashboard else None

    # Save config
    config_dir = Path.home() / ".kvict"
    config_dir.mkdir(exist_ok=True)
    config_file = config_dir / "config.json"

    config = {
        "provider": selected_provider,
        "port": port,
        "dashboard_enabled": enable_dashboard,
        "dashboard_port": dashboard_port,
    }
    if provider_url:
        config["provider_url"] = provider_url

    with open(config_file, "w") as f:
        json.dump(config, f, indent=2)

    print(f"\n[OK] Configuration saved to {config_file}")
    print("\nSetup complete!")
    print("\nNext steps:")
    print(f"  1. Start proxy:    kvict proxy serve")
    if enable_dashboard:
        print(f"  2. Open dashboard: http://localhost:{dashboard_port}")
    print(f"  3. Point apps to:  http://localhost:{port}")
    print("\nOr run 'kvict proxy quickstart' to start everything now.")

    return 0


def proxy_dashboard(args: argparse.Namespace) -> int:
    """Start local dashboard server."""
    try:
        import uvicorn
    except ImportError:
        print("Dashboard requires: pip install kvict[proxy]")
        return 1

    # Try to import dashboard app
    try:
        from proxy.dashboard_server import create_dashboard_app
    except ImportError:
        # Create simple dashboard if module doesn't exist
        from fastapi import FastAPI
        from fastapi.responses import HTMLResponse
        import httpx

        app = FastAPI(title="kvict Dashboard")

        @app.get("/", response_class=HTMLResponse)
        async def dashboard():
            return _get_dashboard_html()

        # Get proxy port from config or args
        config_path = Path.home() / ".kvict" / "config.json"
        proxy_port = args.proxy_port or 8080
        if config_path.exists():
            with open(config_path) as f:
                config = json.load(f)
                proxy_port = args.proxy_port or config.get("port", 8080)

        @app.get("/api/stats")
        async def get_stats():
            # Try to get stats from proxy
            try:
                response = httpx.get(
                    f"http://127.0.0.1:{proxy_port}/api/v1/savings/summary", timeout=2.0
                )
                if response.status_code == 200:
                    data = response.json()
                    return {
                        "dollars_saved": data.get("dollars_saved", 0.0),
                        "requests_optimized": data.get("total_requests", 0),
                        "cache_hit_rate": data.get("cache_hit_rate", 0.0),
                        "tokens_saved": data.get("tokens_saved_total", 0),
                    }
            except Exception as e:
                # Proxy not running or unreachable - return defaults
                logger.debug(f"Could not fetch stats from proxy: {e}")

            return {
                "dollars_saved": 0.0,
                "requests_optimized": 0,
                "cache_hit_rate": 0.0,
                "tokens_saved": 0,
            }

    else:
        app = create_dashboard_app()

    config_path = Path.home() / ".kvict" / "config.json"
    proxy_port = 8080
    if config_path.exists():
        with open(config_path) as f:
            config = json.load(f)
        dashboard_port = args.port or config.get("dashboard_port", 3000)
        proxy_port = config.get("port", 8080)
    else:
        dashboard_port = args.port or 3000

    # Override proxy port if provided via CLI
    if args.proxy_port:
        proxy_port = args.proxy_port

    print(f"Dashboard starting on http://localhost:{dashboard_port}")
    print("   Press Ctrl+C to stop")
    print(f"   Proxy should be running on port {proxy_port}")

    uvicorn.run(app, host="127.0.0.1", port=dashboard_port, log_level="info")
    return 0


def proxy_config(args: argparse.Namespace) -> int:
    """Show current kvict configuration."""
    config_path = Path.home() / ".kvict" / "config.json"
    api_key = get_api_key("kvict")

    print()
    print("kvict Configuration")
    print("=" * 50)

    if config_path.exists():
        print(f"  Config file: {config_path}")
        try:
            with open(config_path) as f:
                config = json.load(f)

            provider = config.get("provider", "openai")
            port = config.get("port", 8080)
            dashboard_enabled = config.get("dashboard_enabled", False)
            dashboard_port = config.get("dashboard_port", 3000)
            provider_url = config.get("provider_url", "")

            print(f"  Provider:    {provider}")
            if provider_url:
                print(f"  Provider URL:{provider_url}")
            print(f"  Proxy port:  {port}")
            if dashboard_enabled:
                print(f"  Dashboard:   enabled (port {dashboard_port})")
            else:
                print("  Dashboard:   disabled")
        except Exception as e:
            print(f"  [ERROR] Failed to read config: {e}")
    else:
        print(f"  Config file: Not found")
        print("  Run 'kvict proxy setup' to configure")

    if api_key:
        print(f"  API key:     {_mask_api_key(api_key)} (stored in keychain)")
    else:
        print("  API key:     Not configured")

    print()
    return 0


def proxy_quickstart(args: argparse.Namespace) -> int:
    """One-command quick start for new users - actually starts the proxy."""
    print()
    print("kvict Quick Start")
    print("=" * 50)

    # Check if already configured
    config_path = Path.home() / ".kvict" / "config.json"
    api_key = get_api_key("kvict")

    if not config_path.exists() or not api_key:
        print("\nFirst time setup required...")
        setup_result = proxy_setup(argparse.Namespace())
        if setup_result != 0:
            return setup_result
        # Reload config and key
        with open(config_path) as f:
            config = json.load(f)
        api_key = get_api_key("kvict")
    else:
        with open(config_path) as f:
            config = json.load(f)
        print(f"\n[OK] Using existing configuration from {config_path}")

    if not api_key:
        print("[ERROR] No API key found. Please run: kvict proxy setup")
        return 1

    port = config.get("port", 8080)
    provider = config.get("provider", "openai")
    provider_url = config.get("provider_url", "")

    # Actually start the proxy!
    print("\nStarting kvict Proxy...")

    # Create args namespace for proxy_serve
    serve_args = argparse.Namespace(
        host="0.0.0.0",
        port=port,
        provider=provider,
        provider_url=provider_url,
        workers=1,
        reload=False,
        log_level="info",
    )

    return proxy_serve(serve_args)


def _get_dashboard_html() -> str:
    """Get HTML for simple dashboard."""
    return """<!DOCTYPE html>
<html>
<head>
    <title>kvict Dashboard</title>
    <meta charset="utf-8">
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: #333;
            padding: 2rem;
            min-height: 100vh;
        }
        .container {
            max-width: 1200px;
            margin: 0 auto;
        }
        h1 {
            color: white;
            font-size: 2.5rem;
            margin-bottom: 2rem;
            text-align: center;
        }
        .stats-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
            gap: 1.5rem;
            margin-bottom: 2rem;
        }
        .stat-card {
            background: white;
            padding: 2rem;
            border-radius: 12px;
            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
            text-align: center;
        }
        .stat-label {
            font-size: 0.9rem;
            color: #666;
            text-transform: uppercase;
            letter-spacing: 1px;
            margin-bottom: 0.5rem;
        }
        .stat-value {
            font-size: 2.5rem;
            font-weight: bold;
            color: #667eea;
        }
        .stat-value.green { color: #10b981; }
        .loading {
            text-align: center;
            color: white;
            font-size: 1.2rem;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>kvict Dashboard</h1>
        <div class="stats-grid">
            <div class="stat-card">
                <div class="stat-label">Saved Today</div>
                <div class="stat-value green" id="saved">$0.00</div>
            </div>
            <div class="stat-card">
                <div class="stat-label">Requests Optimized</div>
                <div class="stat-value" id="requests">0</div>
            </div>
            <div class="stat-card">
                <div class="stat-label">Cache Hit Rate</div>
                <div class="stat-value" id="hitrate">0%</div>
            </div>
            <div class="stat-card">
                <div class="stat-label">Tokens Saved</div>
                <div class="stat-value" id="tokens">0</div>
            </div>
        </div>
    </div>
    <script>
        async function updateStats() {
            try {
                const res = await fetch('/api/stats');
                const data = await res.json();
                document.getElementById('saved').textContent = '$' + data.dollars_saved.toFixed(2);
                document.getElementById('requests').textContent = data.requests_optimized.toLocaleString();
                document.getElementById('hitrate').textContent = data.cache_hit_rate.toFixed(1) + '%';
                document.getElementById('tokens').textContent = data.tokens_saved.toLocaleString();
            } catch (error) {
                console.error('Failed to fetch stats:', error);
            }
        }
        updateStats();
        setInterval(updateStats, 2000);
    </script>
</body>
</html>"""



def vllm_setup(args: argparse.Namespace) -> int:
    """Interactive vLLM integration setup with auto-detection."""
    print()
    print("=" * 60)
    print("  kvict vLLM Integration Setup")
    print("=" * 60)
    print()

    # Import required modules
    try:
        from caae.config import detect_gpu_memory_gb, detect_pcie_bandwidth_gbps
        import yaml
    except ImportError as exc:
        print(f"Error: Missing dependencies: {exc}")
        print("Install with: pip install kvict[vllm]")
        return 1

    # Auto-detect hardware
    print("🔍 Auto-detecting hardware specifications...")
    print()

    gpu_mem_gb = detect_gpu_memory_gb()
    pcie_bw_gbps = detect_pcie_bandwidth_gbps()

    if gpu_mem_gb:
        print(f"✓ GPU Memory: {gpu_mem_gb:.1f} GB")
    else:
        print("⚠ GPU Memory: Not detected (will use default: 80 GB)")
        gpu_mem_gb = 80.0

    if pcie_bw_gbps:
        print(f"✓ PCIe Bandwidth: {pcie_bw_gbps:.1f} GB/s")
    else:
        print("⚠ PCIe Bandwidth: Not detected (will use default: 16 GB/s)")
        pcie_bw_gbps = 16.0

    print()

    # Interactive prompts (unless non-interactive)
    if not args.non_interactive:
        print("Configuration Options (press Enter for defaults):")
        print()

        # GPU cost
        gpu_cost = input(f"GPU rental cost per hour [1.5 USD]: ").strip()
        gpu_cost = float(gpu_cost) if gpu_cost else 1.5

        # SLA targets
        p95_target = input("Target P95 latency [300 ms]: ").strip()
        p95_target = float(p95_target) if p95_target else 300.0

        p99_target = input("Target P99 latency [500 ms]: ").strip()
        p99_target = float(p99_target) if p99_target else 500.0

        # Enable optimizations
        adaptive_sla = input("Enable adaptive SLA optimization? [y/N]: ").strip().lower()
        adaptive_sla = adaptive_sla == "y"

        multi_gpu = input("Enable multi-GPU coordination? (2+ GPUs required) [y/N]: ").strip().lower()
        multi_gpu = multi_gpu == "y"
    else:
        gpu_cost = 1.5
        p95_target = 300.0
        p99_target = 500.0
        adaptive_sla = True
        multi_gpu = False

    # Create config
    config = {
        "optimizations": {
            "multi_gpu_coordination": multi_gpu,
            "speculative_decoding": False,  # Conservative default
            "adaptive_sla": adaptive_sla,
            "shared_kv_pooling": False,  # Conservative default
        },
        "sla": {
            "target_p95_ms": p95_target,
            "target_p99_ms": p99_target,
            "strict_mode": False,
        },
        "cost_model": {
            "gpu_cost_per_hour": gpu_cost,
            "gpu_memory_gb": gpu_mem_gb,
            "pcie_bandwidth_gbps": pcie_bw_gbps,
        },
        "performance": {
            "prediction_accuracy_target": 0.71,
            "circuit_breaker_timeout_ms": 5.0,
        },
        "telemetry": {
            "enable": True,
            "sample_rate": 0.1,
            "export_interval_sec": 60,
        },
        "fallback": {
            "on_error": True,
            "strict_mode": False,
        },
        "advisor": {
            "url": None,
            "timeout_ms": 5.0,
            "check_on_startup": True,
        },
    }

    # Determine config path
    if args.config_path:
        config_path = Path(args.config_path)
    else:
        config_path = Path.home() / ".kvict" / "caae_config.yaml"

    # Create directory if needed
    config_path.parent.mkdir(parents=True, exist_ok=True)

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

    print()
    print("=" * 60)
    print(f"✓ Configuration saved to: {config_path}")
    print("=" * 60)
    print()
    print("Quick Start Code:")
    print()
    print("    from caae.vllm_plugin import create_optimized_llm")
    print()
    print("    llm = create_optimized_llm('meta-llama/Llama-2-70b-hf')")
    print("    outputs = llm.generate(['Hello!'], max_tokens=50)")
    print()
    print("Next Steps:")
    print(f"  1. Run: kvict vllm verify")
    print("  2. See docs: docs/guides/vllm-quickstart.md")
    print()

    return 0


def vllm_verify(args: argparse.Namespace) -> int:
    """Verify vLLM integration health."""
    print()
    print("=" * 60)
    print("  kvict vLLM Integration Health Check")
    print("=" * 60)
    print()

    passed_checks = 0
    total_checks = 0

    # Check 1: vLLM installation
    total_checks += 1
    try:
        import vllm
        vllm_version = vllm.__version__
        print(f"✓ vLLM installed: {vllm_version}")
        passed_checks += 1
    except ImportError:
        print("✗ vLLM not installed")
        print("  Install with: pip install vllm>=0.6.0")
        return 1

    # Check 2: vLLM version compatibility
    total_checks += 1
    try:
        major, minor = map(int, vllm_version.split(".")[:2])
        if (major == 0 and minor >= 6) or major >= 1:
            print(f"✓ vLLM version compatible: {vllm_version}")
            passed_checks += 1
        else:
            print(f"⚠ vLLM version {vllm_version} untested (known compatible: 0.6.0+)")
    except Exception:
        print(f"⚠ Could not parse vLLM version: {vllm_version}")

    # Check 3: CUDA availability
    total_checks += 1
    try:
        import torch
        if torch.cuda.is_available():
            device_name = torch.cuda.get_device_properties(0).name
            print(f"✓ CUDA available: {device_name}")
            passed_checks += 1
        else:
            print("✗ CUDA not available")
            print("  vLLM requires CUDA for GPU acceleration")
    except ImportError:
        print("✗ PyTorch not installed")
        return 1

    # Check 4: NVML for auto-detection
    total_checks += 1
    try:
        import pynvml
        pynvml.nvmlInit()
        handle = pynvml.nvmlDeviceGetHandleByIndex(0)
        driver_version = pynvml.nvmlSystemGetDriverVersion()
        pynvml.nvmlShutdown()
        print(f"✓ NVML available: Driver {driver_version}")
        passed_checks += 1
    except Exception as e:
        print(f"⚠ NVML not available: {e}")
        print("  Install with: pip install nvidia-ml-py3")

    # Check 5: Config file
    total_checks += 1
    try:
        from caae.vllm_plugin import CAAAEConfig
        config = CAAAEConfig.from_file(args.config_path)
        print(f"✓ Config loaded: {config.gpu_memory_gb:.1f} GB GPU, {config.pcie_bandwidth_gbps:.1f} GB/s PCIe")
        passed_checks += 1
    except Exception as e:
        print(f"⚠ Config load failed: {e}")
        print("  Run: kvict vllm setup")

    # Check 6: Advisor connectivity (if configured)
    if hasattr(config, "advisor_url") and config.advisor_url:
        total_checks += 1
        try:
            import httpx
            response = httpx.get(f"{config.advisor_url}/health", timeout=2.0)
            if response.status_code == 200:
                print(f"✓ CAAE Advisor reachable: {config.advisor_url}")
                passed_checks += 1
            else:
                print(f"⚠ CAAE Advisor returned {response.status_code}")
        except Exception as e:
            print(f"⚠ CAAE Advisor unreachable: {e}")

    # Check 7: Minimal inference test (optional)
    if not args.skip_inference:
        total_checks += 1
        print()
        print("Running minimal inference test...")
        try:
            from caae.vllm_plugin import create_optimized_llm
            
            # Use a small model for testing
            llm = create_optimized_llm("facebook/opt-125m", max_model_len=512, gpu_memory_utilization=0.3)
            outputs = llm.generate(["Test"], max_tokens=5)
            
            if outputs and len(outputs) > 0:
                print("✓ Inference test passed")
                passed_checks += 1
            else:
                print("✗ Inference test failed: no outputs")
        except Exception as e:
            print(f"✗ Inference test failed: {e}")
            print("  This may be normal if you don't have a small model downloaded")

    # Summary
    print()
    print("=" * 60)
    print(f"Health Check: {passed_checks}/{total_checks} checks passed")
    print("=" * 60)
    print()

    if passed_checks == total_checks:
        print("✓ All checks passed! Your vLLM integration is ready to use.")
        return 0
    elif passed_checks >= total_checks - 2:
        print("⚠ Most checks passed. Review warnings above.")
        return 0
    else:
        print("✗ Several checks failed. Please review errors above.")
        return 1


def kube_manifest(args: argparse.Namespace) -> int:
    deployment_yaml = f"""apiVersion: apps/v1
kind: Deployment
metadata:
  name: {args.name}
  namespace: {args.namespace}
  labels:
    app: {args.name}
spec:
  replicas: {args.replicas}
  selector:
    matchLabels:
      app: {args.name}
  template:
    metadata:
      labels:
        app: {args.name}
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "8000"
        prometheus.io/path: "/metrics"
    spec:
      containers:
      - name: {args.name}
        image: {args.image}
        imagePullPolicy: IfNotPresent
        args: ["advisor", "serve", "--host=0.0.0.0", "--port=8000"]
        ports:
        - name: http
          containerPort: 8000
        env:
        - name: CAAE_HOST
          value: "0.0.0.0"
        - name: CAAE_PORT
          value: "8000"
        - name: LOG_LEVEL
          value: "{args.log_level}"
        readinessProbe:
          httpGet:
            path: /v1/health
            port: 8000
          initialDelaySeconds: 5
          periodSeconds: 10
        livenessProbe:
          httpGet:
            path: /v1/health
            port: 8000
          initialDelaySeconds: 10
          periodSeconds: 30
        securityContext:
          runAsNonRoot: true
          allowPrivilegeEscalation: false
---
apiVersion: v1
kind: Service
metadata:
  name: {args.name}
  namespace: {args.namespace}
  labels:
    app: {args.name}
spec:
  selector:
    app: {args.name}
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 8000
"""
    print(deployment_yaml.strip())
    return 0


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        prog="kvict",
        description="KV eviction advisor CLI (serve, validate, deploy, kube manifests).",
    )
    parser.add_argument(
        "--version",
        action="version",
        version=f"%(prog)s {__version__}",
    )

    subparsers = parser.add_subparsers(dest="command")

    # advisor group
    advisor = subparsers.add_parser("advisor", help="Run or query the advisor service")
    advisor_sub = advisor.add_subparsers(dest="advisor_command")

    serve = advisor_sub.add_parser("serve", help="Run the FastAPI advisor service")
    serve.add_argument("--host", default="0.0.0.0", help="Bind host")
    serve.add_argument("--port", type=int, default=8000, help="Bind port")
    serve.add_argument("--workers", type=int, default=1, help="Uvicorn workers")
    serve.add_argument(
        "--reload", action="store_true", default=False, help="Enable autoreload (dev)"
    )
    serve.add_argument(
        "--log-level",
        default="info",
        choices=["critical", "error", "warning", "info", "debug", "trace"],
        help="Log level for uvicorn",
    )
    serve.set_defaults(func=advisor_serve)

    health = advisor_sub.add_parser("health", help="Probe advisor health endpoint")
    health.add_argument("--url", help="Full health URL (overrides host/port)")
    health.add_argument("--host", default="127.0.0.1", help="Target host")
    health.add_argument("--port", type=int, default=8000, help="Target port")
    health.add_argument("--timeout", type=float, default=5.0, help="Request timeout")
    health.set_defaults(func=advisor_health)

    metrics = advisor_sub.add_parser("metrics", help="Fetch or tail Prometheus metrics")
    metrics.add_argument("--url", help="Full metrics URL (overrides host/port)")
    metrics.add_argument("--host", default="127.0.0.1", help="Target host")
    metrics.add_argument("--port", type=int, default=8000, help="Target port")
    metrics.add_argument("--timeout", type=float, default=5.0, help="Request timeout")
    metrics.add_argument(
        "--follow",
        action="store_true",
        default=False,
        help="Continuously fetch metrics",
    )
    metrics.add_argument(
        "--interval",
        type=float,
        default=5.0,
        help="Seconds between fetches when --follow",
    )
    metrics.add_argument(
        "--max-iterations",
        type=int,
        default=12,
        help="Maximum iterations when following metrics",
    )
    metrics.set_defaults(func=advisor_metrics)

    # deploy group
    deploy = subparsers.add_parser("deploy", help="Deployment workflows")
    deploy_sub = deploy.add_subparsers(dest="deploy_command")

    phase1 = deploy_sub.add_parser(
        "phase1",
        help="Execute Phase 1 staged rollout (infra/deployment/phase1_deploy.py)",
    )
    phase1.add_argument(
        "--config",
        default=str(DEFAULT_CONFIG),
        help="Path to deployment configuration JSON",
    )
    phase1.add_argument(
        "--stage",
        choices=[
            "staging_validation",
            "canary_deployment",
            "gradual_rollout_10pct",
            "gradual_rollout_25pct",
            "full_deployment",
        ],
        help="Deployment stage to execute",
    )
    phase1.add_argument(
        "--auto-approve",
        action="store_true",
        default=False,
        help="Skip confirmation prompts",
    )
    phase1.add_argument(
        "--rollback",
        nargs="?",
        const="all",
        help="Rollback (optionally provide an experiment id)",
    )
    phase1.add_argument(
        "--status", action="store_true", default=False, help="Show rollout status"
    )
    phase1.set_defaults(func=deploy_phase1)

    # validate group
    validate = subparsers.add_parser("validate", help="Validate readiness artifacts")
    validate.add_argument(
        "--config",
        default=str(DEFAULT_CONFIG),
        help="Path to deployment configuration JSON",
    )
    validate.add_argument(
        "--results-dir",
        default=str(DEFAULT_RESULTS_DIR),
        help="Path to experiment results directory",
    )
    validate.add_argument(
        "--experiment",
        help="Validate a single experiment id (e.g., experiment_7)",
    )
    validate.add_argument(
        "--config-only",
        action="store_true",
        default=False,
        help="Validate config only",
    )
    validate.set_defaults(func=validate_phase1)

    # proxy group
    proxy = subparsers.add_parser(
        "proxy", help="Run or query the subscription stretching proxy"
    )
    proxy_sub = proxy.add_subparsers(dest="proxy_command")

    proxy_serve_p = proxy_sub.add_parser("serve", help="Run the proxy service")
    proxy_serve_p.add_argument("--host", default="0.0.0.0", help="Bind host")
    proxy_serve_p.add_argument("--port", type=int, default=8080, help="Bind port")
    proxy_serve_p.add_argument(
        "--provider",
        default="openai",
        choices=["openai", "anthropic", "vllm", "generic"],
        help="LLM provider backend",
    )
    proxy_serve_p.add_argument(
        "--provider-url", default="", help="Custom base URL for the provider"
    )
    proxy_serve_p.add_argument("--workers", type=int, default=1, help="Uvicorn workers")
    proxy_serve_p.add_argument(
        "--reload", action="store_true", default=False, help="Enable autoreload (dev)"
    )
    proxy_serve_p.add_argument(
        "--log-level",
        default="info",
        choices=["critical", "error", "warning", "info", "debug", "trace"],
        help="Log level for uvicorn",
    )
    proxy_serve_p.set_defaults(func=proxy_serve)

    proxy_health_p = proxy_sub.add_parser("health", help="Probe proxy health endpoint")
    proxy_health_p.add_argument("--url", help="Full health URL (overrides host/port)")
    proxy_health_p.add_argument("--host", default="127.0.0.1", help="Target host")
    proxy_health_p.add_argument("--port", type=int, default=8080, help="Target port")
    proxy_health_p.add_argument(
        "--timeout", type=float, default=5.0, help="Request timeout"
    )
    proxy_health_p.set_defaults(func=proxy_health)

    proxy_stats_p = proxy_sub.add_parser("stats", help="Fetch proxy savings summary")
    proxy_stats_p.add_argument("--url", help="Full savings URL (overrides host/port)")
    proxy_stats_p.add_argument("--host", default="127.0.0.1", help="Target host")
    proxy_stats_p.add_argument("--port", type=int, default=8080, help="Target port")
    proxy_stats_p.add_argument(
        "--timeout", type=float, default=5.0, help="Request timeout"
    )
    proxy_stats_p.set_defaults(func=proxy_stats)

    proxy_setup_p = proxy_sub.add_parser("setup", help="Interactive setup wizard")
    proxy_setup_p.set_defaults(func=proxy_setup)

    proxy_dashboard_p = proxy_sub.add_parser("dashboard", help="Start local dashboard")
    proxy_dashboard_p.add_argument("--port", type=int, help="Dashboard port")
    proxy_dashboard_p.add_argument(
        "--proxy-port", type=int, default=8080, help="Proxy port (for stats)"
    )
    proxy_dashboard_p.set_defaults(func=proxy_dashboard)

    proxy_quickstart_p = proxy_sub.add_parser(
        "quickstart", help="One-command quick start (recommended for new users)"
    )
    proxy_quickstart_p.set_defaults(func=proxy_quickstart)

    proxy_config_p = proxy_sub.add_parser("config", help="Show current configuration")
    proxy_config_p.set_defaults(func=proxy_config)

    # kube group
    kube = subparsers.add_parser(
        "kube", help="Emit Kubernetes manifest for the advisor"
    )
    kube.add_argument("--name", default="kvict-advisor", help="Kubernetes app name")
    kube.add_argument(
        "--namespace", default="ml-inference", help="Kubernetes namespace"
    )
    kube.add_argument(
        "--image",
        default="ghcr.io/your-org/kvict:latest",
        help="Container image reference",
    )
    kube.add_argument("--replicas", type=int, default=2, help="Replica count")
    kube.add_argument(
        "--log-level",
        default="info",
        choices=["critical", "error", "warning", "info", "debug", "trace"],
        help="Log level for the advisor container",
    )
    kube.set_defaults(func=kube_manifest)

    # vllm group (integration tools)
    vllm = subparsers.add_parser("vllm", help="vLLM integration setup and verification")
    vllm_sub = vllm.add_subparsers(dest="vllm_command")

    vllm_setup_p = vllm_sub.add_parser(
        "setup", help="Interactive vLLM integration setup (auto-detects GPU)"
    )
    vllm_setup_p.add_argument(
        "--config-path",
        default=None,
        help="Path to save config file (default: ~/.kvict/caae_config.yaml)",
    )
    vllm_setup_p.add_argument(
        "--non-interactive",
        action="store_true",
        help="Non-interactive mode (use all defaults)",
    )
    vllm_setup_p.set_defaults(func=vllm_setup)

    vllm_verify_p = vllm_sub.add_parser(
        "verify", help="Verify vLLM integration health (checks dependencies, config, GPU)"
    )
    vllm_verify_p.add_argument(
        "--config-path", default=None, help="Path to config file to verify"
    )
    vllm_verify_p.add_argument(
        "--skip-inference",
        action="store_true",
        help="Skip minimal inference test",
    )
    vllm_verify_p.set_defaults(func=vllm_verify)

    return parser


def _print_proxy_help():
    """Print help specifically for proxy commands."""
    print()
    print("kvict Proxy Commands")
    print("=" * 50)
    print()
    print("  Getting Started:")
    print("    quickstart  One-command setup and start (recommended)")
    print("    setup       Interactive configuration wizard")
    print()
    print("  Running Services:")
    print("    serve       Start the proxy server")
    print("    dashboard   Start the dashboard UI")
    print()
    print("  Monitoring:")
    print("    health      Check proxy health status")
    print("    stats       Show savings summary")
    print("    config      Show current configuration")
    print()
    print("Examples:")
    print("  kvict proxy quickstart     # First-time setup + start")
    print("  kvict proxy serve          # Start proxy with saved config")
    print("  kvict proxy stats          # Check your savings")
    print()


def main(argv: Optional[list[str]] = None) -> int:
    parser = build_parser()
    args = parser.parse_args(argv)

    if not hasattr(args, "func"):
        # Check if user ran a command group without subcommand
        if hasattr(args, "command"):
            if args.command == "proxy":
                _print_proxy_help()
                return 0
            elif args.command == "advisor":
                print("\nAdvisor commands: serve, health, metrics")
                print("Example: kvict advisor serve --port 8000")
                return 0
            elif args.command == "deploy":
                print("\nDeploy commands: phase1")
                print("Example: kvict deploy phase1 --stage canary_deployment")
                return 0
        parser.print_help()
        return 1

    return args.func(args)


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