"""
Microsoft Fabric API module (internal).

This is an internal module. Users should import from fabias directly:

    >>> import fabias
    >>> fabias.client(auth=my_auth)
    >>> ws = fabias.workspace("GENESIS")

This module provides the core Fabric API implementation including:
- Workspace and item management
- Connection handling
- Git integration
- OneLake security (ABAC)
"""

from typing import TYPE_CHECKING, Any, Dict, Optional

# Lazy imports - only import types for type hints at static analysis time
if TYPE_CHECKING:
    from .._shared.auth import AuthProvider, FabricAuth, ServicePrincipalAuth
    from .._shared.exceptions import AuthenticationError, FabiasError, NotFoundError
    from .._shared.response import AzResponse, Job
    from .._shared.runtime import runtime
    from ._client import FabricClient
    from ._shared import (
        ConnectionEncryption,
        ConnectionRole,
        # Connection enums
        ConnectivityType,
        GitCredentialsSource,
        # Git enums
        GitProviderType,
        InitializationStrategy,
        ItemAccess,
        # Item enums
        ItemType,
        MemberType,
        # Notebook formats
        NotebookFormat,
        # Operation enums
        OperationStatus,
        PoolType,
        PrincipalType,
        PrivacyLevel,
        # Permissions
        ReadWrite,
        RequiredAction,
        SingleSignOnType,
        # Role assignment enums
        WorkspaceRole,
        enums,
    )
    from .admin import ActivityEvent, CapacitiesAccessor, Capacity, Tenant
    from .connections import Connection, Connections, ConnectionsAccessor
    from .folders import Folder, FolderAccessor
    from .git import Git
    from .items import (
        Environment,
        ItemReference,
        Lakehouse,
        Libraries,
        Notebook,
        Pipeline,
        Variable,
        VariableAccessor,
        VariableLibrary,
    )
    from .items import Environment as EnvironmentItem
    from .spark import (
        DefaultPoolSettings,
        HighConcurrencySettings,
        JobSettings,
        PoolSettings,
        Spark,
        SparkEnvironment,
        SparkSettings,
        StarterPoolSettings,
    )
    from .workspace import Workspace

# Module-level singleton client
_client: Optional["FabricClient"] = None
_configured: bool = False

# Save references to API functions before they get shadowed by submodule imports
# These get set at the end of this file after functions are defined
_api_functions: Dict[str, Any] = {}


def _create_client(
    tenant_id: Optional[str] = None,
    client_id: Optional[str] = None,
    client_secret: Optional[str] = None,
    auth: Optional["AuthProvider"] = None,
) -> "FabricClient":
    """
    Create or configure the Fabric client.

    When called as a module-level function, sets up the singleton for convenience.
    Returns the client instance for both module-level and explicit multi-client use.

    Args:
        tenant_id: Azure AD tenant ID
        client_id: Application (client) ID
        client_secret: Client secret value
        auth: Pre-configured AuthProvider (recommended)

    Returns:
        FabricClient: Configured client instance

    Examples:
        # Module-level singleton (most common)
        >>> import fabias._fabric as fabric
        >>> fabric.client(
        ...     tenant_id="your-tenant-id",
        ...     client_id="your-client-id",
        ...     client_secret="your-secret"
        ... )
        >>> ws = fabric.workspace("GENESIS")

        # With pre-configured auth
        >>> from fabias._shared.auth import ServicePrincipalAuth
        >>> auth = ServicePrincipalAuth(tenant_id, client_id, secret)
        >>> fabric.client(auth=auth)
        >>> ws = fabric.workspace("GENESIS")

        # Multi-client usage
        >>> client1 = fabric.client(auth=auth1)
        >>> client2 = fabric.client(auth=auth2)
        >>> ws1 = client1.workspace("PROD")
        >>> ws2 = client2.workspace("DEV")
    """
    global _client, _configured

    # Lazy import using sys.modules to avoid namespace pollution
    import sys

    if "fabias._fabric._client" not in sys.modules:
        __import__("fabias._fabric._client")
    FabricClient = sys.modules["fabias._fabric._client"].FabricClient

    # CRITICAL: __import__ adds submodules to parent namespace
    # Remove ALL submodules that got imported to prevent shadowing our API functions
    current_module = sys.modules[__name__]
    import types

    for attr_name in list(vars(current_module).keys()):
        attr = getattr(current_module, attr_name, None)
        if isinstance(attr, types.ModuleType) and attr_name in {
            "client",
            "workspace",
            "connection",
            "connections",
            "git",
        }:
            delattr(current_module, attr_name)

    new_client = FabricClient(
        auth=auth, tenant_id=tenant_id, client_id=client_id, client_secret=client_secret
    )

    # Set as module-level singleton
    _client = new_client
    _configured = True

    return new_client


def _get_client() -> "FabricClient":
    """
    Get or lazily create the singleton FabricClient.

    Returns:
        FabricClient: The configured client instance

    Raises:
        AuthenticationError: If not in Fabric and credentials not configured
    """
    global _client, _configured

    if _client is None:
        # Lazy import using sys.modules to avoid namespace pollution
        import sys

        if "fabias._fabric._client" not in sys.modules:
            __import__("fabias._fabric._client")
        FabricClient = sys.modules["fabias._fabric._client"].FabricClient

        # CRITICAL: __import__ adds submodules to parent namespace
        # Remove ALL submodules that got imported to prevent shadowing our API functions
        current_module = sys.modules[__name__]
        import types

        for attr_name in list(vars(current_module).keys()):
            attr = getattr(current_module, attr_name, None)
            if isinstance(attr, types.ModuleType) and attr_name in {
                "client",
                "workspace",
                "connection",
                "connections",
                "git",
            }:
                delattr(current_module, attr_name)

        # Auto-initialize: works in Fabric, or with env vars standalone
        _client = FabricClient()
        _configured = True

    return _client


def _cleanup_namespace() -> "FabricClient":
    """
    Remove submodules from namespace that shadow our API functions.

    Python automatically adds submodules to parent namespace during import
    (e.g., importing fabias._fabric.workspace adds 'workspace' module which
    shadows the workspace() function).

    We only remove modules whose names match our API functions defined
    in this __init__.py file.
    """
    import sys
    import types

    # These are API function names that might get shadowed by submodule imports
    # Only remove if the attribute is a MODULE (not a function)
    api_names = {"workspace", "connection", "connections", "git"}

    current_module = sys.modules[__name__]

    for name in api_names:
        attr = getattr(current_module, name, None)
        # Only remove if it's a module (submodule got imported)
        # This allows __getattr__ to handle the lookup and return the function
        if isinstance(attr, types.ModuleType):
            delattr(current_module, name)
    """
    Get or lazily create the singleton FabricClient.

    Returns:
        FabricClient: The configured client instance

    Raises:
        AuthenticationError: If not in Fabric and credentials not configured
    """
    global _client, _configured

    if _client is None:
        # Lazy import - use __import__ and immediately clean up namespace pollution
        import sys

        if "fabias._fabric._client" not in sys.modules:
            __import__("fabias._fabric._client")
        FabricClient = sys.modules["fabias._fabric._client"].FabricClient

        # CRITICAL: Remove 'client' module from fabric namespace to prevent collision
        import fabias._fabric

        if hasattr(fabias._fabric, "client") and not callable(
            getattr(fabias._fabric, "client", None)
        ):
            delattr(fabias._fabric, "client")

        # Auto-initialize: works in Fabric, or with env vars standalone
        _client = FabricClient()
        _configured = True

    return _client


def reset() -> None:
    """
    Reset the module state, clearing the cached client.

    Useful for testing or when you need to reconfigure authentication.
    """
    global _client, _configured
    _client = None
    _configured = False


# =============================================================================
# Module-level API functions
# =============================================================================


def workspace(workspace_id: Optional[str] = None) -> "Workspace":
    """
    Get a workspace object for accessing workspace resources.

    Args:
        workspace_id: Workspace identifier. Supports:
            - None: Use current notebook's workspace (Fabric only)
            - "default": Explicitly use current workspace
            - GUID: Lookup by workspace ID
            - Display name: Lookup by workspace name

    Returns:
        Workspace: Workspace object with pipelines, lakehouses, etc.

    Examples:
        Current workspace (Fabric only):

        >>> import fabias._fabric as fabric
        >>> ws = fabric.workspace()

        By name:

        >>> ws = fabric.workspace("GENESIS_EXT")

        By GUID:

        >>> ws = fabric.workspace("6bd19ad7-33b5-4865-b607-7378f6699a66")
    """
    return _get_client().workspace(workspace_id)


def connection(name_or_id: str) -> "Connection":
    """
    Get a specific connection by name or ID.

    Searches tenant-wide connections by name (case-insensitive partial match).
    If only one match is found, returns it. If multiple matches or no matches,
    raises an error.

    Args:
        name_or_id: Connection name or GUID

    Returns:
        Connection: The connection object

    Raises:
        NotFoundError: If no connection matches the name/ID
        FabiasError: If multiple connections match the name

    Examples:
        By name:

        >>> import fabias._fabric as fabric
        >>> conn = fabric.connection("SQL Server")

        By GUID:

        >>> conn = fabric.connection("2db03b89-1737-4dc8-ba25-ee866e70c3e9")
    """
    from .._shared.exceptions import FabiasError, NotFoundError

    # Get accessor via __getattr__ (lazy loaded)
    _connections = __getattr__("connections")

    # Try getting by ID first
    try:
        return _connections.get(name_or_id)
    except Exception:
        pass

    # Search by name
    matches = _connections(name=name_or_id)

    if not matches:
        raise NotFoundError(
            f"Connection '{name_or_id}' not found",
            resource_type="Connection",
            resource_id=name_or_id,
        )

    # If exact match exists, use it
    exact_matches = [c for c in matches if c.name == name_or_id]
    if len(exact_matches) == 1:
        return exact_matches[0]

    # Otherwise, require single match
    if len(matches) > 1:
        raise FabiasError(
            f"Multiple connections found matching '{name_or_id}': "
            + ", ".join(c.name for c in matches[:5])
        )

    return matches[0]


def capacity(name_or_id: str) -> "Capacity":
    """
    Get a specific capacity by name or ID.

    Args:
        name_or_id: Capacity display name or GUID

    Returns:
        Capacity: The capacity object

    Raises:
        NotFoundError: If capacity not found
        FabiasError: If multiple capacities match

    Examples:
        By name:

        >>> capacity = fabric.capacity("Premium-P1")
        >>> print(f"SKU: {capacity.sku}, Region: {capacity.region}")

        By GUID:

        >>> capacity = fabric.capacity("capacity-guid")

    Note:
        Requires Capacity Admin or Fabric Administrator permissions.
    """
    # Get accessor via __getattr__ (lazy loaded)
    _capacities = __getattr__("capacities")
    return _capacities.get(name_or_id)


def tenant() -> "Tenant":
    """
    Get the Fabric tenant settings and admin operations.

    Returns:
        Tenant: Tenant administration object

    Examples:
        >>> tenant = fabric.tenant()
        >>> settings = tenant.settings()
        >>> for setting in settings:
        ...     print(f"{setting.title}: {setting.enabled}")

    Note:
        Requires Fabric Administrator permissions.
        Many operations require PIM (Privileged Identity Management) elevation.
    """
    from .admin import Tenant

    return Tenant(_get_client())


# =============================================================================
# Module-level accessors (imported later to avoid shadowing API functions)
# =============================================================================


def __getattr__(name: str):
    """
    Lazy load enums and handle function name restoration.

    Allows deferring enum imports until they're needed, keeping initial import fast.
    Also handles API functions that might get shadowed by submodule imports.
    """
    # Special case: 'client' function renamed to avoid collision with client.py module
    if name == "client":
        return _create_client

    # API functions that might get shadowed by submodule imports
    if name in _api_functions:
        return _api_functions[name]

    # Lazy load enums module
    if name == "enums":
        from ._shared import enums

        return enums

    # Enum types - lazy load from _shared.enums
    elif name in [
        "GitProviderType",
        "GitCredentialsSource",
        "InitializationStrategy",
        "RequiredAction",
        "WorkspaceRole",
        "ConnectionRole",
        "PrincipalType",
        "ConnectivityType",
        "PrivacyLevel",
        "ConnectionEncryption",
        "SingleSignOnType",
        "OperationStatus",
        "ItemType",
        "ReadWrite",
        "ItemAccess",
        "MemberType",
        "NotebookFormat",
    ]:
        from ._shared import enums

        return getattr(enums, name)

    # Classes - lazy load when accessed
    elif name == "FabricClient":
        from ._client import FabricClient

        return FabricClient
    elif name == "Workspace":
        from .workspace import Workspace

        return Workspace
    elif name == "Pipeline":
        from .items import Pipeline

        return Pipeline
    elif name == "Lakehouse":
        from .items import Lakehouse

        return Lakehouse
    elif name == "Notebook":
        from .items import Notebook

        return Notebook
    elif name == "EnvironmentItem":
        from .items import Environment

        return Environment
    elif name == "Environment":
        from .items import Environment

        return Environment
    elif name == "Libraries":
        from .items import Libraries

        return Libraries
    elif name == "VariableLibrary":
        from .items import VariableLibrary

        return VariableLibrary
    elif name == "Variable":
        from .items import Variable

        return Variable
    elif name == "VariableAccessor":
        from .items import VariableAccessor

        return VariableAccessor
    elif name == "ItemReference":
        from .items import ItemReference

        return ItemReference
    elif name == "Git":
        from .git import Git

        return Git
    elif name == "Connections":
        from .connections import Connections

        return Connections
    elif name == "Connection":
        from .connections import Connection

        return Connection
    elif name == "Folder":
        from .folders import Folder

        return Folder
    elif name == "FolderAccessor":
        from .folders import FolderAccessor

        return FolderAccessor
    elif name in ("Job", "AzResponse"):
        from .._shared.response import AzResponse, Job

        return AzResponse if name == "AzResponse" else Job
    elif name == "Capacity":
        from .admin import Capacity

        return Capacity
    elif name == "Tenant":
        from .admin import Tenant

        return Tenant
    elif name == "AuthProvider":
        from .._shared.auth import AuthProvider

        return AuthProvider
    elif name == "ServicePrincipalAuth":
        from .._shared.auth import ServicePrincipalAuth

        return ServicePrincipalAuth
    elif name == "FabricAuth":
        from .._shared.auth import FabricAuth

        return FabricAuth

    raise AttributeError(f"module '{__name__}' has no attribute '{name}'")


# =============================================================================
# Exports
# =============================================================================

__all__ = [
    # Configuration
    "client",
    "reset",
    # Module-level functions
    "workspace",
    "workspaces",
    "connection",
    "connections",
    "capacity",
    "capacities",
    "tenant",
    # Classes (for advanced usage / type hints)
    "FabricClient",
    "Workspace",
    "Pipeline",
    "Lakehouse",
    "Notebook",
    "EnvironmentItem",
    "Git",
    "Connections",
    "Connection",
    "Environment",
    "Libraries",
    "VariableLibrary",
    "VariableAccessor",
    "Variable",
    "ItemReference",
    "Folder",
    "FolderAccessor",
    "AzResponse",
    "Job",  # Backward compatibility
    "Capacity",
    "Tenant",
    # Module references
    "enums",
    # Enums - Git
    "GitProviderType",
    "GitCredentialsSource",
    "InitializationStrategy",
    "RequiredAction",
    # Enums - Role Assignments
    "WorkspaceRole",
    "ConnectionRole",
    "PrincipalType",
    # Enums - Connections
    "ConnectivityType",
    "PrivacyLevel",
    "ConnectionEncryption",
    "SingleSignOnType",
    # Enums - Operations
    "OperationStatus",
    # Enums - Items
    "ItemType",
    # Permissions
    "ReadWrite",
    "ItemAccess",
    "MemberType",
    # Notebook formats
    "NotebookFormat",
]

# Save references to API functions so they can be restored if shadowed by submodule imports
_api_functions["workspace"] = workspace
_api_functions["connection"] = connection
_api_functions["capacity"] = capacity
_api_functions["tenant"] = tenant

# =============================================================================
# Module-level accessors (eager instantiation, lazy client creation)
# =============================================================================
# Accessors are lightweight - they only store a reference to _get_client.
# The actual FabricClient isn't created until accessor methods are called.
# This keeps the expensive parts (auth, API calls) lazy while making accessors
# discoverable for documentation tools like mkdocstrings.

from .admin import ActivityEvent, CapacitiesAccessor  # noqa: E402
from .connections import ConnectionsAccessor  # noqa: E402
from .workspace import WorkspaceAccessor  # noqa: E402

workspaces = WorkspaceAccessor(_get_client)
connections = ConnectionsAccessor(_get_client)
capacities = CapacitiesAccessor(_get_client)

# =============================================================================
# Public function aliases
# =============================================================================
# These must be set AFTER all imports to avoid being shadowed by submodules.
# We also need to explicitly remove any submodules that might shadow our functions.

import sys as _sys  # noqa: E402
import types as _types  # noqa: E402

_current_module = _sys.modules[__name__]
for _attr_name in list(vars(_current_module).keys()):
    _attr = getattr(_current_module, _attr_name, None)
    if isinstance(_attr, _types.ModuleType) and _attr_name in {
        "client",
        "workspace",
        "connection",
        "git",
    }:
        delattr(_current_module, _attr_name)

del _sys, _types, _current_module, _attr_name, _attr

client = _create_client
