library/paths.py
# library/paths.py
"""
Централизованные пути платформы.

Использование:
    from library.paths import PATHS, project_path, project_data_path

    config_file = PATHS.LIBRARY / "config.yaml"
    project_dir = PATHS.PROJECTS / "pirotehnika"

Принцип:
    НИКОГДА не хардкодить пути в коде!
    Менять пути только здесь или через ENV.
"""

import os
from pathlib import Path
from dataclasses import dataclass


@dataclass(frozen=True)
class WorkspacePaths:
    """
    Пути платформы.

    Все пути — Path объекты для удобной работы:
        PATHS.LIBRARY / "functions" / "format"
    """

    # ══════════════════════════════════════════════════════════════════
    # КОРНЕВЫЕ (из ENV или defaults)
    # ══════════════════════════════════════════════════════════════════

    WORKSPACE: Path      # /opt/claude-workspace
    DATASPACE: Path      # /mnt/beget-s3
    BACKUPSPACE: Path    # /mnt/beget-infra

    # ══════════════════════════════════════════════════════════════════
    # LIBRARY (L2-L6)
    # ══════════════════════════════════════════════════════════════════

    LIBRARY: Path        # library/
    PRIMITIVES: Path     # library/primitives/
    FUNCTIONS: Path      # library/functions/
    CONNECTORS: Path     # library/connectors/
    PARSERS: Path        # library/parsers/
    ADAPTERS: Path       # library/adapters/
    STORAGES: Path       # library/storages/
    SERVICES: Path       # library/services/
    INTEGRATIONS: Path   # library/integrations/

    # ══════════════════════════════════════════════════════════════════
    # ПЛАТФОРМА
    # ══════════════════════════════════════════════════════════════════

    SYSTEM: Path         # system/
    AGENTS: Path         # system/agents/
    MONITOR: Path        # system/monitor/
    SCHEDULER: Path      # system/scheduler/

    ARCHITECT: Path      # architect/
    STANDARDS: Path      # architect/standards/

    INFRA: Path          # infra/

    # ══════════════════════════════════════════════════════════════════
    # ПРОЕКТЫ
    # ══════════════════════════════════════════════════════════════════

    PROJECTS: Path       # projects/

    # ══════════════════════════════════════════════════════════════════
    # ДАННЫЕ (S3)
    # ══════════════════════════════════════════════════════════════════

    DATA_PROJECTS: Path  # $DATASPACE/projects/
    DATA_INBOX: Path     # $DATASPACE/inbox/
    DATA_ARCHIVE: Path   # $DATASPACE/archive/


def _build_paths() -> WorkspacePaths:
    """Построить пути из ENV или defaults."""

    # Корневые из ENV
    workspace = Path(os.getenv("WORKSPACE", "/opt/claude-workspace"))
    dataspace = Path(os.getenv("DATASPACE", "/mnt/beget-s3"))
    backupspace = Path(os.getenv("BACKUPSPACE", "/mnt/beget-infra"))

    # Library
    library = workspace / "library"

    return WorkspacePaths(
        # Корневые
        WORKSPACE=workspace,
        DATASPACE=dataspace,
        BACKUPSPACE=backupspace,

        # Library (L2-L6)
        LIBRARY=library,
        PRIMITIVES=library / "primitives",
        FUNCTIONS=library / "functions",
        CONNECTORS=library / "connectors",
        PARSERS=library / "parsers",
        ADAPTERS=library / "adapters",
        STORAGES=library / "storages",
        SERVICES=library / "services",
        INTEGRATIONS=library / "integrations",

        # Платформа
        SYSTEM=workspace / "system",
        AGENTS=workspace / "system" / "agents",
        MONITOR=workspace / "system" / "monitor",
        SCHEDULER=workspace / "system" / "scheduler",
        ARCHITECT=workspace / "architect",
        STANDARDS=workspace / "architect" / "standards",
        INFRA=workspace / "infra",

        # Проекты
        PROJECTS=workspace / "projects",

        # Данные
        DATA_PROJECTS=dataspace / "projects",
        DATA_INBOX=dataspace / "inbox",
        DATA_ARCHIVE=dataspace / "archive",
    )


# ══════════════════════════════════════════════════════════════════════
# СИНГЛТОН — импортировать отсюда
# ══════════════════════════════════════════════════════════════════════

PATHS = _build_paths()


# ══════════════════════════════════════════════════════════════════════
# ХЕЛПЕРЫ
# ══════════════════════════════════════════════════════════════════════

def project_path(name: str) -> Path:
    """
    Путь к проекту (код).

    >>> project_path("pirotehnika")
    Path('/opt/claude-workspace/projects/pirotehnika')
    """
    return PATHS.PROJECTS / name


def project_data_path(name: str) -> Path:
    """
    Путь к данным проекта (S3).

    >>> project_data_path("pirotehnika")
    Path('/mnt/beget-s3/projects/pirotehnika')
    """
    return PATHS.DATA_PROJECTS / name


def project_inbox(name: str) -> Path:
    """
    Inbox проекта для входящих файлов.

    >>> project_inbox("pirotehnika")
    Path('/mnt/beget-s3/projects/pirotehnika/inbox')
    """
    return PATHS.DATA_PROJECTS / name / "inbox"


def connector_path(category: str, name: str) -> Path:
    """
    Путь к коннектору.

    >>> connector_path("api", "ozon")
    Path('.../library/connectors/api/ozon')
    """
    return PATHS.CONNECTORS / category / name


def function_path(category: str) -> Path:
    """
    Путь к категории функций.

    >>> function_path("format")
    Path('.../library/functions/format')
    """
    return PATHS.FUNCTIONS / category


# ══════════════════════════════════════════════════════════════════════
# ПРОВЕРКА И СОЗДАНИЕ
# ══════════════════════════════════════════════════════════════════════

def ensure_paths_exist() -> None:
    """Создать базовые директории если не существуют."""
    dirs = [
        PATHS.LIBRARY,
        PATHS.PRIMITIVES,
        PATHS.FUNCTIONS,
        PATHS.CONNECTORS,
        PATHS.PARSERS,
        PATHS.ADAPTERS,
        PATHS.STORAGES,
        PATHS.SERVICES,
        PATHS.INTEGRATIONS,
    ]
    for d in dirs:
        d.mkdir(parents=True, exist_ok=True)


def check_paths() -> dict:
    """
    Проверить существование путей.

    Returns:
        Dict[str, bool]: {path_name: exists}
    """
    return {
        "WORKSPACE": PATHS.WORKSPACE.exists(),
        "DATASPACE": PATHS.DATASPACE.exists(),
        "LIBRARY": PATHS.LIBRARY.exists(),
        "PROJECTS": PATHS.PROJECTS.exists(),
        "SYSTEM": PATHS.SYSTEM.exists(),
    }


# ══════════════════════════════════════════════════════════════════════
# ДЛЯ ТЕСТИРОВАНИЯ
# ══════════════════════════════════════════════════════════════════════

if __name__ == "__main__":
    print("=== PATHS ===")
    print(f"WORKSPACE:    {PATHS.WORKSPACE}")
    print(f"DATASPACE:    {PATHS.DATASPACE}")
    print(f"LIBRARY:      {PATHS.LIBRARY}")
    print(f"PROJECTS:     {PATHS.PROJECTS}")
    print()
    print("=== CHECK ===")
    for name, exists in check_paths().items():
        status = "✓" if exists else "✗"
        print(f"  {status} {name}")