import os
import re
import sys
from importlib.util import find_spec
from pathlib import Path
from dotenv import load_dotenv

_INT_PATTERN = re.compile(r"^[+-]?\d+$")
_FLOAT_PATTERN = re.compile(r"^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$")
_ENV_LOADED = False


def _find_project_root(start: Path) -> Path | None:
    current = start
    while True:
        if (current / "pyproject.toml").exists() or (current / ".env").exists():
            return current
        if current.parent == current:
            return None
        current = current.parent


def _find_module_root(module_name: str) -> Path | None:
    spec = find_spec(module_name)
    if spec and spec.origin:
        return _find_project_root(Path(spec.origin).resolve())
    return None


def _ordered_env_roots() -> list[Path]:
    roots: list[Path] = []
    env_file = os.getenv("ENV_FILE")
    if env_file:
        env_path = Path(env_file).expanduser().resolve()
        roots.append(env_path.parent)

    argv0 = Path(sys.argv[0]).expanduser().resolve() if sys.argv and sys.argv[0] else None
    if argv0 and argv0.exists():
        entry_root = _find_project_root(argv0.parent)
        if entry_root:
            roots.append(entry_root)

    deepthink_root = _find_module_root("deepthink")
    if deepthink_root:
        roots.append(deepthink_root)

    analogai_root = _find_module_root("analogai")
    if analogai_root:
        roots.append(analogai_root)

    foundation_root = _find_project_root(Path(__file__).resolve())
    if foundation_root:
        roots.append(foundation_root)

    seen: set[Path] = set()
    ordered: list[Path] = []
    for root in roots:
        if root not in seen:
            ordered.append(root)
            seen.add(root)
    return ordered


def load_envs_in_order() -> None:
    global _ENV_LOADED
    if _ENV_LOADED:
        return
    for root in _ordered_env_roots():
        env_path = root / ".env"
        if env_path.exists():
            load_dotenv(env_path, override=False)
    _ENV_LOADED = True


def parse_int(value: str | None, default: int) -> int:
    if value is None:
        return default
    text = str(value).strip()
    if not text:
        return default
    if not _INT_PATTERN.match(text):
        return default
    return int(text)


def parse_optional_float(value: str | None, default: float | None = None) -> float | None:
    if value is None:
        return default
    text = str(value).strip()
    if not text:
        return default
    if not _FLOAT_PATTERN.match(text):
        return default
    return float(text)


def parse_bool(value: str | None, default: bool) -> bool:
    if value is None:
        return default
    text = str(value).strip().lower()
    if text in {"1", "true", "yes", "y", "on"}:
        return True
    if text in {"0", "false", "no", "n", "off"}:
        return False
    return default


def get_required_env(key: str) -> str:
    value = os.getenv(key)
    if value is None:
        raise ValueError(f"Required environment variable '{key}' is not set")
    return value


def get_optional_env(key: str, default: str) -> str:
    return os.getenv(key, default)


def resolve_cpu_count() -> int:
    cpu_count = os.cpu_count()
    return cpu_count if cpu_count and cpu_count > 0 else 1
