import json
from time import sleep

from msfabricpysdkcore.client import FabricClient

class FabricClientCore(FabricClient):
    """FabricClientCore class to interact with Fabric Core APIs"""

    def __init__(self, tenant_id = None, client_id = None, client_secret = None, silent=False) -> None:
        """Initialize FabricClientCore object"""
        super().__init__(tenant_id, client_id, client_secret, silent=silent)

    def long_running_operation(self, response_headers):
        """Check the status of a long running operation"""
        from msfabricpysdkcore.long_running_operation import check_long_running_operation

        return check_long_running_operation(response_headers, self)

    ### Capacities

    def get_capacity(self, capacity_id = None, capacity_name = None):
        """Get a capacity
        
        Args:
            capacity_id (str): The ID of the capacity
            capacity_name (str): The name of the capacity
            
        Returns:
            Capacity: The capacity object
            
        Raises:
            ValueError: If no capacity is found
        """
        if capacity_id is None and capacity_name is None:
            raise ValueError("Either capacity_id or capacity_name must be provided")
        caps = self.list_capacities()
        for cap in caps:
            if capacity_id and cap.id == capacity_id:
                return cap
            if capacity_name and cap.display_name == capacity_name:
                return cap
        raise ValueError("No capacity found") 
    
    def list_capacities(self):
        """List all capacities in the tenant
        Returns:
            list: The list of capacities
        """
        from msfabricpysdkcore.capacity import Capacity
        url = "https://api.fabric.microsoft.com/v1/capacities"

        items = self.calling_routine(url, operation="GET", response_codes=[200, 429], error_message="Error listing capacities", return_format="value_json", paging=True)

        items = [Capacity.from_dict(i) for i in items]
        return items
    
    # Deployment Pipelines

    def deploy_stage_content(self, deployment_pipeline_id, source_stage_id, target_stage_id, created_workspace_details = None,
               items = None, note = None, wait_for_completion = True):
        """Deploy stage content
        Args:
            deployment_pipeline_id (str): The ID of the deployment pipeline
            source_stage_id (str): The ID of the source stage
            target_stage_id (str): The ID of the target stage
            created_workspace_details (list): A list of created workspace details
            items (list): A list of items
            note (str): A note
            wait_for_completion (bool): Whether to wait for the deployment to complete
        Returns:
            Details about the dpeloyment
        """
        
        # POST https://api.fabric.microsoft.com/v1/deploymentPipelines/{deploymentPipelineId}/deploy

        url = f"https://api.fabric.microsoft.com/v1/deploymentPipelines/{deployment_pipeline_id}/deploy"

        body = {
            "sourceStageId": source_stage_id,
            "targetStageId": target_stage_id
        }

        if created_workspace_details:
            body["createdWorkspaceDetails"] = created_workspace_details
        if items:
            body["items"] = items
        if note:
            body["note"] = note
            
        json_operation_result = self.calling_routine(url, operation="POST", body=body, response_codes=[200, 202, 429], error_message="Error deploying stage content", 
                                                     return_format="json+operation_result", wait_for_completion=wait_for_completion)

        return json_operation_result
    
    def get_deployment_pipeline(self, deployment_pipeline_id):
        """Get a deployment pipeline
        Args:
            deployment_pipeline_id (str): The ID of the deployment pipeline
        Returns:
            DeploymentPipeline: The deployment pipeline
        """
        from msfabricpysdkcore.deployment_pipeline import DeploymentPipeline
        url = f"https://api.fabric.microsoft.com/v1/deploymentPipelines/{deployment_pipeline_id}"

        result_json = self.calling_routine(url, operation="GET", response_codes=[200, 429], error_message="Error getting deployment pipeline", return_format="json")

        return DeploymentPipeline.from_dict(result_json, self)
    
    def get_deployment_pipeline_stages_items(self, pipeline_id, stage_id = None, stage_name = None):
        print("DEPRECATED: Use list_deployment_pipeline_stages_items instead")
        return self.list_deployment_pipeline_stages_items(pipeline_id, stage_id, stage_name)

    def list_deployment_pipeline_stages_items(self, deployment_pipeline_id, stage_id = None, stage_name = None):
        """List the items in a deployment stage
        Args:
            pipeline_id (str): The ID of the deployment pipeline
            stage_id (str): The ID of the deployment stage
            stage_name (str): The name of the deployment stage
        Returns:
            list: List of DeploymentStageItem objects
        """

        if stage_id == None and stage_name == None:
            raise Exception("Please provide either stage_id or stage_name")

        if stage_id is None:
            stages = self.list_deployment_pipeline_stages(deployment_pipeline_id)
            dep_pip_stages = [stage for stage in stages if stage.display_name == stage_name]
            if len(dep_pip_stages) == 0:
                raise Exception(f"Stage with name {stage_name} not found")
            stage_id = dep_pip_stages[0].id

        url = f"https://api.fabric.microsoft.com/v1/deploymentPipelines/{deployment_pipeline_id}/stages/{stage_id}/items"

        items = self.calling_routine(url, operation="GET", response_codes=[200, 429],
                                     error_message="Error getting deployment pipeline stage items", return_format="value_json", paging=True)

        return items
    
    def get_deployment_pipeline_stages(self, pipeline_id):
        """Get the stages of a deployment pipeline
        Args:
            pipeline_id (str): The ID of the deployment pipeline
        Returns:
            list: List of DeploymentPipelineStage objects
        """
        print("DEPRECATED: Use list_deployment_pipeline_stages instead")
        return self.list_deployment_pipeline_stages(pipeline_id)
    
    def list_deployment_pipeline_stages(self, deployment_pipeline_id):
        """Get the stages of a deployment pipeline
        Args:
            pipeline_id (str): The ID of the deployment pipeline
        Returns:
            list: List of DeploymentPipelineStage objects
        """
        from msfabricpysdkcore.deployment_pipeline import Deployment_Pipeline_Stage

        url = f"https://api.fabric.microsoft.com/v1/deploymentPipelines/{deployment_pipeline_id}/stages"

        items = self.calling_routine(url, operation="GET", response_codes=[200, 429],
                                      error_message="Error getting deployment pipeline stages", return_format="value_json", paging=True)

        for item in items:
            item["deploymentPipelineId"] = deployment_pipeline_id
        stages = [Deployment_Pipeline_Stage.from_dict(item, self) for item in items]
        
        return stages

    def list_deployment_pipelines(self):
        """List deployment pipelines
        Returns:
            list: List of DeploymentPipeline objects
        """
        from msfabricpysdkcore.deployment_pipeline import DeploymentPipeline

        url = "https://api.fabric.microsoft.com/v1/deploymentPipelines"

        items = self.calling_routine(url, operation="GET", response_codes=[200, 429],
                                     error_message="Error listing deployment pipelines", return_format="value_json", paging=True)


        dep_pipes = [DeploymentPipeline.from_dict(i, core_client=self) for i in items]

        return dep_pipes

    # External Data Shares

    # create

    def create_external_data_share(self, workspace_id, item_id, paths, recipient):
        """Create an external data share in an item
        Args:
            workspace_id (str): The ID of the workspace
            item_id (str): The ID of the item
            paths (list): The paths to share
            recipient (str): The recipient of the share
        Returns:
            dict: The external data share
        """
        
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/items/{item_id}/externalDataShares"

        body = {
            'paths': paths,
            'recipient': recipient
        }
        response_json = self.calling_routine(url, operation="POST", body=body, response_codes=[201, 429],
                                             error_message="Error creating external data share", return_format="json")
        return response_json

    def get_external_data_share(self, workspace_id, item_id, external_data_share_id):
        """Get an external data share in an item
        Args:
            workspace_id (str): The ID of the workspace
            item_id (str): The ID of the item
            external_data_share_id (str): The ID of the external data share
        Returns:
            dict: The external data share
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/items/{item_id}/externalDataShares/{external_data_share_id}"
        response_json = self.calling_routine(url, operation="GET", response_codes=[200, 429],
                                             error_message="Error getting external data share", return_format="json")
        return response_json
    
    # list

    def list_external_data_shares_in_item(self, workspace_id, item_id):
        """List external data shares in an item
        Args:
            workspace_id (str): The ID of the workspace
            item_id (str): The ID of the item
        Returns:
            list: The list of external data shares
        """

        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/items/{item_id}/externalDataShares"

        items = self.calling_routine(url, operation="GET", response_codes=[200, 429],
                                     error_message="Error listing external data shares", return_format="value_json", paging=True)
        return items
    
    # revoke

    def revoke_external_data_share(self, workspace_id, item_id, external_data_share_id):
        """Revoke an external data share in an item
        Args:
            workspace_id (str): The ID of the workspace
            item_id (str): The ID of the item
            external_data_share_id (str): The ID of the external data share
        Returns:
            int: The status code of the response
        """

        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/items/{item_id}/externalDataShares/{external_data_share_id}/revoke"

        response = self.calling_routine(url, operation="POST", response_codes=[200, 429], error_message="Error revoking external data share", return_format="response")
        return response.status_code
    
    # Git
    
    def commit_to_git(self, workspace_id, mode, comment=None, items=None, workspace_head=None):
        # POST https://api.fabric.microsoft.com/v1/workspaces/{workspaceId}/git/commitToGit
        """Commit to git
        Args:
            workspace_id (str): The ID of the workspace
            mode (str): The mode of the commit
            comment (str): The comment of the commit
            items (list): The list of items
            workspace_head (str): The workspace head
        Returns:
            int: The status code of the response
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/git/commitToGit"

        body = {
            'mode': mode
        }

        if comment:
            body['comment'] = comment
        if items:
            body['items'] = items
        if workspace_head:
            body['workspaceHead'] = workspace_head

        response = self.calling_routine(url=url, operation="POST", body=body,
                                        response_codes=[200, 202, 429],
                                        error_message="Error committing to git", return_format="response")

        return response.status_code

    def git_connect(self, workspace_id, git_provider_details):
        """Connect git
        Args:
            workspace_id (str): The ID of the workspace
            git_provider_details (dict): The git provider details
        Returns:
            int: The status code of the response
        """
        # POST https://api.fabric.microsoft.com/v1/workspaces/{workspaceId}/git/connect
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/git/connect"

        payload = {
            'gitProviderDetails': git_provider_details
        }

        response = self.calling_routine(url=url, operation="POST", body=payload,
                                        response_codes=[200, 202, 429],
                                        error_message="Error connecting git", return_format="response")

        return response.status_code

    def git_disconnect(self, workspace_id):
        """Disconnect git
        Args:
            workspace_id (str): The ID of the workspace
        Returns:
            int: The status code of the response
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/git/disconnect"

        response = self.calling_routine(url=url, operation="POST", response_codes=[200, 204, 429],
                                        error_message="Error disconnecting git", return_format="response")
        return response.status_code

    def git_get_connection(self, workspace_id):
        """Get git connection info
        Args:
            workspace_id (str): The ID of the workspace
        Returns:
            dict: The git connection info
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/git/connection"

        response_json = self.calling_routine(url=url, operation="GET", response_codes=[200, 429],
                                             error_message="Error getting git connection info", return_format="json")

        return response_json

    def git_get_status(self, workspace_id):
        """Get git connection status
        Args:
            workspace_id (str): The ID of the workspace
        Returns:
            dict: The git connection status
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/git/status"

        response_json = self.calling_routine(url=url, operation="GET", response_codes=[200, 429],
                                             error_message="Error getting git connection status",
                                             return_format="json")

        return response_json

    def git_initialize_connection(self, workspace_id, initialization_strategy):
    #        POST https://api.fabric.microsoft.com/v1/workspaces/{workspaceId}/git/initializeConnection
        """Initialize git connection
        Args:
            workspace_id (str): The ID of the workspace
            initialization_strategy (dict): The initialization strategy
        Returns:
            int: The status code of the response
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/git/initializeConnection"

        body = {'initializeGitConnectionRequest':initialization_strategy}
        
        response = self.calling_routine(url=url, operation="POST", body=body, response_codes=[200, 202, 429],
                                        error_message="Error initializing connection", return_format="response")
        
        return response.status_code
    
    def update_from_git(self, workspace_id, remote_commit_hash, conflict_resolution = None,
                        options = None, workspace_head = None):
        # POST https://api.fabric.microsoft.com/v1/workspaces/{workspaceId}/git/updateFromGit
        """Update from git"""
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/git/updateFromGit"

        body = {
            "remoteCommitHash" : remote_commit_hash
        }

        if conflict_resolution:
            body['conflictResolution'] = conflict_resolution
        if options:
            body['options'] = options
        if workspace_head:
            body['workspaceHead'] = workspace_head

        response = self.calling_routine(url=url, operation="POST", body=body,
                                        response_codes=[200, 202, 429],
                                        error_message="Error updating from git", return_format="response")

        return response.status_code


    # Items

    # Helper Functions

    def get_item_by_name(self, workspace_id, item_name, item_type):
        """Get an item from a workspace by name
        Args:
            workspace_id (str): The ID of the workspace
            item_name (str): The name of the item
            item_type (str): The type of the item
        Returns:
            Item: The item object
        """
        ws_items = self.list_items(workspace_id = workspace_id, with_properties=False)
        for item in ws_items:
            if item.display_name == item_name and item.type == item_type:
                return self.get_item(workspace_id, item.id, item_type)    


    
    def get_item_specific(self, workspace_id, item_dict):
        """Get the item object based on the type
        Args:
            workspace_id (str): The ID of the workspace
            item_dict (dict): The dictionary representing the item
        Returns:
            Item: The item object
        """
        from msfabricpysdkcore.item import Item

        if item_dict["type"] == "DataPipeline":
            return self.get_data_pipeline(workspace_id, item_dict["id"])
        if item_dict["type"] == "Eventstream":
            return self.get_eventstream(workspace_id, item_dict["id"])
        if item_dict["type"] == "Eventhouse":
            return self.get_eventhouse(workspace_id, item_dict["id"])
        if item_dict["type"] == "KQLDatabase":
            return self.get_kql_database(workspace_id, item_dict["id"])
        if item_dict["type"] == "KQLQueryset":
            return self.get_kql_queryset(workspace_id, item_dict["id"])
        if item_dict["type"] == "Lakehouse":
            return self.get_lakehouse(workspace_id, item_dict["id"])
        if item_dict["type"] == "MLExperiment":
            return self.get_ml_experiment(workspace_id, item_dict["id"])
        if item_dict["type"] == "MLModel":
            return self.get_ml_model(workspace_id, item_dict["id"])
        if item_dict["type"] == "Notebook":
            return self.get_notebook(workspace_id, item_dict["id"])
        if item_dict["type"] == "Report":
            return self.get_report(workspace_id, item_dict["id"])
        if item_dict["type"] == "SemanticModel":
            return self.get_semantic_model(workspace_id, item_dict["id"])
        if item_dict["type"] == "SparkJobDefinition":
            return self.get_spark_job_definition(workspace_id, item_dict["id"])
        if item_dict["type"] == "Warehouse":
            return self.get_warehouse(workspace_id, item_dict["id"])
        if item_dict["type"] == "Environment":
            return self.get_environment(workspace_id, item_dict["id"])

        item_obj = Item.from_dict(item_dict, core_client=self)
        return item_obj

    def get_item_object_w_properties(self, workspace_id, item_list):
        """Get the item object with properties
        Args:
            workspace_id (str): The ID of the workspace
            item_list (list): The list of items
        Returns:
            list: The list of item objects
        """

        new_item_list = [self.get_item_specific(workspace_id, item)  for item in item_list]
        return new_item_list
    
    # Create
  
    def create_item(self, workspace_id, display_name, type, definition = None, description = None, wait_for_completion = True, **kwargs):
        """Create an item in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            display_name (str): The display name of the item
            type (str): The type of the item
            definition (dict): The definition of the item
            description (str): The description of the item
            kwargs: Additional arguments
        Returns:
            Item: The created item
        """

        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/items"
        body = {
            'displayName': display_name,
            'type': type
        }

        if definition:
            body['definition'] = definition
        if description:
            body['description'] = description

        if type in ["dataPipelines",
                    "environments",
                    "eventhouses",
                    "eventstreams",
                    "kqlDatabases",
                    "lakehouses",
                    "mlExperiments", 
                    "mlModels", 
                    "notebooks", 
                    "reports", 
                    "semanticModels", 
                    "sparkJobDefinitions", 
                    "warehouses"]:
            
            if type == "kqlDatabases":
                if "creation_payload" not in kwargs:
                    raise Exception("creation_payload is required for KQLDatabase")
                body["creationPayload"] = kwargs["creation_payload"]
            
            url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/{type}"
            body.pop('type')


        item_dict = self.calling_routine(url, operation="POST", 
                                         body=body, response_codes=[201, 202, 429], 
                                         error_message="Error creating item", return_format="json+operation_result",
                                         wait_for_completion=wait_for_completion)


        if item_dict is None or "no_operation_result" in item_dict:
            print("Item not returned by API, trying to get it by name")
            item = None
            i = 0

            type_mapping = {"dataPipelines": "DataPipeline",
                            "environments": "Environment",
                            "eventhouses": "Eventhouse",
                            "eventstreams": "Eventstream",
                            "kqlDatabases": "KQLDatabase",
                            "lakehouses": "Lakehouse", 
                            "mlExperiments": "MLExperiment",
                            "mlModels": "MLModel", 
                            "notebooks": "Notebook", 
                            "reports": "Report", 
                            "semanticModels": "SemanticModel",
                            "sparkJobDefinitions": "SparkJobDefinition", 
                            "warehouses": "Warehouse"
                            }
            
            if type in type_mapping.keys():
                type = type_mapping[type]
            while item is None and i < 12:
                item = self.get_item_by_name(workspace_id, display_name, type)
                if item is not None:
                    return item
                print("Item not found, waiting 5 seconds")
                sleep(5)
                i += 1

            print("Item not found after 1 minute, returning None")
            return None
                
        return self.get_item_specific(workspace_id, item_dict)

    # Get
 
    def get_item(self, workspace_id, item_id = None, item_name = None, item_type = None):
        # GET https://api.fabric.microsoft.com/v1/workspaces/{workspaceId}/items/{itemId}
        """Get an item from a workspace
        Args:
            workspace_id (str): The ID of the workspace
            item_id (str): The ID of the item
            item_name (str): The name of the item
            item_type (str): The type of the item
        Returns:
            Item: The item object
        Raises:
            Exception: If item_id or the combination item_name + item_type is required
        """
        if item_type:
            if item_type.lower() == "datapipeline":
                return self.get_data_pipeline(workspace_id, item_id, item_name)
            if item_type.lower() == "eventstream":
                return self.get_eventstream(workspace_id, item_id, item_name)
            if item_type.lower() == "kqldatabase":
                return self.get_kql_database(workspace_id, item_id, item_name)
            if item_type.lower() == "kqlqueryset":
                return self.get_kql_queryset(workspace_id, item_id, item_name)
            if item_type.lower() == "lakehouse":
                return self.get_lakehouse(workspace_id, item_id, item_name)
            if item_type.lower() == "mlmodel":
                return self.get_ml_model(workspace_id, item_id, item_name)
            if item_type.lower() == "mlexperiment":
                return self.get_ml_experiment(workspace_id, item_id, item_name)
            if item_type.lower() == "notebook":
                return self.get_notebook(workspace_id, item_id, item_name)
            if item_type.lower() == "report":
                return self.get_report(workspace_id, item_id, item_name)
            if item_type.lower() == "semanticmodel":
                return self.get_semantic_model(workspace_id, item_id, item_name)
            if item_type.lower() == "sparkjobdefinition":
                return self.get_spark_job_definition(workspace_id, item_id, item_name)
            if item_type.lower() == "warehouse":
                return self.get_warehouse(workspace_id, item_id, item_name)
                
        if item_id is None and item_name is not None and item_type is not None:
            return self.get_item_by_name(workspace_id, item_name, item_type)
        elif item_id is None:
            raise Exception("item_id or the combination item_name + item_type is required")
        
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/items/{item_id}"

        item_dict = self.calling_routine(url, operation="GET", response_codes=[200, 429], error_message="Error getting item", return_format="json")

        return self.get_item_specific(workspace_id=workspace_id, item_dict = item_dict)

    # Delete

    def delete_item(self, workspace_id, item_id, type = None):
        """Delete an item from a workspace
        Args:
            workspace_id (str): The ID of the workspace
            item_id (str): The ID of the item
            type (str): The type of the item
        Returns:
            int: The status code of the response
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/items/{item_id}"
        if type:
            url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/{type}/{item_id}"

        response = self.calling_routine(url, operation="DELETE", response_codes=[200, 429], return_format="response",
                                        error_message="Error deleting item")
        
        return response.status_code

    # List
 
    def list_items(self, workspace_id, with_properties = False, type = None):
        """List items in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            with_properties (bool): Whether to get the item object with properties
            type (str): The type of the item
        Returns:
            list: The list of items
        """
        from msfabricpysdkcore.item import Item

        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/items"
        if type:
            url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/{type}"
        
        items = self.calling_routine(url, operation="GET", response_codes=[200, 429],
                                error_message="Error listing items", return_format="value_json", paging=True)
        
        if with_properties:
            items = self.get_item_object_w_properties(items)
        else:
            items = [Item.from_dict(item, core_client=self) for item in items]

        return items

    
    def get_item_definition(self, workspace_id, item_id, type = None, format = None):
        """Get the item definition
        Args:
            workspace_id (str): The ID of the workspace
            item_id (str): The ID of the item
            type (str): The type of the item
            format (str): The format of the item
        Returns:
            dict: The item definition
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/items/{item_id}/getDefinition"
        if type:
            url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/{type}/{item_id}/getDefinition"

        if format:
            url += f"?format={format}"

        return self.calling_routine(url, operation="POST", response_codes=[200, 202, 429],
                                    error_message="Error getting item definition",
                                    return_format="json+operation_result")
    
    
    def update_item(self, workspace_id, item_id, display_name = None, description = None, type = None, return_item="Default"):
        """Update the item
        Args:
            workspace_id (str): The ID of the workspace
            item_id (str): The ID of the item
            display_name (str): The display name of the item
            description (str): The description of the item
            type (str): The type of the item
        Returns:
            dict: The updated item
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/items/{item_id}"
        if type:
            url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/{type}/{item_id}"

        payload = dict()
        if display_name:
            payload['displayName'] = display_name
        if description:
            payload['description'] = description

        resp_dict = self.calling_routine(url, operation="PATCH", body=payload,
                                         response_codes=[200, 429], error_message="Error updating item",
                                         return_format="json")
        if return_item == "Default":
            print("""Warning: Updating an item currently will make invoke an additional API call to get the item object.
                  This default behaviour will change in newer versions of the SDK.
                  To keep this behaviour, set return_item=True in the function call.""")
        if return_item:
            return self.get_item_specific(workspace_id, resp_dict)
        return resp_dict

    def update_item_definition(self, workspace_id, item_id, definition, type = None, wait_for_completion=True):
        """Update the item definition
        Args:
            workspace_id (str): The ID of the workspace
            item_id (str): The ID of the item
            definition (dict): The definition of the item
            type (str): The type of the item
        Returns:
            requests.Response: The response object
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/items/{item_id}/updateDefinition"

        if type:
            url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/{type}/{item_id}/updateDefinition"
            
        payload = {
            'definition': definition
        }

        response = self.calling_routine(url, operation="POST", body=payload, response_codes=[200, 202, 429],
                                        error_message="Error updating item definition", return_format="response",
                                        wait_for_completion=wait_for_completion)
        return response

    # Job Scheduler
    
    def cancel_item_job_instance(self, workspace_id, item_id, job_instance_id):
        """Cancel the job instance
        Args:
            workspace_id (str): The ID of the workspace
            item_id (str): The ID of the item
            job_instance_id (str): The ID of the job instance
        Returns:
            int: The status code of the response
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/items/{item_id}/jobs/instances/{job_instance_id}/cancel"
        
        response = self.calling_routine(url=url, operation="POST", response_codes=[202, 429],
                                        error_message="Error cancelling job instance", return_format="response",
                                        wait_for_completion=False)

        return response.status_code
    

    def get_item_job_instance(self, workspace_id, item_id, job_instance_id):
        """Get the job instance of the item
        Args:
            workspace_id (str): The ID of the workspace
            item_id (str): The ID of the item
            job_instance_id (str): The ID of the job instance
        Returns:
            JobInstance: The job instance object
        """
        from msfabricpysdkcore.job_instance import JobInstance

        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/items/{item_id}/jobs/instances/{job_instance_id}"
        
        job_dict = self.calling_routine(url=url, operation="GET", response_codes=[200, 429],
                                        error_message="Error getting job instance", return_format="json")
        
        job_dict['workspaceId'] = workspace_id
        job_dict['itemId'] = item_id
        return JobInstance.from_dict(job_dict, core_client=self)
    

    def run_on_demand_item_job(self, workspace_id, item_id, job_type, execution_data = None):
        """Run an on demand job on the item
        Args:
            workspace_id (str): The ID of the workspace
            item_id (str): The ID of the item
            job_type (str): The type of the job
            execution_data (dict): The execution data of the job
        Returns:
            JobInstance: The job instance object
        """

        # POST https://api.fabric.microsoft.com/v1/workspaces/{workspaceId}/items/{itemId}/jobs/instances?jobType={jobType}
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/items/{item_id}/jobs/instances?jobType={job_type}"
        payload = {
            'executionData': execution_data
        }

        response = self.calling_routine(url, operation="POST", body=payload, response_codes=[202, 429],
                                        error_message="Error running on demand job",
                                        wait_for_completion=False, return_format="response")

        job_instance_id = response.headers["Location"].split("/")[-1]
        job_instance = self.get_item_job_instance(workspace_id, item_id, job_instance_id=job_instance_id)
        return job_instance
    
    # long running operations

    def get_operation_results(self, operation_id):
        """Get the results of an operation
        Args:
            operation_id (str): The ID of the operation
        Returns:
            dict: The results of the operation
        """
        url = f"https://api.fabric.microsoft.com/v1/operations/{operation_id}/result"

        response = self.calling_routine(url=url, operation="GET", response_codes=[200, 429],
                                             error_message="Error getting operation results", return_format="response",
                                             continue_on_error_code=True)
        if response.status_code == 400:
            return {"no_operation_result": True}
        return json.loads(response.text)
    
    def get_operation_state(self, operation_id):
        """Get the state of an operation
        Args:
            operation_id (str): The ID of the operation
        Returns:
            dict: The state of the operation
        """ 
        url = f"https://api.fabric.microsoft.com/v1/operations/{operation_id}"

        response_json = self.calling_routine(url=url, operation="GET", response_codes=[200, 429],
                                             error_message="Error getting operation state", return_format="json")

        return response_json

    # One Lake Data Access Security

    # create and update

    def create_or_update_data_access_roles(self, workspace_id, item_id, data_access_roles, dryrun = False, etag_match = None):
        # PUT https://api.fabric.microsoft.com/v1/workspaces/{workspaceId}/items/{itemId}/dataAccessRoles

        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/items/{item_id}/dataAccessRoles"

        if dryrun:
            url += "?dryrun=true"
        
        headers = self.auth.get_headers()
        if etag_match:
            if 'If-Match' in etag_match:
                headers['If-Match'] = etag_match['If-Match']
            elif 'If-None-Match' in etag_match:
                headers['If-None-Match'] = etag_match['If-None-Match']
            else:
                raise Exception("Etag match should include If-Match or If-None-Match")

        body = {"value" : data_access_roles}

        response = self.calling_routine(url, operation="PUT", body=body, headers=headers, 
                                        response_codes=[200, 429], error_message="Error creating or updating data access roles", return_format="response")

        return response
    
    
    # list 
    
    def list_data_access_roles(self, workspace_id, item_id):

        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/items/{item_id}/dataAccessRoles"

        items, etag = self.calling_routine(url, operation="GET", response_codes=[200, 429], error_message="Error listing data access roles", return_format="value_json+etag")
        return items, etag
    
    # ShortCuts

    def create_shortcut(self, workspace_id, item_id, path, name, target):
        """Create a shortcut in the item
        Args:
            workspace_id (str): The ID of the workspace
            item_id (str): The ID of the item
            path (str): The path of the shortcut
            name (str): The name of the shortcut
            target (str): The target of the shortcut
        Returns:
            OneLakeShortcut: The created shortcut
        """
        from msfabricpysdkcore.onelakeshortcut import OneLakeShortcut

        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/items/{item_id}/shortcuts"

        body = {'name': name,
                'path': path,
                'target': target}
        
        response_json = self.calling_routine(url, operation="POST", body=body,
                                             response_codes=[201, 429], error_message="Error creating shortcut",
                                             return_format="json")

        shortcut_dict = response_json
        shortcut_dict['workspaceId'] = workspace_id
        shortcut_dict['itemId'] = item_id
        return OneLakeShortcut.from_dict(shortcut_dict,
                                         core_client = self)
    

    def get_shortcut(self, workspace_id, item_id, path, name):
        """Get the shortcut in the item
        Args:
            workspace_id (str): The ID of the workspace
            item_id (str): The ID of the item
            path (str): The path of the shortcut
            name (str): The name of the shortcut
        Returns:
            OneLakeShortcut: The shortcut object
        """
        from msfabricpysdkcore.onelakeshortcut import OneLakeShortcut
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/items/{item_id}/shortcuts/{path}/{name}"
        
        shortcut_dict = self.calling_routine(url=url, operation="GET",
                                             response_codes=[200, 429], error_message="Error getting shortcut",
                                             return_format="json")

        shortcut_dict['workspaceId'] = workspace_id
        shortcut_dict['itemId'] = id
        return OneLakeShortcut.from_dict(shortcut_dict,
                                         core_client = self)
    
    def delete_shortcut(self, workspace_id, item_id, path, name):
        """Delete the shortcut
        Args:
            workspace_id (str): The ID of the workspace
            item_id (str): The ID of the item
            path (str): The path of the shortcut
            name (str): The name of the shortcut
        Returns:
            int: The status code of the response
        """

        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/items/{item_id}/shortcuts/{path}/{name}"
        response = self.calling_routine(url=url, operation="DELETE",
                                             response_codes=[200, 429], error_message="Error creating shortcut",
                                             return_format="response")


        return response.status_code
    
    ### Workspaces

    def add_workspace_role_assignment(self, workspace_id, role, principal):
        """Add a role assignment to a workspace
        Args:
            workspace_id (str): The ID of the workspace
            role (str): The role to assign
            principal (str): The principal to assign the role to    
        Returns:
            int: The status code of the response
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/roleAssignments"

        payload = {
            'principal': principal,
            'role': role
        }

        response = self.calling_routine(url, operation="POST", body=payload, response_codes=[201, 429], error_message="Error adding role assignments", return_format="response")

        return response.status_code
    
    def assign_to_capacity(self, workspace_id, capacity_id, wait_for_completion=True):
        """Assign a workspace to a capacity
        Args:
            workspace_id (str): The ID of the workspace
            capacity_id (str): The ID of the capacity
            wait_for_completion (bool): Whether to wait for the operation to complete
        Returns:
            int: The status code of the response
        """

        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/assignToCapacity"

        body = {
            'capacityId': capacity_id
        }

        response = self.calling_routine(url, operation="POST", body=body, response_codes=[202, 429], error_message="Error assigning capacity", return_format="response", wait_for_completion=wait_for_completion)

        return response.status_code
    
    def create_workspace(self, display_name, capacity_id = None, description = None, exists_ok = True):
        """Create a workspace
        Args:
            display_name (str): The display name of the workspace
            capacity_id (str): The ID of the capacity to assign the workspace to
            description (str): The description of the workspace
            exists_ok (bool): Whether to return the existing workspace if it already exists
        Returns:
            Workspace: The created workspace
        """
        from msfabricpysdkcore.workspace import Workspace

        body = dict()
        body["displayName"] = display_name
        if capacity_id:
            body["capacityId"] = capacity_id
        if description:
            body["description"] = description
        
        url = "https://api.fabric.microsoft.com/v1/workspaces"

        response = self.calling_routine(url, operation="POST", body=body, response_codes=[201, 429], error_message="Error creating workspace", return_format="response", continue_on_error_code=True)
        ws_dict = json.loads(response.text)
        if response.status_code not in (201, 429):
            if "errorCode" in ws_dict and ws_dict["errorCode"] == "WorkspaceNameAlreadyExists" and exists_ok:
                return self.get_workspace_by_name(display_name)
            else:
                raise Exception(f"Error creating workspace: {response.status_code}, {response.text}")

        ws = Workspace.from_dict(ws_dict, core_client=self)
        return ws
    
    def delete_workspace(self, workspace_id = None, display_name = None):
        """Delete a workspace
        Args:
            workspace_id (str): The ID of the workspace
            display_name (str): The display name of the workspace
        Returns:
            int: The status code of the response
        """
        if workspace_id is None and display_name is None:
            raise ValueError("Either workspace_id or display_name must be provided")
        if workspace_id is None:
            ws = self.get_workspace_by_name(display_name)
            workspace_id = ws.id

        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}"

        response = self.calling_routine(url, operation="DELETE", response_codes=[200, 429], error_message="Error deleting workspace", return_format="response")

        return response.status_code
    
    def delete_workspace_role_assignment(self, workspace_id, workspace_role_assignment_id):
        """Delete a role assignment from the workspace
        Args:
            workspace_id (str): The ID of the workspace
            workspace_role_assignment_id (str): The ID of the role assignment
        Returns:
            int: The status code of the response
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/roleAssignments/{workspace_role_assignment_id}"

        response = self.calling_routine(url, operation="DELETE", response_codes=[200, 429], error_message="Error deleting role assignments", return_format="response")
        
        return response.status_code
    
    def deprovision_identity(self, workspace_id):
        """Deprovision an identity for a workspace
        Args:
            workspace_id (str): The ID of the workspace 
        Returns:
            requests.Response: The response object
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/deprovisionIdentity"

        response = self.calling_routine(url, operation="POST", response_codes=[200, 201, 202, 429], error_message="Error deprovisioning identity", return_format="response")

        return response

    def get_workspace(self, id = None, name = None):
        """Get workspace by id or name
        Args:
            id (str): The ID of the workspace
            name (str): The name of the workspace
        Returns:
            Workspace: The workspace object
        Raises:
            ValueError: If neither id nor name is provided
        """
        if id:
            return self.get_workspace_by_id(id)
        if name:
            return self.get_workspace_by_name(name)
        raise ValueError("Either id or name must be provided")
    
    def get_workspace_by_id(self, id):
        """Get workspace by id
        Args:
            id (str): The ID of the workspace
        Returns:
            Workspace: The workspace object
        """
        from msfabricpysdkcore.workspace import Workspace

        url = f"https://api.fabric.microsoft.com/v1/workspaces/{id}"

        ws_dict = self.calling_routine(url, operation="GET", response_codes=[200, 404], error_message="Error getting workspace", return_format="json")

        ws = Workspace.from_dict(ws_dict, core_client=self)

        return ws

    def get_workspace_by_name(self, name):
        """Get workspace by name
        Args:
            name (str): The name of the workspace
        Returns:
            Workspace: The workspace object
        """
        ws_list = self.list_workspaces()
        for ws in ws_list:
            if ws.display_name == name:
                return ws
            
    def get_workspace_role_assignment(self, workspace_id, workspace_role_assignment_id):
        """Get a role assignment for a workspace
        Args:
            workspace_id (str): The ID of the workspace
            workspace_role_assignment_id (str): The ID of the role assignment
        Returns:
            dict: The role assignment
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/roleAssignments/{workspace_role_assignment_id}"

        return self.calling_routine(url, operation="GET", response_codes=[200, 429], error_message="Error getting role assignments", return_format="json")
           
    def list_workspace_role_assignments(self, workspace_id):
        """List role assignments for the workspace
        Args:
            workspace_id (str): The ID of the workspace
        Returns:
            list: The list of role assignments
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/roleAssignments"

       
        role_assignments = self.calling_routine(url, operation="GET", response_codes=[200, 429],
                                                error_message="Error listing workspace role assignments", return_format = "value_json",
                                                paging = True)

        return role_assignments
       
    def list_workspaces(self):
        """List all workspaces in the tenant
        Returns:
            list: The list of workspaces
        """
        from msfabricpysdkcore.workspace import Workspace

        url = "https://api.fabric.microsoft.com/v1/workspaces"

        ws_list = self.calling_routine(url, operation="GET", response_codes=[200], error_message="Error listing workspaces", return_format="value_json", paging=True)
        ws_list = [Workspace.from_dict(ws, core_client=self) for ws in ws_list]

        return ws_list

    def provision_identity(self, workspace_id):
        """Provision an identity for a workspace
        Args:
            workspace_id (str): The ID of the workspace
        Returns:
            requests.Response: The response object
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/provisionIdentity"

        response = self.calling_routine(url, operation="POST", response_codes=[200, 201, 202, 429], error_message="Error provisioning identity", return_format="response")
        return response
    
    def unassign_from_capacity(self, workspace_id, wait_for_completion = True):        
        """Unassign a workspace from a capacity
        Args:
            workspace_id (str): The ID of the workspace
            wait_for_completion (bool): Whether to wait for the operation to complete
        Returns:
            int: The status code of the response
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/unassignFromCapacity"

        response = self.calling_routine(url, operation="POST", response_codes=[202, 429], error_message="Error unassigning capacity", return_format="response", wait_for_completion=wait_for_completion)

        return response.status_code

        
    def update_workspace(self, workspace_id, display_name = None, description = None):
        """Update the workspace
        Args:
            workspace_id (str): The ID of the workspace
            display_name (str): The display name of the workspace
            description (str): The description of the workspace
        Returns:
            Workspace: The updated workspace
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}"

        body = dict()
        if display_name:
            body["displayName"] = display_name
        if description:
            body["description"] = description

        response = self.calling_routine(url, operation="PATCH", body=body, response_codes=[200, 429], error_message="Error updating workspace", return_format="response")

        assert response.status_code == 200

        return self.get_workspace_by_id(workspace_id)


    def update_workspace_role_assignment(self, workspace_id, role, workspace_role_assignment_id):
        """Update a role assignment for a workspace
        Args:
            workspace_id (str): The ID of the workspace
            role (str): The role to assign
            workspace_role_assignment_id (str): The ID of the role assignment
        Returns:
            int: The status code of the response
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/roleAssignments/{workspace_role_assignment_id}"
        body = {
            'role': role
        }

        response = self.calling_routine(url, operation="PATCH", body=body, response_codes=[200, 429], error_message="Error updating role assignments", return_format="response")

        return response.status_code   


    
    # list things
    def list_dashboards(self, workspace_id):
        """List dashboards in a workspace"""
        return self.list_items(workspace_id, type="dashboards")
    
    def list_datamarts(self, workspace_id):
        """List datamarts in a workspace"""
        return self.list_items(workspace_id, type="datamarts")
    
    def list_paginated_reports(self, workspace_id):
        """List paginated reports in a workspace"""
        return self.list_items(workspace_id, type="paginatedReports")

    def list_sql_endpoints(self, workspace_id):
        """List sql endpoints in a workspace"""
        return self.list_items(workspace_id, type="sqlEndpoints")

    def list_mirrored_warehouses(self, workspace_id):
        """List mirrored warehouses in a workspace"""
        return self.list_items(workspace_id, type="mirroredWarehouses")
    
    # dataPipelines

    def create_data_pipeline(self, workspace_id, display_name, definition = None, description = None):
        """Create a data pipeline in a workspace"""
        return self.create_item(workspace_id=workspace_id,
                                display_name = display_name,
                                type = "dataPipelines",
                                definition = definition,
                                description = description)
    
    def delete_data_pipeline(self, workspace_id, data_pipeline_id):
        """Delete a data pipeline from a workspace
        Args:
            workspace_id (str): The ID of the workspace
            data_pipeline_id (str): The ID of the data pipeline
        Returns:
            int: The status code of the response
        """
        return self.delete_item(workspace_id, item_id=data_pipeline_id, type="dataPipelines")
    
    def get_data_pipeline(self, workspace_id, data_pipeline_id = None, data_pipeline_name = None):
        """Get a data pipeline from a workspace
        Args:
            workspace_id (str): The ID of the workspace
            data_pipeline_id (str): The ID of the data pipeline
            data_pipeline_name (str): The name of the data pipeline
        Returns:
            DataPipeline: The data pipeline object
        """
        from msfabricpysdkcore.otheritems import DataPipeline

        if data_pipeline_id is None and data_pipeline_name is not None:
            data_pipelines = self.list_data_pipelines(workspace_id)
            dps = [dp for dp in data_pipelines if dp.display_name == data_pipeline_name]
            if len(dps) == 0:
                raise Exception(f"Data pipeline with name {data_pipeline_name} not found")
            data_pipeline_id = dps[0].id
        elif data_pipeline_id is None:
            raise Exception("data_pipeline_id or the data_pipeline_name is required")
        
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/dataPipelines/{data_pipeline_id}"

        item_dict = self.calling_routine(url, operation="GET", response_codes=[200, 429],
                                         error_message="Error getting data pipeline", return_format="json")

        dp = DataPipeline.from_dict(item_dict, core_client=self)
        dp.get_definition()
        return dp
    
    def list_data_pipelines(self, workspace_id, with_properties = False):
        """List data pipelines in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            with_properties (bool): Whether to get the item object with properties
        Returns:
            list: The list of data pipelines
        """
        return self.list_items(workspace_id, type="dataPipelines", with_properties=with_properties)
    
    def update_data_pipeline(self, workspace_id, data_pipeline_id, display_name = None, description = None, return_item="Default"):
        """Update a data pipeline in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            data_pipeline_id (str): The ID of the data pipeline
            display_name (str): The display name of the data pipeline
            description (str): The description of the data pipeline
        Returns:
            dict: The updated data pipeline
        """
        return self.update_item(workspace_id, item_id=data_pipeline_id, display_name=display_name, description=description, type="dataPipelines",
                                return_item=return_item)
    

    # environments

    def create_environment(self, workspace_id, display_name, description = None):
        """Create an environment in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            display_name (str): The display name of the environment
            description (str): The description of the environment
        Returns:
            dict: The created environment
        """
        return self.create_item(workspace_id=workspace_id,
                                display_name = display_name,
                                type = "environments",
                                definition = None,
                                description = description)
    
    def delete_environment(self, workspace_id, environment_id):
        """Delete an environment from a workspace
        Args:
            workspace_id (str): The ID of the workspace
            environment_id (str): The ID of the environment
        Returns:
            int: The status code of the response
        """
        return self.delete_item(workspace_id, item_id=environment_id, type="environments")
    
    def get_environment(self, workspace_id, environment_id = None, environment_name = None):
        """Get an environment from a workspace
        Args:
            workspace_id (str): The ID of the workspace
            environment_id (str): The ID of the environment
            environment_name (str): The name of the environment
        Returns:
            dict: The environment
        """
        from msfabricpysdkcore.environment import Environment
        if environment_id is None and environment_name is not None:
            envs = self.list_environments(workspace_id)
            envs = [env for env in envs if env.display_name == environment_name]
            if len(envs) == 0:
                raise Exception(f"Environment with name {environment_name} not found")
            environment_id = envs[0].id
        elif environment_id is None:
            raise Exception("environment_id or the environment_name is required")
        
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/environments/{environment_id}"
        item_dict = self.calling_routine(url, operation="GET", response_codes=[200, 429],
                                         error_message="Error getting environment", return_format="json")
        env = Environment.from_dict(item_dict, core_client=self)
        return env

    def list_environments(self, workspace_id, with_properties = False):
        """List environments in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            with_properties (bool): Whether to get the item object with properties
        Returns:
            list: The list of environments
        """
        return self.list_items(workspace_id, type="environments", with_properties=with_properties)
    
    def update_environment(self, workspace_id, environment_id, display_name = None, description = None, return_item="Default"):
        """Update an environment in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            environment_id (str): The ID of the environment
            display_name (str): The display name of the environment
            description (str): The description of the environment
        Returns:
            dict: The updated environment
        """
        return self.update_item(workspace_id, item_id=environment_id, display_name=display_name, description=description, 
                                type="environments", return_item=return_item)

    # environmentSparkCompute

    def get_published_settings(self, workspace_id, environment_id):
        """Get the published settings of the environment
        Args:
            workspace_id (str): The ID of the workspace
            environment_id (str): The ID of the environment
        Returns:
            dict: The published settings
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/environments/{environment_id}/sparkcompute"

        resp_json = self.calling_routine(url, operation="GET", response_codes=[200, 429], error_message="Error getting published settings", return_format="json")     
        return resp_json

    def get_staging_settings(self, workspace_id, environment_id):
        """Get the staging settings of the environment
        Args:
            workspace_id (str): The ID of the workspace
            environment_id (str): The ID of the environment
        Returns:
            dict: The staging settings
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/environments/{environment_id}/staging/sparkcompute"

        resp_json = self.calling_routine(url, operation="GET", response_codes=[200, 429], error_message="Error getting staging settings", return_format="json")       
        return resp_json
    
    def update_staging_settings(self, workspace_id, environment_id,
                                driver_cores = None, driver_memory = None, dynamic_executor_allocation = None,
                                executor_cores = None, executor_memory = None, instance_pool = None,
                                runtime_version = None, spark_properties = None):
        """Update the staging settings of the environment
        Args:
            workspace_id (str): The ID of the workspace
            environment_id (str): The ID of the environment
            driver_cores (int): The number of driver cores
            driver_memory (str): The memory for the driver
            dynamic_executor_allocation (bool): Whether to dynamically allocate executors
            executor_cores (int): The number of executor cores
            executor_memory (str): The memory for the executor
            instance_pool (str): The instance pool
            runtime_version (str): The runtime version
            spark_properties (dict): The spark properties
        Returns:
            dict: The updated staging settings
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/environments/{environment_id}/staging/sparkcompute"
        body = {}
        if driver_cores is not None:
            body['driverCores'] = driver_cores
        if driver_memory is not None:
            body['driverMemory'] = driver_memory
        if dynamic_executor_allocation is not None:
            body['dynamicExecutorAllocation'] = dynamic_executor_allocation
        if executor_cores is not None:
            body['executorCores'] = executor_cores
        if executor_memory is not None:
            body['executorMemory'] = executor_memory
        if instance_pool is not None:
            body['instancePool'] = instance_pool
        if runtime_version is not None:
            body['runtimeVersion'] = runtime_version
        if spark_properties is not None:
            body['sparkProperties'] = spark_properties

        respone_json = self.calling_routine(url, operation="PATCH", body=body, response_codes=[200, 429],
                                            error_message="Error updating staging settings", return_format="json")

        return respone_json


    # environmentSparkLibraries
    
    def cancel_publish(self, workspace_id, environment_id):
        """Cancel the publishing of the staging settings and libraries of the environment
        Args:
            workspace_id (str): The ID of the workspace
            environment_id (str): The ID of the environment
        Returns:
            dict: The operation result or response value
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/environments/{environment_id}/staging/cancelPublish"

        resp_dict = self.calling_routine(url, operation="POST", response_codes=[200, 429], error_message="Error canceling publish", return_format="json")
        return resp_dict
    
    def delete_staging_library(self, workspace_id, environment_id, library_to_delete):
        """Delete a library from the staging libraries of the environment
        Args:
            workspace_id (str): The ID of the workspace
            environment_id (str): The ID of the environment
            library_to_delete (str): The library to delete
        Returns:
            requests.Response: The response object
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/environments/{environment_id}/staging/libraries?libraryToDelete={library_to_delete}"

        response = self.calling_routine(url, operation="DELETE", response_codes=[200, 429], error_message="Error deleting staging library", return_format="response")

        return response
    
    def get_published_libraries(self, workspace_id, environment_id):
        """Get the published libraries of the environment
        Args:
            workspace_id (str): The ID of the workspace
            environment_id (str): The ID of the environment
        Returns:
            dict: The published libraries
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/environments/{environment_id}/libraries"

        resp_json = self.calling_routine(url, operation="GET", response_codes=[200, 429], error_message="Error getting published libraries", return_format="json")
        return resp_json
    
    def get_staging_libraries(self, workspace_id, environment_id):
        """Get the staging libraries of the environment
        Args:
            workspace_id (str): The ID of the workspace
            environment_id (str): The ID of the environment
        Returns:
            dict: The staging libraries
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/environments/{environment_id}/staging/libraries"

        resp_json = self.calling_routine(url, operation="GET", response_codes=[200, 429], error_message="Error getting staging libraries", return_format="json")
        return resp_json
    
    def publish_environment(self, workspace_id, environment_id):
        """Publish the staging settings and libraries of the environment
        Args:
            workspace_id (str): The ID of the workspace
            environment_id (str): The ID of the environment
        Returns:
            dict: The operation result or response value
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/environments/{environment_id}/staging/publish"

        resp_dict = self.calling_routine(url, operation="POST", response_codes=[200, 429], error_message="Error publishing staging",
                                         return_format="json+operation_result")

        return resp_dict
    
    def upload_staging_library(self, workspace_id, environment_id, file_path):
        """Update staging libraries for an environment
        Args:
            workspace_id (str): The ID of the workspace
            environment_id (str): The ID of the environment
            file_path (str): The file path to upload
        Returns:
            requests.Response: The response object
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/environments/{environment_id}/staging/libraries"
        response = self.calling_routine(url, operation="POST", file_path=file_path, response_codes=[200, 429],
                                        error_message="Error uploading staging library", return_format="response")
        return response
    

    
    # eventhouses

    def create_eventhouse(self, workspace_id, display_name, description = None):
        """Create an eventhouse in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            display_name (str): The display name of the eventhouse
            description (str): The description of the eventhouse
        Returns:
            Eventhouse: The created eventhouse
        """
        return self.create_item(workspace_id=workspace_id,
                                display_name = display_name,
                                type = "eventhouses",
                                definition = None,
                                description = description)

    def delete_eventhouse(self, workspace_id, eventhouse_id):
        """Delete an eventhouse from a workspace
        Args:
            workspace_id (str): The ID of the workspace
            eventhouse_id (str): The ID of the eventhouse
        Returns:
            int: The status code of the response
        """
        return self.delete_item(workspace_id, eventhouse_id, type="eventhouses")
    
    def get_eventhouse(self, workspace_id, eventhouse_id = None, eventhouse_name = None):
        """Get an eventhouse from a workspace
        Args:
            workspace_id (str): The ID of the workspace
            eventhouse_id (str): The ID of the eventhouse
            eventhouse_name (str): The name of the eventhouse
        Returns:
            Eventhouse: The eventhouse object
        """
        from msfabricpysdkcore.otheritems import Eventhouse
        if eventhouse_id is None and eventhouse_name is not None:
            ehs = self.list_eventhouses(workspace_id)
            ehs = [eh for eh in ehs if eh.display_name == eventhouse_name]
            if len(ehs) == 0:
                raise Exception(f"Eventhouse with name {eventhouse_name} not found")
            eventhouse_id = ehs[0].id
        if eventhouse_id is None:
            raise Exception("eventhouse_id or the eventhouse_name is required")
        
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/eventhouses/{eventhouse_id}"

        item_dict = self.calling_routine(url, operation="GET", response_codes=[200, 429],
                                         error_message="Error getting eventhouse", return_format="json")
        return Eventhouse.from_dict(item_dict, core_client=self)
    
    def list_eventhouses(self, workspace_id, with_properties = False):
        """List eventhouses in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            with_properties (bool): Whether to get the item object with properties
        Returns:
            list: The list of eventhouses
        """
        return self.list_items(workspace_id=workspace_id, type="eventhouses", with_properties=with_properties)
    
    def update_eventhouse(self, workspace_id, eventhouse_id, display_name = None, description = None, return_item="Default"):
        """Update an eventhouse in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            eventhouse_id (str): The ID of the eventhouse
            display_name (str): The display name of the eventhouse
            description (str): The description of the eventhouse
        Returns:
            dict: The updated eventhouse
        """
        return self.update_item(workspace_id=workspace_id, item_id=eventhouse_id,
                                display_name=display_name, description=description, type="eventhouses", return_item=return_item)

    # eventstreams

    def create_eventstream(self, workspace_id, display_name, description = None):
        """Create an eventstream in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            display_name (str): The display name of the eventstream
            description (str): The description of the eventstream
        Returns:
            dict: The created eventstream
        """
        return self.create_item(workspace_id = workspace_id,
                                display_name = display_name,
                                type = "eventstreams",
                                definition = None,
                                description = description)
    

    def get_eventstream(self, workspace_id, eventstream_id = None, eventstream_name = None):
        """Get an eventstream from a workspace
        Args:
            workspace_id (str): The ID of the workspace
            eventstream_id (str): The ID of the eventstream
            eventstream_name (str): The name of the eventstream
        Returns:
            Eventstream: The eventstream object
        """
        from msfabricpysdkcore.otheritems import Eventstream
        if eventstream_id is None and eventstream_name is not None:
            evstreams = self.list_eventstreams(workspace_id)
            evstreams = [ev for ev in evstreams if ev.display_name == eventstream_name]
            if len(evstreams) == 0:
                raise Exception(f"Eventstream with name {eventstream_name} not found")
            eventstream_id = evstreams[0].id
        if eventstream_id is None:
            raise Exception("eventstream_id or the eventstream_name is required")
        
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/eventstreams/{eventstream_id}"

        item_dict = self.calling_routine(url, operation="GET", response_codes=[200, 429],
                                            error_message="Error getting eventstream", return_format="json")
        return Eventstream.from_dict(item_dict, core_client=self)
    
    def delete_eventstream(self, workspace_id, eventstream_id):
        """Delete an eventstream from a workspace
        Args:
            workspace_id (str): The ID of the workspace
            eventstream_id (str): The ID of the eventstream
        Returns:
            int: The status code of the response
        """
        return self.delete_item(workspace_id, eventstream_id, type="eventstreams")
    
    def list_eventstreams(self, workspace_id, with_properties = False):
        """List eventstreams in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            with_properties (bool): Whether to get the item object with properties
        Returns:
            list: The list of event
        """
        return self.list_items(workspace_id=workspace_id, type="eventstreams", with_properties=with_properties)

    def update_eventstream(self, workspace_id, eventstream_id, display_name = None, description = None, return_item="Default"):
        """Update an eventstream in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            eventstream_id (str): The ID of the eventstream
            display_name (str): The display name of the eventstream
            description (str): The description of the eventstream
        Returns:
            dict: The updated eventstream
        """
        return self.update_item(workspace_id, eventstream_id, display_name = display_name, description = description, 
                                type= "eventstreams", return_item=return_item)

    # kqlDatabases

    def create_kql_database(self, workspace_id, creation_payload, display_name, description = None):
        """Create a kql database in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            creation_payload (dict): The creation payload
            display_name (str): The display name of the kql database
            description (str): The description of the kql database
        Returns:
            dict: The created kql database
        """
        return self.create_item(workspace_id = workspace_id,
                                display_name = display_name,
                                type = "kqlDatabases",
                                description = description,
                                creation_payload = creation_payload)
    
    def delete_kql_database(self, workspace_id, kql_database_id):
        """Delete a kql database from a workspace
        Args:
            workspace_id (str): The ID of the workspace
            kql_database_id (str): The ID of the kql database
        Returns:
            int: The status code of the response
        """
        return self.delete_item(workspace_id, kql_database_id, type="kqlDatabases")
       
    def get_kql_database(self, workspace_id, kql_database_id = None, kql_database_name = None):
        """Get a kql database from a workspace
        Args:
            workspace_id (str): The ID of the workspace
            kql_database_id (str): The ID of the kql database
            kql_database_name (str): The name of the kql database
        Returns:
            KQLDatabase: The kql database object
        """
        from msfabricpysdkcore.otheritems import KQLDatabase
        if kql_database_id is None and kql_database_name is not None:
            kql_databases = self.list_kql_databases(workspace_id)
            kql_databases = [kd for kd in kql_databases if kd.display_name == kql_database_name]
            if len(kql_databases) == 0:
                raise Exception(f"Kql database with name {kql_database_name} not found")
            kql_database_id = kql_databases[0].id
        if kql_database_id is None:
            raise Exception("kql_database_id or the kql_database_name is required")
        
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/kqlDatabases/{kql_database_id}"

        item_dict = self.calling_routine(url, operation="GET", response_codes=[200, 429],
                                         error_message="Error getting kql database", return_format="json")
        return KQLDatabase.from_dict(item_dict, core_client=self)

    def list_kql_databases(self, workspace_id, with_properties = False):
        """List kql databases in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            with_properties (bool): Whether to get the item object with properties
        Returns:
            list: The list of kql databases"""
        return self.list_items(workspace_id=workspace_id, type="kqlDatabases", with_properties=with_properties)
    
    def update_kql_database(self, workspace_id, kql_database_id, display_name = None, description = None, return_item="Default"):
        """Update a kql database in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            kql_database_id (str): The ID of the kql database
            display_name (str): The display name of the kql database
            description (str): The description of the kql database
        Returns:
            dict: The updated kql database
        """
        return self.update_item(workspace_id, kql_database_id, display_name = display_name,
                                description = description, type= "kqlDatabases", return_item=return_item)

    # kqlQuerysets

        
    def delete_kql_queryset(self, workspace_id, kql_queryset_id):
        """Delete a kql queryset from a workspace
        Args:
            workspace_id (str): The ID of the workspace
            kql_queryset_id (str): The ID of the kql queryset
        Returns:
            int: The status code of the response
        """
        return self.delete_item(workspace_id, kql_queryset_id, type="kqlQuerysets")
    
    def get_kql_queryset(self, workspace_id, kql_queryset_id = None, kql_queryset_name = None):
        """Get a kql queryset from a workspace
        Args:
            workspace_id (str): The ID of the workspace
            kql_queryset_id (str): The ID of the kql queryset
            kql_queryset_name (str): The name of the kql queryset
        Returns:
            KQLQueryset: The kql queryset object
        """
        from msfabricpysdkcore.otheritems import KQLQueryset
        if kql_queryset_id is None and kql_queryset_name is not None:
            kql_querysets = self.list_kql_querysets(workspace_id)
            kql_querysets = [kq for kq in kql_querysets if kq.display_name == kql_queryset_name]
            if len(kql_querysets) == 0:
                raise Exception(f"Kql queryset with name {kql_queryset_name} not found")
            kql_queryset_id = kql_querysets[0].id
        if kql_queryset_id is None:
            raise Exception("kql_queryset_id or the kql_queryset_name is required")
        
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/kqlQuerysets/{kql_queryset_id}"

        item_dict = self.calling_routine(url, operation="GET", response_codes=[200, 429],
                                         error_message="Error getting kql queryset", return_format="json")
        
        return KQLQueryset.from_dict(item_dict, core_client=self)

    def update_kql_queryset(self, workspace_id, kql_queryset_id, display_name = None, description = None, return_item="Default"):
        """Update a kql queryset in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            kql_queryset_id (str): The ID of the kql queryset
            display_name (str): The display name of the kql queryset
            description (str): The description of the kql queryset
        Returns:
            dict: The updated kql queryset
        """
        return self.update_item(workspace_id, kql_queryset_id, display_name = display_name,
                                description = description, type= "kqlQuerysets", return_item=return_item)

    def list_kql_querysets(self, workspace_id, with_properties = False):
        """List kql querysets in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            with_properties (bool): Whether to get the item object with properties
        Returns:
            list: The list of kql querysets
        """
        return self.list_items(workspace_id=workspace_id, type="kqlQuerysets", with_properties=with_properties)


    # lakehouses

    def run_on_demand_table_maintenance(self, workspace_id, lakehouse_id, 
                                        execution_data = None,
                                        job_type = "TableMaintenance", wait_for_completion = False):
        """Run on demand table maintenance
        Args:
            workspace_id (str): The ID of the workspace
            lakehouse_id (str): The ID of the lakehouse
            execution_data (dict): The execution data
            job_type (str): The job type
            wait_for_completion (bool): Whether to wait for the operation to complete
        Returns:
            dict: The operation result or response value
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/lakehouses/{lakehouse_id}/jobs/instances?jobType={job_type}"

        body = {
                "executionData": execution_data
              }
        
        respone_operation_result = self.calling_routine(url, operation="POST", body=body, response_codes=[200, 202, 429],
                                                        error_message="Error running on demand table maintenance",
                                                        return_format="response+operation_result", wait_for_completion=wait_for_completion)

        return respone_operation_result
    
    def create_lakehouse(self, workspace_id, display_name, description = None):
        """Create a lakehouse in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            display_name (str): The display name of the lakehouse
            description (str): The description of the lakehouse
        Returns:
            dict: The created lakehouse
        """
        return self.create_item(workspace_id = workspace_id,
                                display_name = display_name,
                                type = "lakehouses",
                                description = description)
    
    def delete_lakehouse(self, workspace_id, lakehouse_id):
        """Delete a lakehouse from a workspace
        Args:
            workspace_id (str): The ID of the workspace
            lakehouse_id (str): The ID of the lakehouse
        Returns:
            int: The status code of the response
        """
        return self.delete_item(workspace_id, lakehouse_id, type="lakehouses")
    
    def get_lakehouse(self, workspace_id, lakehouse_id = None, lakehouse_name = None):
        """Get a lakehouse from a workspace
        Args:
            workspace_id (str): The ID of the workspace
            lakehouse_id (str): The ID of the lakehouse
            lakehouse_name (str): The name of the lakehouse
        Returns:
            Lakehouse: The lakehouse object
        """
        from msfabricpysdkcore.lakehouse import Lakehouse
        if lakehouse_id is None and lakehouse_name is not None:
            lakehouses = self.list_lakehouses(workspace_id)
            lakehouses = [lh for lh in lakehouses if lh.display_name == lakehouse_name]
            if len(lakehouses) == 0:
                raise Exception(f"Lakehouse with name {lakehouse_name} not found")
            lakehouse_id = lakehouses[0].id
        if lakehouse_id is None:
            raise Exception("lakehouse_id or the lakehouse_name is required")
        
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/lakehouses/{lakehouse_id}"

        item_dict = self.calling_routine(url, operation="GET", response_codes=[200, 429],
                                         error_message="Error getting lakehouse", return_format="json")
        return Lakehouse.from_dict(item_dict, core_client=self)
    
    def list_lakehouses(self, workspace_id, with_properties = False):
        """List lakehouses in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            with_properties (bool): Whether to get the item object with properties
        Returns:
            list: The list of lakehouses
        """
        return self.list_items(workspace_id, type="lakehouses", with_properties = with_properties)

    def update_lakehouse(self, workspace_id, lakehouse_id, display_name = None, description = None, return_item="Default"):
        """Update a lakehouse in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            lakehouse_id (str): The ID of the lakehouse
            display_name (str): The display name of the lakehouse
            description (str): The description of the lakehouse
        Returns:
            dict: The updated lakehouse
        """
        return self.update_item(workspace_id, item_id=lakehouse_id, display_name = display_name, description = description,
                                type="lakehouses", return_item=return_item)


    def list_tables(self, workspace_id, lakehouse_id):
        """List all tables in the lakehouse
        Args:
            workspace_id (str): The ID of the workspace
            lakehouse_id (str): The ID of the lakehouse
        Returns:
            list: The list of tables
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/lakehouses/{lakehouse_id}/tables"

        items = self.calling_routine(url, operation="GET", response_codes=[200, 429],
                                        error_message="Error listing tables", return_format="data", paging=True)

        return items
        
    def check_if_table_is_created(self, workspace_id, lakehouse_id, table_name):
        """Check if the table is created
        Args:
            workspace_id (str): The ID of the workspace
            lakehouse_id (str): The ID of the lakehouse
            table_name (str): The name of the table
        Returns:
            bool: Whether the table is created
        """
        for _ in range(60):
            table_names = [table["name"] for table in self.list_tables(workspace_id, lakehouse_id)]
            if table_name in table_names:
                return True
            sleep(3)
        return False
    
    def load_table(self, workspace_id, lakehouse_id, table_name, path_type, relative_path,
                    file_extension = None, format_options = None,
                    mode = None, recursive = None, wait_for_completion = True):
        """Load a table in the lakehouse
        Args:
            workspace_id (str): The ID of the workspace
            lakehouse_id (str): The ID of the lakehouse
            table_name (str): The name of the table
            path_type (str): The path type
            relative_path (str): The relative path
            file_extension (str): The file extension
            format_options (dict): The format options
            mode (str): The mode
            recursive (bool): Whether to load recursively
            wait_for_completion (bool): Whether to wait for the operation to complete
        Returns:
            int: The status code of the response
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/lakehouses/{lakehouse_id}/tables/{table_name}/load"

        body = {
                "relativePath": relative_path,
                "pathType": path_type,
              }

        if file_extension:
            body["fileExtension"] = file_extension
        if format_options:
            body["formatOptions"] = format_options
        if mode:
            body["mode"] = mode
        if recursive:
            body["recursive"] = recursive

        response = self.calling_routine(url, operation="POST", body=body, response_codes=[202, 429],
                                        error_message="Error loading table", return_format="response",
                                        wait_for_completion=False)
        
        if wait_for_completion:
            success = self.check_if_table_is_created(workspace_id = workspace_id,
                                                     lakehouse_id = lakehouse_id,
                                                     table_name = table_name)
            
        if not success:
            print("Warning: Table not created after 3 minutes")
        else:
            print("Table created")
        return response.status_code

    # mlExperiments


    def create_ml_experiment(self, workspace_id, display_name, description = None):
        """Create an ml experiment in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            display_name (str): The display name of the ml experiment
            description (str): The description of the ml experiment
        Returns:
            dict: The created ml experiment
        """
        return self.create_item(workspace_id = workspace_id,
                                display_name = display_name,
                                type = "mlExperiments",
                                description = description)
    
    def delete_ml_experiment(self, workspace_id, ml_experiment_id):
        """Delete an ml experiment from a workspace
        Args:
            workspace_id (str): The ID of the workspace
            ml_experiment_id (str): The ID of the ml experiment
        Returns:
            int: The status code of the response
        """
        return self.delete_item(workspace_id, ml_experiment_id, type="mlExperiments")
    
    def get_ml_experiment(self, workspace_id, ml_experiment_id = None, ml_experiment_name = None):
        """Get an ml experiment from a workspace
        Args:
            workspace_id (str): The ID of the workspace
            ml_experiment_id (str): The ID of the ml experiment
            ml_experiment_name (str): The name of the ml experiment
        Returns:
            MLExperiment: The ml experiment object
        """
        from msfabricpysdkcore.otheritems import MLExperiment
        if ml_experiment_id is None and ml_experiment_name is not None:
            ml_experiments = self.list_ml_experiments(workspace_id)
            ml_experiments = [ml for ml in ml_experiments if ml.display_name == ml_experiment_name]
            if len(ml_experiments) == 0:
                raise Exception(f"ML experiment with name {ml_experiment_name} not found")
            ml_experiment_id = ml_experiments[0].id
        if ml_experiment_id is None:
            raise Exception("ml_experiment_id or the ml_experiment_name is required")
              
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/mlExperiments/{ml_experiment_id}"

        item_dict = self.calling_routine(url, operation="GET", response_codes=[200, 429],
                                         error_message="Error getting ml experiment", return_format="json")
        return MLExperiment.from_dict(item_dict, core_client=self)
    
    def list_ml_experiments(self, workspace_id, with_properties = False):
        """List ml experiments in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            with_properties (bool): Whether to get the item object with properties
        Returns:
            list: The list of ml experiments
        """
        return self.list_items(workspace_id=workspace_id, type="mlExperiments", with_properties = with_properties)
    
    def update_ml_experiment(self, workspace_id, ml_experiment_id, display_name = None, description = None, return_item="Default"):
        """Update an ml experiment in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            ml_experiment_id (str): The ID of the ml experiment
            display_name (str): The display name of the ml experiment
            description (str): The description of the ml experiment
        Returns:
            dict: The updated ml experiment
        """
        return self.update_item(workspace_id, ml_experiment_id, display_name = display_name, description = description,
                                type="mlExperiments", return_item=return_item)
  
    # mlModels

    def create_ml_model(self, workspace_id, display_name, description = None):
        """Create an ml model in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            display_name (str): The display name of the ml model
            description (str): The description of the ml model
        Returns:
            dict: The created ml model
        """
        return self.create_item(workspace_id = workspace_id, display_name = display_name, type = "mlModels", description = description,
                                wait_for_completion = True)
    
    def delete_ml_model(self, workspace_id, ml_model_id):
        """Delete an ml model from a workspace
        Args:
            workspace_id (str): The ID of the workspace
            ml_model_id (str): The ID of the ml model
        Returns:
            int: The status code of the response
        """
        return self.delete_item(workspace_id, ml_model_id, type="mlModels")

    def get_ml_model(self, workspace_id, ml_model_id = None, ml_model_name = None):
        """Get an ml model from a workspace
        Args:
            workspace_id (str): The ID of the workspace
            ml_model_id (str): The ID of the ml model
            ml_model_name (str): The name of the ml model
        Returns:
            MLModel: The ml model object
        """
        from msfabricpysdkcore.otheritems import MLModel
        if ml_model_id is None and ml_model_name is not None:
            ml_models = self.list_ml_models(workspace_id)
            ml_models = [ml for ml in ml_models if ml.display_name == ml_model_name]
            if len(ml_models) == 0:
                raise Exception(f"ML model with name {ml_model_name} not found")
            ml_model_id = ml_models[0].id
        if ml_model_id is None:
            raise Exception("ml_model_id or the ml_model_name is required")

        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/mlModels/{ml_model_id}"

        item_dict = self.calling_routine(url, operation="GET", response_codes=[200, 429],
                                         error_message="Error getting ml model", return_format="json")
        
        return MLModel.from_dict(item_dict, core_client=self)

    def list_ml_models(self, workspace_id, with_properties=False):
        """List ml models in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            with_properties (bool): Whether to get the item object with properties
        Returns:
            list: The list of ml models
        """
        return self.list_items(workspace_id=workspace_id, type="mlModels", with_properties = with_properties)
    
    def update_ml_model(self, workspace_id, ml_model_id, display_name = None, description = None, return_item="Default"):
        """Update an ml model in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            ml_model_id (str): The ID of the ml model
            display_name (str): The display name of the ml model
            description (str): The description of the ml model
        Returns:    
            dict: The updated ml model
        """
        return self.update_item(workspace_id, ml_model_id, display_name = display_name, description = description,
                                type="mlModels", return_item=return_item)
    
    # notebooks

    def create_notebook(self, workspace_id, display_name, definition = None, description = None):
        """Create a notebook in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            display_name (str): The display name of the notebook
            definition (dict): The definition of the notebook
            description (str): The description of the notebook
        Returns:
            dict: The created notebook
        """
        return self.create_item(workspace_id = workspace_id, display_name = display_name, type = "notebooks", definition = definition, description = description)
    
    def get_notebook(self, workspace_id, notebook_id = None, notebook_name = None):
        """Get a notebook from a workspace
        Args:
            workspace_id (str): The ID of the workspace
            notebook_id (str): The ID of the notebook
            notebook_name (str): The name of the notebook
        Returns:
            Notebook: The notebook object"""
        from msfabricpysdkcore.otheritems import Notebook
        if notebook_id is None and notebook_name is not None:
            notebooks = self.list_notebooks(workspace_id)
            notebooks = [nb for nb in notebooks if nb.display_name == notebook_name]
            if len(notebooks) == 0:
                raise Exception(f"Notebook with name {notebook_name} not found")
            notebook_id = notebooks[0].id
        if notebook_id is None:
            raise Exception("notebook_id or the notebook_name is required")
        
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/notebooks/{notebook_id}"

        item_dict = self.calling_routine(url, operation="GET", response_codes=[200, 429],
                                         error_message="Error getting notebook", return_format="json")
        
        notebook = Notebook.from_dict(item_dict, core_client=self)
        notebook.get_definition()
        return notebook

    
    def get_notebook_definition(self, workspace_id, notebook_id, format = None):
        """Get the definition of a notebook
        Args:
            workspace_id (str): The ID of the workspace
            notebook_id (str): The ID of the notebook
            format (str): The format of the definition
        Returns:
            dict: The definition of the notebook
        """
        return self.get_item_definition(workspace_id, notebook_id, type="notebooks", format=format)

    def delete_notebook(self, workspace_id, notebook_id):
        """Delete a notebook from a workspace
        Args:
            workspace_id (str): The ID of the workspace
            notebook_id (str): The ID of the notebook
        Returns:
            int: The status code of the response
        """
        return self.delete_item(workspace_id, notebook_id, type="notebooks")

    def list_notebooks(self, workspace_id, with_properties = False):
        """List notebooks in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            with_properties (bool): Whether to get the item object with properties
        Returns:
            list: The list of notebooks
        """
        return self.list_items(workspace_id = workspace_id, type = "notebooks", with_properties = with_properties)
    
    def update_notebook(self, workspace_id, notebook_id, display_name = None, description = None, return_item="Default"):
        """Update a notebook in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            notebook_id (str): The ID of the notebook
            display_name (str): The display name of the notebook
            description (str): The description of the notebook
        Returns:
            dict: The updated notebook
        """
        return self.update_item(workspace_id, notebook_id, display_name = display_name, description = description,
                                type="notebooks", return_item=return_item)
    
    def update_notebook_definition(self, workspace_id, notebook_id, definition):
        """Update the definition of a notebook
        Args:
            workspace_id (str): The ID of the workspace
            notebook_id (str): The ID of the notebook
            definition (dict): The definition of the notebook
        Returns:
            dict: The updated notebook
        """
        return self.update_item_definition(workspace_id, notebook_id, definition, type="notebooks")
    
    # reports

    def create_report(self, workspace_id, display_name, definition = None, description = None):
        """Create a report in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            display_name (str): The display name of the report
            definition (dict): The definition of the report
            description (str): The description of the report
        Returns:
            dict: The created report
        """
        return self.create_item(workspace_id = workspace_id, display_name = display_name, type = "reports", definition = definition, description = description)
    
    def delete_report(self, workspace_id, report_id):
        """Delete a report from a workspace
        Args:
            workspace_id (str): The ID of the workspace
            report_id (str): The ID of the report
        Returns:
            int: The status code of the response
        """
        return self.delete_item(workspace_id, report_id, type="reports")
    
    def get_report(self, workspace_id, report_id = None, report_name = None):
        """Get a report from a workspace
        Args:
            workspace_id (str): The ID of the workspace
            report_id (str): The ID of the report
            report_name (str): The name of the report
        Returns:
            Report: The report object
        """
        from msfabricpysdkcore.otheritems import Report
        if report_id is None and report_name is not None:
            reports = self.list_reports(workspace_id)
            reports = [rp for rp in reports if rp.display_name == report_name]
            if len(reports) == 0:
                raise Exception(f"Report with name {report_name} not found")
            report_id = reports[0].id
        if report_id is None:
            raise Exception("report_id or the report_name is required")        

        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/reports/{report_id}"

        item_dict = self.calling_routine(url, operation="GET", response_codes=[200, 429],
                                         error_message="Error getting report", return_format="json")
        
        report = Report.from_dict(item_dict, core_client=self)
        report.get_definition()
        return report


    def get_report_definition(self, workspace_id, report_id, format = None):
        """Get the definition of a report
        Args:
            workspace_id (str): The ID of the workspace
            report_id (str): The ID of the report
            format (str): The format of the definition
        Returns:
            dict: The definition of the report
        """
        return self.get_item_definition(workspace_id, report_id, type="reports", format=format)
    
    def list_reports(self, workspace_id, with_properties = False):
        """List reports in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            with_properties (bool): Whether to get the item object with properties
        Returns:
            list: The list of reports
        """
        return self.list_items(workspace_id = workspace_id, type = "reports", with_properties = with_properties)

    def update_report_definition(self, workspace_id, report_id, definition):
        """Update the definition of a report
        Args:
            workspace_id (str): The ID of the workspace
            report_id (str): The ID of the report
            definition (dict): The definition of the report
        Returns:
            Report: The updated report
        """
        return self.update_item_definition(workspace_id, report_id, definition, type="reports")

    # semanticModels

    def list_semantic_models(self, workspace_id, with_properties = False):
        """List semantic models in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            with_properties (bool): Whether to get the item object with properties
        Returns:
            list: The list of semantic models
        """
        return self.list_items(workspace_id = workspace_id, type = "semanticModels", with_properties = with_properties)
    
    def create_semantic_model(self, workspace_id, display_name, definition = None, description = None):
        """Create a semantic model in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            display_name (str): The display name of the semantic model
            definition (dict): The definition of the semantic model
            description (str): The description of the semantic model
        Returns:
            dict: The created semantic model
        """
        return self.create_item(workspace_id = workspace_id, display_name = display_name, type = "semanticModels", definition = definition, description = description)
    
    def get_semantic_model(self, workspace_id, semantic_model_id = None, semantic_model_name = None):
        """Get a semantic model from a workspace
        Args:
            workspace_id (str): The ID of the workspace
            semantic_model_id (str): The ID of the semantic model
            semantic_model_name (str): The name of the semantic model
        Returns:
            SemanticModel: The semantic model object
        """
        from msfabricpysdkcore.otheritems import SemanticModel
        if semantic_model_id is None and semantic_model_name is not None:
            semantic_models = self.list_semantic_models(workspace_id)
            semantic_models = [sm for sm in semantic_models if sm.display_name == semantic_model_name]
            if len(semantic_models) == 0:
                raise Exception(f"Semantic model with name {semantic_model_name} not found")
            semantic_model_id = semantic_models[0].id
        if semantic_model_id is None:
            raise Exception("semantic_model_id or the semantic_model_name is required")
        
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/semanticModels/{semantic_model_id}"

        item_dict = self.calling_routine(url, operation="GET", response_codes=[200, 429],
                                            error_message="Error getting semantic model", return_format="json")
        semmodel = SemanticModel.from_dict(item_dict, core_client=self)
        semmodel.get_definition()

        return semmodel
    
    def delete_semantic_model(self, workspace_id, semantic_model_id):
        """Delete a semantic model from a workspace
        Args:
            workspace_id (str): The ID of the workspace
            semantic_model_id (str): The ID of the semantic model
        Returns:
            int: The status code of the response
        """
        return self.delete_item(workspace_id, semantic_model_id, type="semanticModels")
    
    # def update_semantic_model(self, workspace_id, semantic_model_id, display_name = None, description = None):
    #     """Update a semantic model in a workspace"""
    #     ws = self.get_workspace_by_id(workspace_id)
    #     return ws.update_semantic_model(semantic_model_id, display_name = display_name, description = description)
    
    def get_semantic_model_definition(self, workspace_id, semantic_model_id, format = None):
        """Get the definition of a semantic model
        Args:
            workspace_id (str): The ID of the workspace
            semantic_model_id (str): The ID of the semantic model
            format (str): The format of the definition
        Returns:
            dict: The definition of the semantic model
        """
        return self.get_item_definition(workspace_id, semantic_model_id, type="semanticModels", format=format)

    def update_semantic_model_definition(self, workspace_id, semantic_model_id, definition):
        """Update the definition of a semantic model
        Args:
            workspace_id (str): The ID of the workspace
            semantic_model_id (str): The ID of the semantic model
            definition (dict): The definition of the semantic model
        Returns:
            dict: The updated semantic model
        """
        return self.update_item_definition(workspace_id, semantic_model_id, definition, type="semanticModels", wait_for_completion=False)
    # spark workspace custom pools

    def create_workspace_custom_pool(self, workspace_id, name, node_family, node_size, auto_scale, dynamic_executor_allocation):
        """Create a workspace custom pool
        Args:
            workspace_id (str): The ID of the workspace
            name (str): The name of the pool
            node_family (str): The node family
            node_size (str): The node size
            auto_scale (bool): Whether to auto scale
            dynamic_executor_allocation (bool): Whether to use dynamic executor allocation
        Returns:
            SparkCustomPool: The created custom pool
        """
        from msfabricpysdkcore.spark_custom_pool import SparkCustomPool
        
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/spark/pools"

        body = {
            "name": name,
            "nodeFamily": node_family,
            "nodeSize": node_size,
            "autoScale": auto_scale,
            "dynamicExecutorAllocation": dynamic_executor_allocation
        }
        response_dict = self.calling_routine(url, operation="POST", body=body, response_codes=[200, 201, 429],
                                            error_message="Error creating workspace custom pool", return_format="json")

        response_dict["workspaceId"] = workspace_id
        return SparkCustomPool.from_dict(response_dict, core_client=self)

    def get_workspace_custom_pool(self, workspace_id, pool_id):
        """Get a workspace custom pool
        Args:
            workspace_id (str): The ID of the workspace
            pool_id (str): The ID of the pool
        Returns:
            SparkCustomPool: The custom pool
        """
        from msfabricpysdkcore.spark_custom_pool import SparkCustomPool

        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/spark/pools/{pool_id}"
        response_dict = self.calling_routine(url, operation="GET", response_codes=[200, 429],
                                            error_message="Error getting workspace custom pool", return_format="json")
        response_dict["workspaceId"] = workspace_id
        return SparkCustomPool.from_dict(response_dict, core_client=self)
    
    def delete_workspace_custom_pool(self, workspace_id, pool_id):
        """Delete a workspace custom pool
        Args:
            workspace_id (str): The ID of the workspace
            pool_id (str): The ID of the pool
        Returns:
            int: The status code of the response
        """

        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/spark/pools/{pool_id}"

        response = self.calling_routine(url, operation="DELETE", response_codes=[200, 429],
                                        error_message="Error deleting workspace custom pool", return_format="response")

        return response.status_code
    
    def list_workspace_custom_pools(self, workspace_id):
        """List workspace custom pools
        Args:
            workspace_id (str): The ID of the workspace
        Returns:
            list: The list of custom pools
        """
        from msfabricpysdkcore.spark_custom_pool import SparkCustomPool

        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/spark/pools"
        
        items = self.calling_routine(url, operation="GET", response_codes=[200, 429],
                                     error_message="Error listing workspace custom pools", return_format="json", paging=True)
        for item in items:
            item["workspaceId"] = workspace_id
        sppools = [SparkCustomPool.from_dict(item, core_client=self) for item in items]

        return sppools
    
    def update_workspace_custom_pool(self, workspace_id, pool_id, name = None, node_family = None, node_size = None, auto_scale = None, dynamic_executor_allocation = None,
                                     return_item = "Default"):
        """Update a workspace custom pool
        Args:
            workspace_id (str): The ID of the workspace
            pool_id (str): The ID of the pool
            name (str): The name of the pool
            node_family (str): The node family
            node_size (str): The node size
            auto_scale (bool): Whether to auto scale
            dynamic_executor_allocation (bool): Whether to use dynamic executor allocation
        Returns:
            int: The status code of the response
        """
        
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/spark/pools/{pool_id}"
        body = {}

        if name is not None:
            body['name'] = name
        if node_family is not None:
            body['nodeFamily'] = node_family
        if node_size is not None:
            body['nodeSize'] = node_size
        if auto_scale is not None:
            body['autoScale'] = auto_scale
        if dynamic_executor_allocation is not None:
            body['dynamicExecutorAllocation'] = dynamic_executor_allocation

        if not body:
            return None

        response_json = self.calling_routine(url, operation="PATCH", body=body, response_codes=[200, 429],
                                             error_message="Error updating workspace custom pool", return_format="json")

        if return_item == "Default":
            print("""Warning: Updating an item currently will make invoke an additional API call to get the item object.
                  This default behaviour will change in newer versions of the SDK.
                  To keep this behaviour, set return_item=True in the function call.""")
        if return_item:
            return self.get_workspace_custom_pool(workspace_id, pool_id)
        return response_json

    # Spark workspace settings

    def get_spark_settings(self, workspace_id):
        """Get spark settings for a workspace
        Args:
            workspace_id (str): The ID of the workspace
        Returns:
            dict: The spark settings
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/spark/settings"

        response_json = self.calling_routine(url, operation="GET", response_codes=[200, 429],
                                            error_message="Error getting spark settings", return_format="json")

        return response_json
    
    def update_spark_settings(self, workspace_id, automatic_log = None, 
                              environment = None, high_concurrency = None, pool = None):
        """Update spark settings for a workspace
        Args:
            workspace_id (str): The ID of the workspace
            automatic_log (bool): Whether to automatically log
            environment (str): The environment
            high_concurrency (bool): Whether to use high concurrency
            pool (str): The pool
        Returns:
            dict: The updated spark settings
        """

        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/spark/settings"

        body = {}

        if automatic_log:
            body["automaticLog"] = automatic_log
        if environment:
            body["environment"] = environment
        if high_concurrency:
            body["highConcurrency"] = high_concurrency
        if pool:
            body["pool"] = pool

        response_json = self.calling_routine(url, operation="PATCH", body=body, response_codes=[200, 429],
                                            error_message="Error updating spark settings", return_format="json")

        return response_json

    # sparkJobDefinitions

    def create_spark_job_definition(self, workspace_id, display_name, definition = None, description = None):
        """Create a spark job definition in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            display_name (str): The display name of the spark job definition
            definition (dict): The definition of the spark job definition
            description (str): The description of the spark job definition
        Returns:
            dict: The created spark job definition
        """
        return self.create_item(workspace_id = workspace_id, display_name = display_name, type = "sparkJobDefinitions", definition = definition, description = description)
    
    def delete_spark_job_definition(self, workspace_id, spark_job_definition_id):
        """Delete a spark job definition from a workspace
        Args:
            workspace_id (str): The ID of the workspace
            spark_job_definition_id (str): The ID of the spark job definition
        Returns:
            int: The status code of the response
        """
        return self.delete_item(workspace_id, spark_job_definition_id, type="sparkJobDefinitions")

    def get_spark_job_definition(self, workspace_id, spark_job_definition_id = None, spark_job_definition_name = None):
        """Get a spark job definition from a workspace
        Args:
            workspace_id (str): The ID of the workspace
            spark_job_definition_id (str): The ID of the spark job definition
            spark_job_definition_name (str): The name of the spark job definition
        Returns:
            SparkJobDefinition: The spark job definition object
        """
        from msfabricpysdkcore.otheritems import SparkJobDefinition
        if spark_job_definition_id is None and spark_job_definition_name is not None:
            spark_job_definitions = self.list_spark_job_definitions(workspace_id)
            spark_job_definitions = [sjd for sjd in spark_job_definitions if sjd.display_name == spark_job_definition_name]
            if len(spark_job_definitions) == 0:
                raise Exception(f"Spark job definition with name {spark_job_definition_name} not found")
            spark_job_definition_id = spark_job_definitions[0].id
        elif spark_job_definition_id is None:
            raise Exception("spark_job_definition_id or the spark_job_definition_name is required")
        
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/sparkjobdefinitions/{spark_job_definition_id}"

        item_dict = self.calling_routine(url, operation="GET", response_codes=[200, 429],
                                         error_message="Error getting spark job definition", return_format="json")

        sjd_obj =  SparkJobDefinition.from_dict(item_dict, core_client=self)
        sjd_obj.get_definition()
        return sjd_obj
    
    def list_spark_job_definitions(self, workspace_id, with_properties = False):
        """List spark job definitions in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            with_properties (bool): Whether to get the item object with properties
        Returns:
            list: The list of spark job definitions
        """
        return self.list_items(workspace_id = workspace_id, type = "sparkJobDefinitions", with_properties = with_properties)
    
    def update_spark_job_definition(self, workspace_id, spark_job_definition_id, display_name = None, description = None, return_item="Default"):
        """Update a spark job definition in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            spark_job_definition_id (str): The ID of the spark job definition
            display_name (str): The display name of the spark job definition
            description (str): The description of the spark job definition
        Returns:
            dict: The updated spark job definition
        """
        return self.update_item(workspace_id, spark_job_definition_id, display_name = display_name, description = description,
                                type="sparkJobDefinitions", return_item=return_item)
    
    def get_spark_job_definition_definition(self, workspace_id, spark_job_definition_id, format = None):
        """Get the definition of a spark job definition
        Args:
            workspace_id (str): The ID of the workspace
            spark_job_definition_id (str): The ID of the spark job definition
            format (str): The format of the definition
        Returns:
            dict: The definition of the spark job definition
        """
        return self.get_item_definition(workspace_id, spark_job_definition_id, type="sparkJobDefinitions", format=format)

    def update_spark_job_definition_definition(self, workspace_id, spark_job_definition_id, definition):
        """Update the definition of a spark job definition
        Args:
            workspace_id (str): The ID of the workspace
            spark_job_definition_id (str): The ID of the spark job definition
            definition (dict): The definition of the spark job definition
        Returns:
            dict: The updated spark job definition
        """
        return self.update_item_definition(workspace_id, spark_job_definition_id, definition, type="sparkJobDefinitions")
    
    def run_on_demand_spark_job_definition(self, workspace_id, spark_job_definition_id, job_type = "sparkjob"):
        """Run an on demand spark job definition
        Args:
            workspace_id (str): The ID of the workspace
            spark_job_definition_id (str): The ID of the spark job definition
            job_type (str): The job type
        Returns:
            dict: The job instance
        """
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/sparkJobDefinitions/{spark_job_definition_id}/jobs/instances?jobType={job_type}"

        response = self.calling_routine(url, operation="POST", response_codes=[202, 429], wait_for_completion = False,
                                        error_message="Error running on demand spark job definition", return_format="response")

        location = response.headers['Location']
        job_instance_id = location.split('/')[-1]

        return self.get_item_job_instance(workspace_id = workspace_id,
                                          item_id = spark_job_definition_id,
                                          job_instance_id = job_instance_id)

    
    # warehouses

    def create_warehouse(self, workspace_id, display_name, description = None):
        """Create a warehouse in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            display_name (str): The display name of the warehouse
            description (str): The description of the warehouse
        Returns:
            dict: The created warehouse
        """
        return self.create_item(workspace_id = workspace_id, display_name = display_name, type = "warehouses", description = description)
    
    def delete_warehouse(self, workspace_id, warehouse_id):
        """Delete a warehouse from a workspace
        Args:
            workspace_id (str): The ID of the workspace
            warehouse_id (str): The ID of the warehouse
        Returns:
            int: The status code of the response
        """
        return self.delete_item(workspace_id, warehouse_id, type="warehouses")
    
    def get_warehouse(self, workspace_id, warehouse_id = None, warehouse_name = None):
        """Get a warehouse from a workspace
        Args:
            workspace_id (str): The ID of the workspace
            warehouse_id (str): The ID of the warehouse
            warehouse_name (str): The name of the warehouse
        Returns:
            Warehouse: The warehouse object
        """
        from msfabricpysdkcore.otheritems import Warehouse
        if warehouse_id is None and warehouse_name is not None:
            warehouses = self.list_warehouses(workspace_id)
            warehouses = [wh for wh in warehouses if wh.display_name == warehouse_name]
            if len(warehouses) == 0:
                raise Exception(f"Warehouse with name {warehouse_name} not found")
            warehouse_id = warehouses[0].id
        if warehouse_id is None:
            raise Exception("warehouse_id or the warehouse_name is required")
        
        url = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/warehouses/{warehouse_id}"

        item_dict = self.calling_routine(url, operation="GET", response_codes=[200, 429],
                                         error_message="Error getting warehouse", return_format="json")
        
        return Warehouse.from_dict(item_dict, core_client=self)
        
    def list_warehouses(self, workspace_id, with_properties = False):
        """List warehouses in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            with_properties (bool): Whether to get the item object with properties
        Returns:
            list: The list of warehouses
        """
        return self.list_items(workspace_id = workspace_id, type = "warehouses", with_properties = with_properties)

    def update_warehouse(self, workspace_id, warehouse_id, display_name = None, description = None, return_item="Default"):
        """Update a warehouse in a workspace
        Args:
            workspace_id (str): The ID of the workspace
            warehouse_id (str): The ID of the warehouse
            display_name (str): The display name of the warehouse
            description (str): The description of the warehouse
        Returns:
            dict: The updated warehouse
        """
        return self.update_item(workspace_id, warehouse_id, display_name = display_name, description = description, 
                                type="warehouses", return_item=return_item)
