"""Service launchers for GATI services."""
import os
import shutil
import subprocess
import sys
from pathlib import Path
from typing import Optional, List, Dict


def find_backend_dir() -> Optional[Path]:
    """Find the backend directory.
    
    Tries:
    1. Local development: ../../backend from package
    2. Current working directory
    3. Installed package location (pip install)
    4. Returns None if not found
    """
    # Try relative to package (for development)
    package_dir = Path(__file__).parent.parent.parent.parent
    backend_dir = package_dir / "backend"
    if backend_dir.exists() and (backend_dir / "app").exists():
        return backend_dir
    
    # Try from current working directory
    cwd_backend = Path.cwd() / "backend"
    if cwd_backend.exists() and (cwd_backend / "app").exists():
        return cwd_backend
    
    # Try to find in installed package location (pip install)
    try:
        import gati
        import importlib.util
        
        # Get the installed package location
        gati_spec = importlib.util.find_spec("gati")
        if gati_spec and gati_spec.origin:
            gati_init = Path(gati_spec.origin)  # .../site-packages/gati/__init__.py
            site_packages = gati_init.parent.parent  # .../site-packages/
            
            # Backend is installed alongside gati package in site-packages/backend
            # (from the custom build step that copies it to build/lib/backend)
            direct_backend = site_packages / "backend"
            if direct_backend.exists() and (direct_backend / "app").exists():
                return direct_backend
            
            # Also check if backend is in the gati package directory (editable installs)
            pkg_backend = gati_init.parent.parent / "backend"
            if pkg_backend.exists() and (pkg_backend / "app").exists():
                return pkg_backend
            
            # Look for gati-X.X.X directory structure (where backend might be in source dist)
            for item in site_packages.glob("gati-*"):
                if item.is_dir():
                    pip_backend = item / "backend"
                    if pip_backend.exists() and (pip_backend / "app").exists():
                        return pip_backend
    except Exception:
        pass
    
    return None


def find_dashboard_dir() -> Optional[Path]:
    """Find the dashboard directory."""
    package_dir = Path(__file__).parent.parent.parent.parent
    dashboard_dir = package_dir / "dashboard"
    if dashboard_dir.exists() and (dashboard_dir / "dist").exists():
        return dashboard_dir
    
    cwd_dashboard = Path.cwd() / "dashboard"
    if cwd_dashboard.exists() and (cwd_dashboard / "dist").exists():
        return cwd_dashboard
    
    # Try to find in installed package location (pip install)
    try:
        import gati
        import importlib.util
        
        # Get the installed package location
        gati_spec = importlib.util.find_spec("gati")
        if gati_spec and gati_spec.origin:
            gati_init = Path(gati_spec.origin)  # .../site-packages/gati/__init__.py
            site_packages = gati_init.parent.parent  # .../site-packages/
            
            # Look for gati-X.X.X directory structure (where dashboard might be)
            for item in site_packages.glob("gati-*"):
                if item.is_dir():
                    pip_dashboard = item / "dashboard"
                    if pip_dashboard.exists() and (pip_dashboard / "dist").exists():
                        return pip_dashboard
            
            # Fallback: check directly in site-packages
            direct_dashboard = site_packages / "dashboard"
            if direct_dashboard.exists() and (direct_dashboard / "dist").exists():
                return direct_dashboard
            
            # Also check if dashboard is in the gati package directory
            pkg_dashboard = gati_init.parent.parent / "dashboard"
            if pkg_dashboard.exists() and (pkg_dashboard / "dist").exists():
                return pkg_dashboard
    except Exception:
        pass
    
    return None


def find_mcp_server_path() -> Optional[Path]:
    """Find the MCP server executable path.
    
    Returns:
        Path to mcp-server/dist/index.js, or None if not found
    """
    # Try relative to package (for development)
    package_dir = Path(__file__).parent.parent.parent.parent
    mcp_dir = package_dir / "mcp-server"
    mcp_server = mcp_dir / "dist" / "index.js"
    
    if mcp_server.exists():
        return mcp_server
    
    # Try from current working directory
    cwd_mcp = Path.cwd() / "mcp-server" / "dist" / "index.js"
    if cwd_mcp.exists():
        return cwd_mcp
    
    # Try to find in installed package location (pip install)
    try:
        import gati
        gati_pkg = Path(gati.__file__).parent.parent.parent
        # Check if installed via pip (site-packages structure)
        # The MCP server should be at the same level as the gati package
        installed_mcp = gati_pkg.parent / "mcp-server" / "dist" / "index.js"
        if installed_mcp.exists():
            return installed_mcp
        
        # Also check if mcp-server is in the gati package directory (editable installs)
        pkg_mcp = gati_pkg / "mcp-server" / "dist" / "index.js"
        if pkg_mcp.exists():
            return pkg_mcp
        
        # Check if we're in a development environment (mcp-server at repo root)
        repo_root = gati_pkg
        while repo_root.parent != repo_root:
            repo_root = repo_root.parent
            if (repo_root / "mcp-server" / "dist" / "index.js").exists():
                return repo_root / "mcp-server" / "dist" / "index.js"
            # Stop if we've gone too far up
            if repo_root.name in ("site-packages", "dist-packages", "lib"):
                break
    except Exception:
        pass
    
    return None


def check_dependencies() -> Dict[str, bool]:
    """Check if required dependencies are available."""
    deps = {
        "python": shutil.which("python3") or shutil.which("python"),
        "node": shutil.which("node"),
        "uvicorn": None,
    }
    
    # Check if uvicorn is available
    if deps["python"]:
        try:
            result = subprocess.run(
                [deps["python"], "-m", "pip", "show", "uvicorn"],
                capture_output=True,
                text=True,
            )
            deps["uvicorn"] = result.returncode == 0
        except Exception:
            deps["uvicorn"] = False
    
    return deps


def start_backend(
    data_dir: Path,
    port: int = 8000,
    host: str = "0.0.0.0",
) -> tuple[List[str], Dict[str, str], Optional[Path]]:
    """Start the backend service.
    
    Args:
        data_dir: Directory to store database and data
        port: Port to run on
        host: Host to bind to
        
    Returns:
        Command to run
    """
    backend_dir = find_backend_dir()
    if not backend_dir:
        raise RuntimeError(
            "Backend directory not found. Please ensure you're running from "
            "the GATI SDK repository root, or install the backend separately."
        )
    
    # Check and install backend dependencies
    requirements_file = backend_dir / "requirements.txt"
    if requirements_file.exists():
        print("Checking backend dependencies...")
        try:
            # Try importing fastapi to check if dependencies are installed
            import fastapi
        except ImportError:
            print("Installing backend dependencies...")
            subprocess.run(
                [sys.executable, "-m", "pip", "install", "-q", "-r", str(requirements_file)],
                check=False,
            )
    
    # Ensure data directory exists
    data_dir.mkdir(parents=True, exist_ok=True)
    db_path = data_dir / "gati.db"
    
    # Set environment variables
    env = os.environ.copy()
    env["DATABASE_URL"] = f"sqlite+aiosqlite:///{db_path}"
    env["PYTHONPATH"] = str(backend_dir)
    
    # Check if migrations need to be run
    alembic_ini = backend_dir / "alembic.ini"
    if alembic_ini.exists():
        # Run migrations first (don't fail if alembic not available)
        try:
            result = subprocess.run(
                [sys.executable, "-m", "alembic", "upgrade", "head"],
                cwd=backend_dir,
                env=env,
                capture_output=True,
                check=False,
            )
            if result.returncode != 0:
                # If migrations fail, try to continue anyway (database might already exist)
                pass
        except Exception:
            # If alembic is not available, continue anyway
            pass
    
    # Start uvicorn
    cmd = [
        sys.executable,
        "-m", "uvicorn",
        "app.main:app",
        "--host", host,
        "--port", str(port),
        "--workers", "1",  # Single worker for SQLite
    ]
    
    return cmd, env, backend_dir


def start_dashboard(port: int = 3000) -> tuple[List[str], Dict[str, str], Optional[Path]]:
    """Start the dashboard service.
    
    Args:
        port: Port to run on
        
    Returns:
        Command, environment, and working directory
    """
    dashboard_dir = find_dashboard_dir()
    if not dashboard_dir:
        raise RuntimeError(
            "Dashboard directory not found. Please ensure the dashboard is built "
            "and available in the dashboard/dist directory."
        )
    
    dist_dir = dashboard_dir / "dist"
    if not dist_dir.exists():
        raise RuntimeError(
            f"Dashboard dist directory not found at {dist_dir}. "
            "Please build the dashboard first: cd dashboard && npm run build"
        )
    
    # Use SPA-aware server to handle client-side routing
    cmd = [
        sys.executable,
        "-m", "gati.cli.spa_server",
        str(port),
        str(dist_dir),
    ]
    
    return cmd, os.environ.copy(), None


def start_mcp_server(
    data_dir: Path,
    backend_url: str = "http://localhost:8000",
) -> Optional[tuple[List[str], Dict[str, str], Optional[Path]]]:
    """Start the MCP server (optional).
    
    Args:
        data_dir: Directory with database
        backend_url: Backend API URL
        
    Returns:
        Command, environment, and working directory, or None if not available
    """
    package_dir = Path(__file__).parent.parent.parent.parent
    mcp_dir = package_dir / "mcp-server"
    
    if not mcp_dir.exists():
        return None
    
    dist_dir = mcp_dir / "dist"
    if not dist_dir.exists() or not (dist_dir / "index.js").exists():
        # Try to build it
        if (mcp_dir / "package.json").exists():
            subprocess.run(
                ["npm", "run", "build"],
                cwd=mcp_dir,
                check=False,
            )
    
    if not (dist_dir / "index.js").exists():
        return None
    
    db_path = data_dir / "gati.db"
    env = os.environ.copy()
    env["DATABASE_PATH"] = str(db_path)
    env["BACKEND_URL"] = backend_url
    
    cmd = ["node", str(dist_dir / "index.js")]
    
    return cmd, env, None

