"""Read-only web project discovery and classification.

We classify a directory by the presence of marker files only. We NEVER read the
contents of wp-config.php, .env, composer.json secrets, etc. — only that the
files exist. SECURITY: no .env values, no credentials, no private keys.
"""

from __future__ import annotations

from pathlib import Path
from typing import Any

from logger import get_logger

log = get_logger("discovery.projects")

_WEB_ROOTS = ("/var/www", "/srv/www", "/srv")
_MAX_DEPTH = 2


def classify(path: Path) -> dict[str, Any] | None:
    """Classify a single directory. Returns None if it isn't a project root."""
    if (path / "wp-config.php").exists():
        return {"type": "wordpress", "path": str(path), "runtime": "php"}
    if (path / "artisan").exists() and (path / "composer.json").exists():
        return {"type": "laravel", "path": str(path), "runtime": "php"}
    if (path / "package.json").exists():
        return {"type": "node", "path": str(path), "runtime": "node"}
    if (path / "index.html").exists():
        return {"type": "static", "path": str(path), "runtime": "static"}
    return None


def find_projects(roots: tuple[str, ...] = _WEB_ROOTS) -> list[dict[str, Any]]:
    """Scan common web roots and classify each immediate project directory."""
    projects: list[dict[str, Any]] = []
    seen: set[str] = set()
    for root in roots:
        base = Path(root)
        if not base.is_dir():
            continue
        for candidate in _candidate_dirs(base, _MAX_DEPTH):
            if str(candidate) in seen:
                continue
            result = classify(candidate)
            if result:
                seen.add(str(candidate))
                projects.append(result)
    return projects


def _candidate_dirs(base: Path, max_depth: int):
    """Yield directories up to max_depth below base (and base itself)."""
    yield base
    for depth in range(1, max_depth + 1):
        pattern = "/".join(["*"] * depth)
        try:
            for path in base.glob(pattern):
                if path.is_dir():
                    yield path
        except OSError:
            continue
