import os
import re
from pathlib import Path


_INSTANCE_NAME_RE = re.compile(r"^[a-z0-9][a-z0-9_-]{0,63}$")


DEFAULT_LEFT4ME_ROOT = Path("/var/lib/left4me")


def get_left4me_root() -> Path:
    raw = os.environ.get("LEFT4ME_ROOT")
    if raw is None:
        return DEFAULT_LEFT4ME_ROOT
    root = raw.strip()
    if not root:
        raise ValueError("LEFT4ME_ROOT must not be empty")
    if root != raw:
        raise ValueError("LEFT4ME_ROOT must not contain leading or trailing whitespace")
    path = Path(root)
    if not path.is_absolute():
        raise ValueError("LEFT4ME_ROOT must be absolute")
    return path


def validate_instance_name(name: str) -> str:
    if not _INSTANCE_NAME_RE.fullmatch(name):
        raise ValueError(
            "instance name must match [a-z0-9][a-z0-9_-]{0,63} "
            "(lowercase, no path separators, no whitespace)"
        )
    return name


def validate_overlay_ref(ref: str) -> str:
    stripped = ref.strip()
    if stripped != ref:
        raise ValueError("overlay ref must not contain leading or trailing whitespace")
    if stripped in {"", ".", ".."}:
        raise ValueError("overlay ref must not be empty or current/parent directory")
    if Path(stripped).is_absolute():
        raise ValueError("overlay ref must be relative")

    components = stripped.split("/")
    if any(component in {"", ".", ".."} for component in components):
        raise ValueError("overlay ref must not contain empty, current, or parent components")

    return stripped


def overlay_path(ref: str, *, root: Path | None = None) -> Path:
    safe_ref = validate_overlay_ref(ref)
    left4me_root = get_left4me_root() if root is None else Path(root)
    overlays_root = left4me_root / "overlays"
    candidate = overlays_root / safe_ref

    resolved_overlays_root = overlays_root.resolve()
    resolved_candidate = candidate.resolve()
    if resolved_candidate != resolved_overlays_root and resolved_overlays_root not in resolved_candidate.parents:
        raise ValueError("overlay path escapes overlay root")

    return candidate
