"""
Microsoft Fabric Workspace model.

Represents a Fabric workspace and provides access to workspace resources.
"""

from typing import TYPE_CHECKING, Callable, Optional, Union, cast

from .._shared.exceptions import FabiasError, NotFoundError
from .._shared.runtime import runtime
from .._shared.utils import GUID
from ._shared.enums import ItemType
from ._shared.role_assignments import BaseRoleAssignment, BaseRoleAssignmentAccessor

if TYPE_CHECKING:
    from ._client import FabricClient
    from .folders import Folder, FolderAccessor
    from .git import Git
    from .items import Environment, Lakehouse, Notebook, Pipeline, VariableLibrary
    from .items.environment import EnvironmentAccessor
    from .items.lakehouse import LakehouseAccessor
    from .items.notebook import NotebookAccessor
    from .items.pipeline import PipelineAccessor
    from .items.variablelibrary import VariableLibraryAccessor
    from .spark import Spark


class Workspace:
    """
    Represents a Microsoft Fabric workspace.

    Provides access to workspace resources including pipelines, lakehouses,
    notebooks, environments, and Git integration. Resolves workspace
    identifiers from display names or GUIDs.

    Attributes:
        id (str): Workspace unique identifier (GUID)
        name (str): Workspace display name
        capacity_id (str): Capacity GUID hosting this workspace
        description (str): Workspace description

    Examples:
        Access via FabricClient:

        >>> client = FabricClient()
        >>> workspace = client.workspace("GENESIS_EXT")
        >>> print(f"Workspace: {workspace.name} ({workspace.id})")

        Access workspace resources:

        >>> pipeline = workspace.pipeline("Daily ETL")
        >>> lakehouse = workspace.lakehouse("Analytics")
        >>> git = workspace.git
    """

    def __init__(
        self,
        client: "FabricClient",
        workspace_id: Optional[str] = None,
        workspace_data: Optional[dict] = None,
    ):
        """
        Initialize workspace.

        Args:
            client: Authenticated FabricClient instance
            workspace_id: Workspace identifier (name, GUID, or None for current)
            workspace_data: Pre-populated workspace data from API (skips resolution)

        Behavior:
            - If workspace_data provided: Pre-populate attributes (no API call)
            - If GUID provided: Store it without API call (lazy load properties)
            - If name provided: Resolve immediately to get GUID (required for operations)
            - If None or 'default': Resolve to current workspace (Fabric only)

        Raises:
            NotFoundError: If workspace name cannot be found
            FabiasError: If not in Fabric and no workspace_id provided
        """
        self._client = client
        self._workspace_id = workspace_id
        self._data_fetched = False

        # Data attributes (lazy loaded unless resolved by name or pre-populated)
        self._id: Optional[str] = None
        self._name: Optional[str] = None
        self._capacity_id: Optional[str] = None
        self._description: Optional[str] = None

        # Cached sub-objects
        self._git: Optional["Git"] = None
        self._spark: Optional["Spark"] = None
        self._items_cache: dict = {}

        # Pre-populate from provided data
        if workspace_data:
            self._id = workspace_data.get("id")
            self._name = workspace_data.get("displayName") or workspace_data.get("name")
            self._capacity_id = workspace_data.get("capacityId")
            self._description = workspace_data.get("description")
            self._data_fetched = True
            return

        # If GUID provided, store it without API call
        if workspace_id and GUID.valid(workspace_id):
            self._id = workspace_id
            return

        # If name provided (or None/default), resolve immediately to get GUID
        # We need GUID for all subsequent operations
        self._resolve_identifier(workspace_id)

    def _resolve_identifier(self, workspace_id: Optional[str]) -> None:
        """
        Resolve workspace identifier to get the GUID.

        Called when a name is provided (or None/default).
        Populates all data since we're already making an API call.

        Args:
            workspace_id: Identifier to resolve
        """
        # Handle None or "default" - use current workspace
        if not workspace_id or workspace_id.strip().lower() == "default":  # pragma: no cover
            if not runtime.isFabric:
                raise FabiasError(
                    "Cannot determine current workspace outside Fabric. "
                    "Please provide a workspace ID or name."
                )

            # Get from runtime context
            workspace_id = runtime.context.get("workspace_id")

        if not workspace_id:
            raise FabiasError("Could not determine workspace ID")

        # If it happens to be a GUID at this point, just store it
        if GUID.valid(workspace_id):
            self._id = workspace_id
            return

        # Name-based resolution: try admin API first (if credentials available)
        if self._client.hasExplicitCredentials:
            try:
                response = self._client.get(f"/admin/workspaces?name={workspace_id}")
                data = response.json()
                workspaces = data.get("workspaces", [])

                if workspaces:
                    workspace = workspaces[0]
                    if workspace.get("name") == workspace_id:
                        self._id = workspace.get("id")
                        self._name = workspace.get("name")
                        self._capacity_id = workspace.get("capacityId")
                        self._description = workspace.get("description")
                        self._data_fetched = True
                        return
            except Exception:
                pass  # Fall through to list search

        # Fallback: use client.workspaces() which handles sempy vs API internally
        workspaces = self._client.workspaces()

        matches = [w for w in workspaces if w.name == workspace_id or w.id == workspace_id]

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

        if len(matches) > 1:
            raise FabiasError(f"Multiple workspaces found with name '{workspace_id}'")

        matched = matches[0]
        self._id = matched.id
        self._name = matched.name
        self._capacity_id = matched.capacityId
        self._description = matched.description
        self._data_fetched = True

    def _fetch(self) -> None:
        """Lazy load workspace data from API if not already fetched."""
        if self._data_fetched or not self._id:
            return

        response = self._client.get(f"/workspaces/{self._id}")
        data = response.json()
        self._name = data.get("displayName")
        self._capacity_id = data.get("capacityId")
        self._description = data.get("description")
        self._data_fetched = True

    @property
    def id(self) -> Optional[str]:
        """Workspace GUID."""
        return self._id

    def _get_id(self) -> str:
        """Get workspace ID with assertion (for internal use)."""
        if self._id is None:
            raise FabiasError("Workspace ID not set - workspace not properly initialized")
        return self._id

    @property
    def name(self) -> Optional[str]:
        """Workspace display name (lazy loaded)."""
        self._fetch()
        return self._name

    @property
    def capacityId(self) -> Optional[str]:
        """Capacity GUID hosting this workspace (lazy loaded)."""
        self._fetch()
        return self._capacity_id

    @property
    def description(self) -> Optional[str]:
        """Workspace description (lazy loaded)."""
        self._fetch()
        return self._description

    def delete(self) -> None:  # pragma: no cover
        """
        Delete this workspace.

        Warning: This operation is permanent and cannot be undone.
        All items in the workspace will be deleted.

        Example:
            >>> ws = fabric.workspace("Old Workspace")
            >>> ws.delete()
        """
        if not self._id:
            raise FabiasError("Cannot delete workspace: ID not resolved")

        self._client.delete(f"/workspaces/{self._id}")

        # Clear local state since workspace no longer exists
        self._id = None
        self._name = None
        self._capacity_id = None
        self._description = None
        self._data_fetched = False

    @property
    def spark(self) -> "Spark":
        """
        Get Spark settings handler for this workspace.

        Returns:
            Spark: Spark settings handler

        Examples:
            Get current Spark settings:

            >>> settings = workspace.spark.settings()
            >>> print(f"Auto log: {settings.automatic_log}")
            >>> print(f"Pool max nodes: {settings.pool_max_nodes}")

            Update Spark settings:

            >>> workspace.spark.settings.update(
            ...     automatic_log=True,
            ...     pool={"starterPool": {"maxNodeCount": 10, "maxExecutors": 5}},
            ... )
        """
        if self._spark is None:
            from .spark import Spark

            self._spark = Spark(self._client, self._get_id())
        return self._spark

    @property
    def git(self) -> "Git":
        """
        Get Git integration handler for this workspace.

        Returns:
            Git: Git operations handler

        Examples:
            >>> git = workspace.git
            >>> status = git.status()
            >>> if status.changes:
            ...     git.update_from_git()
        """
        if self._git is None:
            from .git import Git

            self._git = Git(self._client, self._get_id())
        return self._git

    def roleAssignment(self, principal_id: str) -> "WorkspaceRoleAssignment":
        """
        Get a specific role assignment by principal ID.

        This method accepts a principal ID and looks up the corresponding
        role assignment. If multiple or no assignments are found, raises an error.

        Args:
            principal_id: Principal GUID (user, group, or service principal)

        Returns:
            WorkspaceRoleAssignment: The role assignment

        Raises:
            NotFoundError: If no role assignment found for the principal
            FabiasError: If multiple role assignments found (shouldn't happen)

        Examples:
            >>> ws = fabric.workspace("GENESIS")
            >>> assignment = ws.roleAssignment("user-guid")
            >>> print(assignment.role)
        """
        # List all assignments to find the one with matching principal_id
        assignments = self.roleAssignments()

        # Find assignment with matching principal ID
        matches = [a for a in assignments if a.principalId == principal_id]

        if not matches:
            from .._shared.exceptions import NotFoundError

            raise NotFoundError(
                f"No role assignment found for principal {principal_id}",
                resource_type="WorkspaceRoleAssignment",
                resource_id=principal_id,
            )

        if len(matches) > 1:
            from .._shared.exceptions import FabiasError

            raise FabiasError(
                f"Multiple role assignments found for principal {principal_id}. "
                "This should not happen."
            )

        return cast("WorkspaceRoleAssignment", matches[0])

    @property
    def roleAssignments(self) -> "WorkspaceRoleAssignmentAccessor":
        """
        Access role assignments for this workspace.

        Returns:
            WorkspaceRoleAssignmentAccessor: Accessor for listing and managing role assignments

        Examples:
            List role assignments:

            >>> ws = fabric.workspace("GENESIS")
            >>> for assignment in ws.roleAssignments():
            ...     print(f"{assignment.principalName}: {assignment.role}")

            Add role assignment:

            >>> ws.roleAssignments.add(
            ...     principal_id="user-guid",
            ...     principal_type="User",
            ...     role="Admin"
            ... )
        """
        return WorkspaceRoleAssignmentAccessor(self._client, self._get_id())

    # =============================================================================
    # Items
    # =============================================================================

    def pipeline(self, identifier: str) -> "Pipeline":
        """
        Get a specific pipeline by name or ID.

        Args:
            identifier: Pipeline name or GUID

        Returns:
            Pipeline: The pipeline object

        Examples:
            >>> pipeline = workspace.pipeline("Daily ETL")
            >>> job = pipeline.run()
        """
        from .items.pipeline import Pipeline

        return Pipeline(self._client, self._get_id(), identifier)

    @property
    def pipelines(self) -> "PipelineAccessor":
        """
        Access pipeline list and creation operations.

        Returns:
            PipelineAccessor: Accessor for listing and creating pipelines

        Examples:
            List all pipelines:

            >>> for pipeline in workspace.pipelines():
            ...     print(pipeline.name)

            Create new pipeline:

            >>> pipeline = workspace.pipelines.add("New ETL")
        """
        from .items.pipeline import PipelineAccessor

        return PipelineAccessor(self._client, self._get_id())

    def lakehouse(self, identifier: str) -> "Lakehouse":
        """
        Get a specific lakehouse by name or ID.

        Args:
            identifier: Lakehouse name or GUID

        Returns:
            Lakehouse: The lakehouse object

        Examples:
            >>> lakehouse = workspace.lakehouse("Analytics")
        """
        from .items.lakehouse import Lakehouse

        return Lakehouse(self._client, self._get_id(), identifier)

    @property
    def lakehouses(self) -> "LakehouseAccessor":
        """
        Access lakehouse list and creation operations.

        Returns:
            LakehouseAccessor: Accessor for listing and creating lakehouses

        Examples:
            List all lakehouses:

            >>> for lh in workspace.lakehouses():
            ...     print(lh.name)

            Create new lakehouse:

            >>> lakehouse = workspace.lakehouses.add("Data Lake")
        """
        from .items.lakehouse import LakehouseAccessor

        return LakehouseAccessor(self._client, self._get_id())

    def notebook(self, identifier: str) -> "Notebook":
        """
        Get a specific notebook by name or ID.

        Args:
            identifier: Notebook name or GUID

        Returns:
            Notebook: The notebook object

        Examples:
            >>> notebook = workspace.notebook("ETL Script")
        """
        from .items.notebook import Notebook

        return Notebook(self._client, self._get_id(), identifier)

    @property
    def notebooks(self) -> "NotebookAccessor":
        """
        Access notebook list and creation operations.

        Returns:
            NotebookAccessor: Accessor for listing and creating notebooks

        Examples:
            List all notebooks:

            >>> for nb in workspace.notebooks():
            ...     print(nb.name)

            Create new notebook:

            >>> notebook = workspace.notebooks.add("New Analysis")
        """
        from .items.notebook import NotebookAccessor

        return NotebookAccessor(self._client, self._get_id())

    def environment(self, identifier: str) -> "Environment":
        """
        Get a specific environment by name or ID.

        Args:
            identifier: Environment name or GUID

        Returns:
            Environment: The environment object

        Examples:
            >>> env = workspace.environment("ML Environment")
        """
        from .items.environment import Environment

        return Environment(self._client, self._get_id(), identifier)

    @property
    def environments(self) -> "EnvironmentAccessor":
        """
        Access environment list and creation operations.

        Returns:
            EnvironmentAccessor: Accessor for listing and creating environments

        Examples:
            List all environments:

            >>> for env in workspace.environments():
            ...     print(env.name)

            Create new environment:

            >>> env = workspace.environments.add("Data Science")
        """
        from .items.environment import EnvironmentAccessor

        return EnvironmentAccessor(self._client, self._get_id())

    def variableLibrary(self, identifier: str) -> "VariableLibrary":
        """
        Get a specific variable library by name or ID.

        Args:
            identifier: Variable library name or GUID

        Returns:
            VariableLibrary: The variable library object

        Examples:
            >>> lib = workspace.variableLibrary("Deployment Config")
            >>> print(f"Active value set: {lib.active_value_set}")
        """
        from .items.variablelibrary import VariableLibrary

        return VariableLibrary(self._client, self._get_id(), identifier)

    @property
    def variableLibraries(self) -> "VariableLibraryAccessor":
        """
        Access variable library list and creation operations.

        Returns:
            VariableLibraryAccessor: Accessor for listing and creating variable libraries

        Examples:
            List all variable libraries:

            >>> for lib in workspace.variableLibraries():
            ...     print(f"{lib.name}: active={lib.active_value_set}")

            Create new variable library:

            >>> lib = workspace.variableLibraries.add("Deployment Config")

            Create with initial variables:

            >>> import base64, json
            >>> variables_json = {
            ...     "$schema": "...",
            ...     "variables": [{"name": "env", "type": "String", "value": "dev"}]
            ... }
            >>> lib = workspace.variableLibraries.add(
            ...     "Deployment Config",
            ...     definition={
            ...         "format": "VariableLibraryV1",
            ...         "parts": [{
            ...             "path": "variables.json",
            ...             "payload": base64.b64encode(
            ...                 json.dumps(variables_json).encode()
            ...             ).decode(),
            ...             "payloadType": "InlineBase64"
            ...         }]
            ...     }
            ... )
        """
        from .items.variablelibrary import VariableLibraryAccessor

        return VariableLibraryAccessor(self._client, self._get_id())

    # =============================================================================
    # Folders
    # =============================================================================

    def folder(self, identifier: str) -> "Folder":
        """
        Get a specific folder by name or ID.

        Args:
            identifier: Folder display name or GUID

        Returns:
            Folder: The folder object

        Examples:
            >>> folder = workspace.folder("Sales")
            >>> print(f"{folder.name} (parent: {folder.parent})")
        """
        from .folders import Folder

        return Folder(self._client, self._get_id(), identifier)

    @property
    def folders(self) -> "FolderAccessor":
        """
        Access folder list and creation operations.

        Returns:
            FolderAccessor: Accessor for listing and creating folders

        Examples:
            List all folders:

            >>> for f in workspace.folders():
            ...     print(f"{f.name} (parent: {f.parent})")

            List direct children of a folder:

            >>> children = workspace.folders(root_folder_id=parent.id, recursive=False)

            Create new folder:

            >>> folder = workspace.folders.add("Reports")

            Create nested folder:

            >>> child = workspace.folders.add("Q1", parent=parent.id)
        """
        from .folders import FolderAccessor

        return FolderAccessor(self._client, self._get_id())

    # =============================================================================
    # Items
    # =============================================================================

    def items(self, item_type: Optional[Union[ItemType, str]] = None) -> list:
        """
        List items in this workspace, converted to specific types.

        Returns typed item objects (Pipeline, Notebook, etc.) based on the
        'type' field in the API response. Unknown types return base Item.

        Args:
            item_type: Optional filter by type. Can be ItemType enum or string
                (e.g., ItemType.DATA_PIPELINE, "DataPipeline", ItemType.NOTEBOOK, "Notebook")

        Returns:
            list: List of Item objects (Pipeline, Notebook, etc.)

        Examples:
            All items:

            >>> items = workspace.items()
            >>> for item in items:
            ...     print(f"{item.name}: {type(item).__name__}")

            Filtered by type:

            >>> pipelines = workspace.items(item_type="DataPipeline")
            >>> notebooks = workspace.items(item_type="Notebook")
        """
        from .items import fromJson

        # Build endpoint with optional type filter
        endpoint = f"/workspaces/{self.id}/items"
        if item_type:
            # Convert enum to string if needed
            type_str = item_type.value if isinstance(item_type, ItemType) else item_type
            endpoint = f"{endpoint}?type={type_str}"

        # Get all items (pagination handled automatically by client)
        response = self._client.get(endpoint)
        data = response.json()

        # Convert JSON to typed objects
        return [
            fromJson(self._client, self._get_id(), item_data) for item_data in data.get("value", [])
        ]

    @classmethod
    def create(  # pragma: no cover
        cls, client: "FabricClient", name: str, capacity: str, description: Optional[str] = None
    ) -> "Workspace":
        """
        Create a new workspace.

        Args:
            client: Authenticated FabricClient instance
            name: Display name for the new workspace
            capacity: Capacity GUID to host the workspace
            description: Optional workspace description

        Returns:
            Workspace: The newly created workspace

        Examples:
            >>> from fabias.fabric import FabricClient, Workspace
            >>> client = FabricClient(auth=auth)
            >>> ws = Workspace.create(client, "New Workspace", capacity="...")
        """
        payload = {"displayName": name, "capacityId": capacity}
        if description is not None:
            payload["description"] = description

        try:
            response = client.post("/workspaces", data=payload)

            if response.status_code == 202:
                operation_id = response.headers.get("x-ms-operation-id")
                raise FabiasError(
                    f"Workspace creation started asynchronously (operation: {operation_id}). "
                    "Polling not yet implemented."
                )

            workspace_data = response.json()
            workspace_id = workspace_data.get("id")

            # Return new Workspace instance with resolved ID
            return cls(client, workspace_id=workspace_id)
        except Exception as e:
            # If workspace already exists (409), fetch and return it
            from .._shared.exceptions import ApiError

            if (
                (isinstance(e, ApiError) and e.status_code == 409)
                or "409" in str(e)
                or "already exists" in str(e).lower()
            ):
                # Search for existing workspace by name
                workspaces = client.workspaces()
                matches = [w for w in workspaces if w.name == name]
                if matches:
                    return matches[0]
            raise

    def __repr__(self) -> str:
        """Return string representation."""
        return f"Workspace(id={self.id}, name={self.name!r})"

    def __getattr__(self, name: str) -> Callable:
        """
        Dynamic attribute access for undefined endpoints.

        Allows accessing Fabric API endpoints that don't have explicit methods.
        Converts attribute name to API endpoint and returns a callable or data.

        Args:
            name: Attribute name (will be converted to API endpoint)

        Returns:
            Callable: A function that accepts an optional item_id parameter

        Examples:
            # List eventhouses (no explicit method defined)
            >>> eventhouses = workspace.eventhouses()

            # Get specific eventhouse
            >>> eh = workspace.eventhouse("some-id")

            # Access any Fabric item type
            >>> warehouses = workspace.warehouses()
            >>> kqlDatabases = workspace.kqlDatabases()
        """
        # Don't intercept private attributes or known properties
        if name.startswith("_"):
            raise AttributeError(name)

        def dynamic_item_accessor(item_id: Optional[str] = None):
            """
            Access items of a given type.

            If item_id is provided, gets that specific item.
            Otherwise, lists all items of this type.
            """
            # Handle singular vs plural naming
            # e.g., "eventhouse" -> get one, "eventhouses" -> list all
            if item_id is not None:
                # Get specific item: /workspaces/{id}/{type}/{item_id}
                endpoint = f"/workspaces/{self.id}/{name}/{item_id}"
                response = self._client.get(endpoint)
                return response.json()
            else:
                # List items: /workspaces/{id}/{type}
                endpoint = f"/workspaces/{self.id}/{name}"
                response = self._client.get(endpoint)
                data = response.json()
                # Most Fabric list endpoints return {"value": [...]}
                if "value" in data:
                    return data["value"]
                return data

        return dynamic_item_accessor


class WorkspaceRoleAssignment(BaseRoleAssignment):
    """
    Represents a role assignment for a workspace.

    Inherits from BaseRoleAssignment to provide:
    - principal (dict): Principal information (user/group/service principal)
    - role (str): Assigned role (e.g., 'Admin', 'Member', 'Contributor', 'Viewer')
    - principal_id, principal_type, principal_name properties

    Workspace-specific roles:
    - Admin: Full control over the workspace
    - Member: Can view, edit, and share workspace items
    - Contributor: Can view and edit workspace items
    - Viewer: Read-only access to workspace items
    """

    pass


class WorkspaceRoleAssignmentAccessor(BaseRoleAssignmentAccessor):
    """
    Accessor for workspace role assignment operations.

    Inherits from BaseRoleAssignmentAccessor to provide:
    - __call__(): List all role assignments
    - add(principal_id, principal_type, role): Add role assignment (with 409 handling)
    - update(principal_id, role): Update role assignment
    - delete(principal_id): Delete role assignment
    """

    def __init__(self, client: "FabricClient", workspace_id: str):
        """Initialize role assignment accessor for a workspace."""
        endpoint = f"/workspaces/{workspace_id}/roleAssignments"
        super().__init__(client, endpoint, WorkspaceRoleAssignment)


class WorkspaceAccessor:
    """
    Accessor for workspace-level operations.

    Provides methods to list and create workspaces.
    Follows the same pattern as PipelineAccessor, LakehouseAccessor, etc.

    Supports lazy client loading for module-level usage.
    """

    def __init__(self, client_or_getter: Union["FabricClient", Callable[[], "FabricClient"]]):
        """
        Initialize workspace accessor.

        Args:
            client_or_getter: Either a FabricClient instance or a callable that returns one
        """
        if callable(client_or_getter):
            self._client_getter: Optional[Callable[[], "FabricClient"]] = client_or_getter
            self._client: Optional["FabricClient"] = None
        else:
            self._client = client_or_getter
            self._client_getter = None

    def _get_client(self) -> "FabricClient":
        """Get client, lazily loading if needed."""
        if self._client is None:
            if self._client_getter is None:
                raise RuntimeError("No client or client getter available")
            self._client = self._client_getter()
        return self._client

    def __call__(self) -> list["Workspace"]:
        """
        List all accessible workspaces.

        Returns:
            list[Workspace]: List of Workspace objects

        Examples:
            >>> import fabias.fabric as fabric
            >>> for ws in fabric.workspaces():
            ...     print(f"{ws.name}: {ws.id}")
        """
        return self._get_client().workspaces()

    def add(
        self, name: str, capacity: str, description: Optional[str] = None
    ) -> Workspace:  # pragma: no cover
        """
        Create a new workspace.

        Args:
            name: Display name for the new workspace
            capacity: Capacity GUID to host the workspace
            description: Optional workspace description

        Returns:
            Workspace: The newly created workspace

        Examples:
            >>> import fabias.fabric as fabric
            >>> new_ws = fabric.workspaces.add("Analytics WS", capacity="...")
        """
        try:
            return Workspace.create(self._get_client(), name, capacity, description)
        except Exception as e:
            # If workspace already exists (409), fetch and return it
            from .._shared.exceptions import ApiError

            if (
                (isinstance(e, ApiError) and e.status_code == 409)
                or "409" in str(e)
                or "already exists" in str(e).lower()
            ):
                workspaces = self._get_client().workspaces()
                matches = [w for w in workspaces if w.name == name]
                if matches:
                    return matches[0]
            raise

    def delete(self, name_or_id: str) -> None:  # pragma: no cover
        """
        Delete a workspace by name or GUID.

        Warning: This operation is permanent and cannot be undone.
        All items in the workspace will be deleted.

        Args:
            name_or_id: Workspace name or GUID

        Examples:
            >>> fabric.workspaces.delete("Old Workspace")
            >>> fabric.workspaces.delete("abc-123-guid")
        """
        from . import workspace as get_workspace

        # Get the workspace (this resolves name to ID if needed)
        ws = get_workspace(name_or_id)

        if not ws.id:
            from .._shared.exceptions import FabiasError

            raise FabiasError(f"Cannot delete workspace: ID not resolved for '{name_or_id}'")

        self._get_client().delete(f"/workspaces/{ws.id}")
