"""Click wrappers: tests + diagnose groups — split from ctl.py."""
from __future__ import annotations

import os
import sys
import json
import socket
import subprocess
from pathlib import Path
from urllib.parse import urlparse
from typing import NamedTuple

import click
import httpx

from proxym.cli import diagnostics as diag_cli
from proxym.cli._groups._shared import (
    make_args as _make_args,
    register_commands,
    passthrough,
    passthrough_fmt,
)


# ── Tests group ──────────────────────────────────────────────

@click.group(name="tests")
def tests():
    """Run system diagnostics."""
    pass


register_commands(tests, [
    ("vscode",      diag_cli.cmd_test_vscode,      "Test VS Code Web connectivity."),
    ("proxy",       diag_cli.cmd_test_proxy,        "Test LLM proxy connectivity."),
    ("agent-setup", diag_cli.cmd_test_agent_setup,  "Comprehensive agent readiness check."),
    ("agent",       diag_cli.cmd_test_agent,         "Test agent with a simple completion."),
], passthrough)

register_commands(tests, [
    ("status",     diag_cli.cmd_tests_status,     "Run all diagnostic tests."),
    ("providers",  diag_cli.cmd_test_providers,   "Check configured LLM providers."),
    ("extensions", diag_cli.cmd_test_extensions,  "Check installed VS Code extensions."),
], passthrough_fmt)


# ── Diagnose alias group ─────────────────────────────────────

@click.group(name="diagnose")
def diagnose():
    """Run diagnostics to verify system health (alias for 'tests')."""
    pass


register_commands(diagnose, [
    ("proxy",  diag_cli.cmd_test_proxy,   "Test proxy connectivity."),
    ("vscode", diag_cli.cmd_test_vscode,  "Test VS Code Web status."),
    ("agent",  diag_cli.cmd_test_agent,   "Test agent setup + completion."),
], passthrough)

register_commands(diagnose, [
    ("all",       diag_cli.cmd_tests_status,   "Run all diagnostic tests."),
    ("providers", diag_cli.cmd_test_providers, "Test provider configuration."),
], passthrough_fmt)


@diagnose.command(name="test")
@click.argument("test_name")
@click.pass_context
def diagnose_one(ctx: click.Context, test_name: str):
    """Run a specific test (vscode|proxy|roo|extensions|providers|copilot|agent-setup|agent)."""
    from proxym.cli._client import make_client, print_json
    args = _make_args(ctx)
    c = make_client(args.proxy, args.key)
    r = c.get(f"/tests/{test_name}", timeout=30)
    print_json(r.json())


# ── Doctor command ───────────────────────────────────────────

class CheckResult(NamedTuple):
    name: str
    status: str
    message: str
    fix: str | None = None


def _load_env_vars() -> dict[str, str]:
    """Load environment variables from .env."""
    env_vars = {}
    env_path = Path(".env")
    if env_path.exists():
        content = env_path.read_text()
        for line in content.split('\n'):
            line = line.strip()
            if line and not line.startswith('#') and '=' in line:
                key, _, value = line.partition('=')
                env_vars[key] = value.strip().strip('"\'')
    return env_vars


def _run_doctor_checks() -> list[CheckResult]:
    """Run comprehensive system diagnostics."""
    checks = []
    env_vars = _load_env_vars()
    
    # Check .env file
    env_path = Path(".env")
    if not env_path.exists():
        checks.append(CheckResult(".env file", "error", ".env file not found", 
            "Create .env: cp .env.example .env && edit with your API keys"))
    elif not env_path.read_text().strip():
        checks.append(CheckResult(".env file", "error", ".env file is empty",
            "Add required configuration: AI_PROXY_MASTER_KEY and LLM provider keys"))
    else:
        checks.append(CheckResult(".env file", "ok", f"Loaded {len(env_vars)} variables"))
    
    # Check required config
    if not env_vars.get("AI_PROXY_MASTER_KEY"):
        checks.append(CheckResult("Master key", "error", "AI_PROXY_MASTER_KEY missing",
            "Add to .env: AI_PROXY_MASTER_KEY=sk-proxy-..."))
    else:
        key = env_vars.get("AI_PROXY_MASTER_KEY", "")
        if key.startswith("sk-or-"):
            checks.append(CheckResult("Master key", "warning", "External OpenRouter key - SSE may not work",
                "Generate local key: proxym keys generate"))
        elif not key.startswith("sk-proxy-"):
            checks.append(CheckResult("Master key", "warning", "Key format may be invalid",
                "Generate new key: proxym keys generate"))
        else:
            checks.append(CheckResult("Master key", "ok", "Local key format"))
    
    # Check LLM providers
    providers = ["OPENROUTER_API_KEY", "ANTHROPIC_API_KEY", "OPENAI_API_KEY", "GOOGLE_AI_API_KEY", "MISTRAL_API_KEY"]
    configured = [p.replace("_API_KEY", "").replace("_", " ").title() for p in providers if env_vars.get(p)]
    if not configured:
        checks.append(CheckResult("LLM providers", "error", "No provider API keys configured",
            "Add at least one: OPENROUTER_API_KEY, ANTHROPIC_API_KEY, OPENAI_API_KEY, etc."))
    else:
        checks.append(CheckResult("LLM providers", "ok", f"Configured: {', '.join(configured)}"))
    
    # Check proxy server
    proxy_url = env_vars.get("AI_PROXY_URL", "http://localhost:4000")
    try:
        parsed = urlparse(proxy_url)
        host = parsed.hostname or "localhost"
        port = parsed.port or 4000
        
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(2)
        result = sock.connect_ex((host, port))
        sock.close()
        
        if result != 0:
            checks.append(CheckResult("Proxy server", "error", f"Not reachable at {host}:{port}",
                f"Start server: proxym serve (expected on {proxy_url})"))
        else:
            try:
                r = httpx.get(f"{proxy_url}/health", timeout=5)
                if r.status_code == 200:
                    version = r.json().get("version", "unknown")
                    checks.append(CheckResult("Proxy server", "ok", f"Running (v{version})"))
                else:
                    checks.append(CheckResult("Proxy server", "warning", f"Health check failed (HTTP {r.status_code})",
                        "Check server logs"))
            except Exception:
                checks.append(CheckResult("Proxy server", "warning", "Port open but HTTP failed",
                    "Server may be starting up - wait and retry"))
    except Exception as e:
        checks.append(CheckResult("Proxy server", "error", f"Check failed: {e}",
            "Check AI_PROXY_URL format"))
    
    # Check data directory
    data_dir = Path("data")
    if not data_dir.exists():
        checks.append(CheckResult("Data directory", "warning", "Directory not found",
            "Create: mkdir data"))
    elif not os.access(data_dir, os.W_OK):
        checks.append(CheckResult("Data directory", "error", "Not writable",
            "Fix permissions: chmod 755 data && chown $USER data"))
    else:
        checks.append(CheckResult("Data directory", "ok", f"Ready ({len(list(data_dir.iterdir()))} files)"))
    
    # Check git repository
    git_dir = Path(".git")
    if not git_dir.exists():
        checks.append(CheckResult("Git repository", "warning", "Not a git repo - dashboard git sync won't work",
            "Initialize: git init && git add . && git commit -m 'initial'"))
    else:
        # Check for commits
        try:
            result = subprocess.run(
                ["git", "log", "--oneline", "-1"],
                capture_output=True,
                text=True,
                timeout=2
            )
            if result.returncode == 0 and result.stdout.strip():
                checks.append(CheckResult("Git repository", "ok", "Repository with commits"))
            else:
                checks.append(CheckResult("Git repository", "warning", "Git initialized but no commits",
                    "Create initial commit: git add . && git commit -m 'initial'"))
        except Exception:
            checks.append(CheckResult("Git repository", "ok", "Repository present"))
    
    # Check Docker
    try:
        result = subprocess.run(
            ["docker", "version", "--format", "{{.Server.Version}}"],
            capture_output=True,
            text=True,
            timeout=3
        )
        if result.returncode == 0 and result.stdout.strip():
            version = result.stdout.strip()
            checks.append(CheckResult("Docker", "ok", f"Docker v{version}"))
        else:
            checks.append(CheckResult("Docker", "warning", "Docker installed but daemon not running",
                "Start Docker: sudo systemctl start docker or launch Docker Desktop"))
    except FileNotFoundError:
        checks.append(CheckResult("Docker", "info", "Not installed (optional)",
            "Install Docker for containerized tools"))
    except Exception as e:
        checks.append(CheckResult("Docker", "warning", f"Docker check failed: {e}"))
    
    # Check Node.js (for dashboard dev)
    try:
        result = subprocess.run(
            ["node", "--version"],
            capture_output=True,
            text=True,
            timeout=2
        )
        if result.returncode == 0:
            version = result.stdout.strip().lstrip('v')
            major = int(version.split('.')[0])
            if major >= 18:
                checks.append(CheckResult("Node.js", "ok", f"v{version}"))
            else:
                checks.append(CheckResult("Node.js", "warning", f"v{version} (recommended: v18+)",
                    "Upgrade: nvm install 20"))
        else:
            checks.append(CheckResult("Node.js", "info", "Not installed (optional for dashboard dev)"))
    except FileNotFoundError:
        checks.append(CheckResult("Node.js", "info", "Not installed (optional)"))
    except Exception as e:
        checks.append(CheckResult("Node.js", "warning", f"Check failed: {e}"))
    
    # Check Python dependencies
    try:
        import proxym
        checks.append(CheckResult("Python packages", "ok", "proxym importable"))
    except ImportError as e:
        checks.append(CheckResult("Python packages", "error", f"Cannot import proxym: {e}",
            "Install: pip install -e ."))
    
    # Check Ollama if configured
    ollama_url = env_vars.get("OLLAMA_BASE_URL", "")
    if ollama_url:
        try:
            r = httpx.get(f"{ollama_url}/api/tags", timeout=3)
            if r.status_code == 200:
                models = r.json().get("models", [])
                checks.append(CheckResult("Ollama", "ok", f"Running with {len(models)} models"))
            else:
                checks.append(CheckResult("Ollama", "warning", f"Ollama returned HTTP {r.status_code}",
                    "Check Ollama service"))
        except Exception as e:
            checks.append(CheckResult("Ollama", "warning", f"Not reachable: {e}",
                "Start Ollama or check OLLAMA_BASE_URL"))
    
    # Check LLM backend connectivity (if server running and key available)
    proxy_running = any(c.name == "Proxy server" and c.status == "ok" for c in checks)
    master_key = env_vars.get("AI_PROXY_MASTER_KEY", "")
    
    if proxy_running and master_key:
        try:
            r = httpx.post(
                f"{proxy_url}/v1/chat/completions",
                headers={"Authorization": f"Bearer {master_key}", "Content-Type": "application/json"},
                json={
                    "model": "cheap",
                    "messages": [{"role": "user", "content": "hi"}],
                    "max_tokens": 5
                },
                timeout=10
            )
            if r.status_code == 200:
                checks.append(CheckResult("LLM backend", "ok", "LLM API responding"))
            elif r.status_code == 401:
                checks.append(CheckResult("LLM backend", "error", "Authentication failed - invalid master key",
                    "Check AI_PROXY_MASTER_KEY or regenerate: proxym keys generate"))
            elif r.status_code == 429:
                checks.append(CheckResult("LLM backend", "warning", "Rate limited by provider",
                    "Wait and retry, or check provider quotas"))
            else:
                error = r.json().get("error", {}).get("message", f"HTTP {r.status_code}")
                checks.append(CheckResult("LLM backend", "error", f"LLM error: {error}",
                    "Check LLM provider API key configuration"))
        except httpx.ConnectError:
            checks.append(CheckResult("LLM backend", "error", "Cannot connect to LLM backend",
                "Check proxy server and AI_PROXY_URL"))
        except Exception as e:
            checks.append(CheckResult("LLM backend", "warning", f"Test failed: {e}"))
    elif not proxy_running:
        checks.append(CheckResult("LLM backend", "info", "Skipped - proxy server not running",
            "Start server to test LLM: proxym serve"))
    else:
        checks.append(CheckResult("LLM backend", "info", "Skipped - no master key",
            "Add AI_PROXY_MASTER_KEY to test LLM"))
    
    # Check SQLite/database
    try:
        import sqlite3
        data_dir = Path("data")
        if data_dir.exists():
            db_files = list(data_dir.glob("*.sqlite")) + list(data_dir.glob("*.db"))
            if db_files:
                checks.append(CheckResult("Database", "ok", f"SQLite DB found ({len(db_files)} file(s))"))
            else:
                checks.append(CheckResult("Database", "info", "No SQLite DB yet (will be created on first use)"))
        else:
            checks.append(CheckResult("Database", "warning", "Data directory missing",
                "Create: mkdir data"))
    except Exception as e:
        checks.append(CheckResult("Database", "warning", f"SQLite check failed: {e}"))
    
    return checks


def _print_doctor_report(checks: list[CheckResult]):
    """Print diagnostic report."""
    print("\n" + "═" * 60)
    print("  🔍 PROXYM SYSTEM DIAGNOSTICS")
    print("═" * 60 + "\n")
    
    ok = [c for c in checks if c.status == "ok"]
    warnings = [c for c in checks if c.status == "warning"]
    errors = [c for c in checks if c.status == "error"]
    
    if errors:
        print(f"\033[0;31m✗ {len(errors)} CRITICAL ISSUE(S)\033[0m")
        for check in errors:
            print(f"\n  \033[1m{check.name}\033[0m")
            print(f"  → {check.message}")
            if check.fix:
                print(f"  \033[2m  Fix: {check.fix}\033[0m")
        print()
    
    if warnings:
        print(f"\033[0;33m⚠ {len(warnings)} WARNING(S)\033[0m")
        for check in warnings:
            print(f"\n  \033[1m{check.name}\033[0m")
            print(f"  → {check.message}")
            if check.fix:
                print(f"  \033[2m  Fix: {check.fix}\033[0m")
        print()
    
    if ok:
        print(f"\033[0;32m✓ {len(ok)} CHECK(S) PASSED\033[0m")
        for check in ok:
            print(f"  • {check.name}: {check.message}")
        print()
    
    print("─" * 60)
    if errors:
        print("\033[0;31mStatus: NOT READY - fix critical issues\033[0m")
    elif warnings:
        print("\033[0;33mStatus: FUNCTIONAL with warnings\033[0m")
    else:
        print("\033[0;32mStatus: ALL SYSTEMS OPERATIONAL\033[0m")
    print("─" * 60 + "\n")


@click.command(name="doctor")
def doctor_cmd():
    """Comprehensive system diagnostics — check configuration and health.
    
    Checks:
      • .env file and API keys
      • Proxy server connectivity
      • LLM provider configuration
      • Data directory and git repository
    
    Run this first if you're experiencing issues.
    """
    checks = _run_doctor_checks()
    _print_doctor_report(checks)
    
    errors = [c for c in checks if c.status == "error"]
    if errors:
        print("Quick actions:")
        print("  proxym keys init     # Initialize API keys")
        print("  proxym serve         # Start the proxy server")
        sys.exit(1)
