"""AGFS Python SDK - Client library for AGFS Server API"""

__version__ = "0.1.7"

import glob
import importlib.util
import logging
import os
import sys
import sysconfig
from pathlib import Path

from .async_client import AsyncAGFSClient
from .exceptions import (
    AGFSAlreadyExistsError,
    AGFSClientError,
    AGFSConfigError,
    AGFSConnectionError,
    AGFSDirectoryNotEmptyError,
    AGFSFileExistsError,
    AGFSHTTPError,
    AGFSInternalError,
    AGFSInvalidOperationError,
    AGFSInvalidPathError,
    AGFSIoError,
    AGFSIsADirectoryError,
    AGFSMountPointExistsError,
    AGFSMountPointNotFoundError,
    AGFSNetworkError,
    AGFSNotADirectoryError,
    AGFSNotFoundError,
    AGFSNotSupportedError,
    AGFSPermissionDeniedError,
    AGFSPluginError,
    AGFSSerializationError,
    AGFSTimeoutError,
    GitConcurrentCommitError,
    GitRestoreWritebackPartialError,
)
from .helpers import cp, download, upload
from .protocols import AGFSSyncClientProtocol

_logger = logging.getLogger(__name__)

# Directory that ships pre-built native libraries (Rust .so/.dylib).
_LIB_DIR = Path(__file__).resolve().parent.parent / "lib"


def _is_compatible_ragfs_extension(path: str, ext_suffix: str) -> bool:
    """Return whether a vendored ragfs_python extension can be loaded here."""
    name = Path(path).name
    if not name.startswith("ragfs_python"):
        return False

    # CPython-specific extensions are only safe for the exact running
    # interpreter ABI tag. Reject both Unix-style `.cpython-312-...` and
    # Windows-style `.cp312-...` artifacts unless they exactly match
    # the active EXT_SUFFIX.
    if name.startswith("ragfs_python.cp") and not name.startswith("ragfs_python.abi3."):
        return name == f"ragfs_python{ext_suffix}"

    # Stable ABI artifacts are intentionally interpreter-independent.
    if name.startswith("ragfs_python.abi3."):
        return True

    # Keep accepting generic platform extensions when projects ship them.
    return name.endswith((".so", ".dylib", ".pyd"))


def _find_ragfs_so():
    """Locate the ragfs_python native extension inside openviking/lib/.

    Returns the path to the ``.so`` / ``.dylib`` / ``.pyd`` file, or *None*.
    """
    try:
        ext_suffix = sysconfig.get_config_var("EXT_SUFFIX") or ".so"
        # Exact match first: ragfs_python.cpython-312-darwin.so or ragfs_python.abi3.so
        exact = _LIB_DIR / f"ragfs_python{ext_suffix}"
        if exact.exists():
            return str(exact)
        # Try abi3 suffix explicitly first (stable ABI)
        abi3_suffix = ".abi3.so"
        if sys.platform == "win32":
            abi3_suffix = ".abi3.pyd"
        abi3_exact = _LIB_DIR / f"ragfs_python{abi3_suffix}"
        if abi3_exact.exists():
            return str(abi3_exact)
        # Glob fallback: keep stable/generic artifacts, but never load a
        # CPython-version-specific binary whose tag differs from EXT_SUFFIX.
        for pattern in ("ragfs_python.cpython-*", "ragfs_python.abi3.*", "ragfs_python.*"):
            for match in sorted(glob.glob(str(_LIB_DIR / pattern))):
                if _is_compatible_ragfs_extension(match, ext_suffix):
                    return match
    except Exception:
        pass
    return None


def _load_rust_binding():
    """Attempt to load the Rust (PyO3) binding client.

    Prefers the pip-installed ``ragfs_python`` package (e.g. from maturin develop),
    then falls back to the vendored native extension in openviking/lib/.
    """
    # Prefer pip-installed version (handles @rpath correctly)
    try:
        from ragfs_python import RAGFSBindingClient as _Rust

        return _Rust, None
    except ImportError:
        pass

    # Fallback: vendored .so in openviking/lib/
    try:
        so_path = _find_ragfs_so()
        if so_path:
            spec = importlib.util.spec_from_file_location("ragfs_python", so_path)
            mod = importlib.util.module_from_spec(spec)
            spec.loader.exec_module(mod)
            return mod.RAGFSBindingClient, None
    except Exception:
        pass

    raise ImportError("Rust binding not available")


def get_binding_client():
    """Get the RAGFS binding client class.

    Returns:
        ``(RAGFSBindingClient_class, BindingFileHandle_class)``
    """
    try:
        client, fh = _load_rust_binding()
        _logger.info("Loaded RAGFS Rust binding")
        return client, fh
    except ImportError as exc:
        raise ImportError("ragfs_python native library is not available: " + str(exc)) from exc


# Module-level defaults
# Ensure module import never fails, even if bindings are unavailable
try:
    RAGFSBindingClient, BindingFileHandle = get_binding_client()
    # Backward compatibility alias
    AGFSBindingClient = RAGFSBindingClient
except Exception:
    _logger.warning(
        "Failed to initialize RAGFSBindingClient during module import; "
        "RAGFSBindingClient will be None. Use get_binding_client() for explicit handling."
    )
    RAGFSBindingClient = None
    AGFSBindingClient = None
    BindingFileHandle = None

__all__ = [
    "AsyncAGFSClient",
    "AGFSSyncClientProtocol",
    "AGFSBindingClient",
    "RAGFSBindingClient",
    "BindingFileHandle",
    "get_binding_client",
    "AGFSClientError",
    "AGFSConnectionError",
    "AGFSTimeoutError",
    "AGFSHTTPError",
    "AGFSNotSupportedError",
    "AGFSNotFoundError",
    "AGFSAlreadyExistsError",
    "AGFSFileExistsError",
    "AGFSPermissionDeniedError",
    "AGFSInvalidPathError",
    "AGFSNotADirectoryError",
    "AGFSIsADirectoryError",
    "AGFSDirectoryNotEmptyError",
    "AGFSInvalidOperationError",
    "AGFSIoError",
    "AGFSConfigError",
    "AGFSMountPointNotFoundError",
    "AGFSMountPointExistsError",
    "AGFSSerializationError",
    "AGFSNetworkError",
    "AGFSInternalError",
    "AGFSPluginError",
    "GitConcurrentCommitError",
    "GitRestoreWritebackPartialError",
    "cp",
    "upload",
    "download",
]
