Public/cloud-network.ps1

function Get-CloudNetwork {
    <#
    .SYNOPSIS
        Gets networks from the Cloud Server.
     
    .DESCRIPTION
        Retrieves a list of networks. Automatically handles token refresh.
     
    .PARAMETER Name
        Optional. Filter by network name.
     
    .PARAMETER ID
        Optional. Filter by network ID
     
    .EXAMPLE
        # Get all networks
        Get-CloudNetwork
         
    .EXAMPLE
        # Get network by ID
        Get-CloudNetwork -ID 5
         
    .EXAMPLE
        # Get network by name
        Get-CloudNetwork -Name "VPN-net"
    #>

    
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false)]
        [string]$Name,
        
        [Parameter(Mandatory = $false)]
        [Nullable[int]]$ID
    )
    
    $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/network"
    
    if ($PSBoundParameters.ContainsKey('ID')) {
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/network/${ID}"
        $response = Invoke-CloudApiRequest -Uri $uri -Method Get
        $network = $response.network
        return $network
    }
    else {
        $response = Invoke-CloudApiRequest -Uri $uri -Method Get
        $networks = $response.networks
        
        if ($Name) {
            $networks = $networks | Where-Object { $_.name -like "*$Name*" }
        }
        
        return $networks
    }
}

function Get-CloudNetworkTemplate {
    <#
    .SYNOPSIS
        Gets network templates from the Cloud Server.
     
    .DESCRIPTION
        Retrieves a list of network templates. Automatically handles token refresh.
     
    .PARAMETER Name
        Optional. Filter by network template name.
     
    .PARAMETER ID
        Optional. Filter by network template ID (can be 0)
     
    .EXAMPLE
        # Get all network templates
        Get-CloudNetworkTemplate
         
    .EXAMPLE
        # Get network template by ID
        Get-CloudNetworkTemplate -ID 5
         
    .EXAMPLE
        # Get network template by name
        Get-CloudNetworkTemplate -Name "Linux-Net-Template"
    #>

    
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false)]
        [string]$Name,
        
        [Parameter(Mandatory = $false)]
        [Nullable[int]]$ID
    )
    
    $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/network/template"
    
    if ($PSBoundParameters.ContainsKey('ID')) {
        # Query specific template by ID
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/network/template/${ID}"
        $response = Invoke-CloudApiRequest -Uri $uri -Method Get
        
        # If the API returns the template directly, return it
        # Otherwise filter (adjust based on actual API response structure)
        return $response.template
    }
    else {
        # Query all templates
        $response = Invoke-CloudApiRequest -Uri $uri -Method Get
        $nettemplates = $response.template
        
        if ($Name) {
            $nettemplates = $nettemplates | Where-Object { $_.name -like "*$Name*" }
        }
        
        return $nettemplates
    }
}

function New-CloudNetworkTemplate {
    <#
    .SYNOPSIS
        Creates a new virtual network template in the Cloud Server.
     
    .DESCRIPTION
        Creates a virtual network template. Automatically handles token refresh.
     
    .PARAMETER Name
        Name of the network template
         
    .PARAMETER VnMad
        Virtual Network Manager driver (e.g., "bridge", "vxlan", "ovswitch")
         
    .PARAMETER Bridge
        Bridge name for the network
         
    .PARAMETER NetworkAddress
        Network address (e.g., "192.168.1.0")
         
    .PARAMETER NetworkMask
        Network mask (e.g., "255.255.255.0")
         
    .PARAMETER Gateway
        Optional gateway address
         
    .PARAMETER DNS
        Optional DNS server address
         
    .PARAMETER Description
        Optional description of the network template
     
    .PARAMETER Template
        Full network template as a string (alternative to individual parameters)
     
    .EXAMPLE
        # Create a new network template with individual flags
        New-CloudNetworkTemplate -Name "office-network" -VnMad "bridge" -Bridge "br0" -NetworkAddress "192.168.1.0" -NetworkMask "255.255.255.0"
         
    .EXAMPLE
        # Create a new network template with individual flags
        New-CloudNetworkTemplate -Name "dmz-network" -VnMad "bridge" -Bridge "br1" -NetworkAddress "10.0.0.0" -NetworkMask "255.255.255.0" -Gateway "10.0.0.1" -DNS "8.8.8.8"
         
    .EXAMPLE
        # Create a new network template with a template variable
        $template = @"
    name = "custom-network"
    vn_mad = "bridge"
    bridge = "br0"
    network_address = "172.16.0.0"
    network_mask = "255.255.0.0"
    gateway = "172.16.0.1"
    dns = "8.8.8.8 8.8.4.4"
    description = "Custom network template"
    "@
        New-CloudNetworkTemplate -Template $template
    #>

    
    [CmdletBinding(DefaultParameterSetName='Individual')]
    param(
        [Parameter(Mandatory = $true, ParameterSetName='Individual')]
        [string]$Name,
        
        [Parameter(Mandatory = $true, ParameterSetName='Individual')]
        [ValidateSet('bridge', 'vxlan', 'ovswitch', 'ebtables', 'fw', '802.1Q', 'vlan')]
        [string]$VnMad,
        
        [Parameter(Mandatory = $true, ParameterSetName='Individual')]
        [string]$Bridge,
        
        [Parameter(Mandatory = $true, ParameterSetName='Individual')]
        [string]$NetworkAddress,
        
        [Parameter(Mandatory = $true, ParameterSetName='Individual')]
        [string]$NetworkMask,
        
        [Parameter(Mandatory = $false, ParameterSetName='Individual')]
        [string]$Gateway,
        
        [Parameter(Mandatory = $false, ParameterSetName='Individual')]
        [string]$DNS,
        
        [Parameter(Mandatory = $false, ParameterSetName='Individual')]
        [string]$Description,
        
        [Parameter(Mandatory = $true, ParameterSetName='Template')]
        [string]$Template
    )
    
    # Build template string if using individual parameters
    if ($PSCmdlet.ParameterSetName -eq 'Individual') {
        $templateLines = @(
            "name = `"$Name`""
            "vn_mad = `"$VnMad`""
            "bridge = `"$Bridge`""
            "network_address = `"$NetworkAddress`""
            "network_mask = `"$NetworkMask`""
        )
        
        # Add optional parameters if provided
        if ($Gateway) {
            $templateLines += "gateway = `"$Gateway`""
        }
        
        if ($DNS) {
            $templateLines += "dns = `"$DNS`""
        }
        
        if ($Description) {
            $templateLines += "description = `"$Description`""
        }
        
        $Template = $templateLines -join "`n"
    }
    
    # Pass hashtable directly - Invoke-CloudApiRequest will convert to JSON
    $templateData = @{
        template = $Template
    }
    
    $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/network/template"
    
    Write-Verbose "Request URI: $uri"
    Write-Verbose "Network Template:`n$Template"
    
    $response = Invoke-CloudApiRequest -Uri $uri -Method Post -Body $templateData
    
    Write-Host "Network template created successfully." -ForegroundColor Green
    
    return $response
}

function Update-CloudNetworkOwner {
    <#
    .SYNOPSIS
        Updates the ownership of a network in the Cloud Server.
     
    .DESCRIPTION
        Changes the user and/or group ownership of a network. Prompts for confirmation.
     
    .PARAMETER ID
        Required. ID of the network.
     
    .PARAMETER UserID
        Required. ID of the user to own the network.
         
    .PARAMETER GroupID
        Optional. ID of the group to own the network.
         
    .EXAMPLE
        # Change the owner of a network to a specific user
        Update-CloudNetworkOwner -ID 5 -UserID 2
         
    .EXAMPLE
        # Change the owner of a network to a specific user and group
        Update-CloudNetworkOwner -ID 5 -UserID 2 -GroupID 100
         
    .EXAMPLE
        # Change the owner of a network to a specific user, bypassing confirmation prompt
        Update-CloudNetworkOwner -ID 5 -UserID 2 -Confirm:$false
    #>

    
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [int]$ID,
        
        [Parameter(Mandatory = $true)]
        [Alias('User')]
        [int]$UserID,
                
        [Parameter(Mandatory = $false)]
        [Alias('Group')]
        [int]$GroupID
    )
    
    process {
        # Build the update body
        $body = [PSCustomObject]@{
            user = $UserID
        }
        
        if ($PSBoundParameters.ContainsKey('GroupID')) {
            $body | Add-Member -NotePropertyName 'group' -NotePropertyValue $GroupID
        }
        
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/network/${ID}/ownership"
        
        # Build action description for confirmation
        $actionParts = @("Change owner to User ID $UserID")
        if ($PSBoundParameters.ContainsKey('GroupID')) {
            $actionParts += "Group ID $GroupID"
        }
        $actionDescription = $actionParts -join " and "
        
        # Use the helper function
        Invoke-CloudResourceUpdate `
            -CallerPSCmdlet $PSCmdlet `
            -ResourceType "Network" `
            -ID $ID `
            -Uri $uri `
            -Body $body `
            -Action $actionDescription `
            -GetResourceScript { Get-CloudNetwork -ID $ID } `
            -SuccessMessage "Network $ID ownership updated successfully."
    }
}

function Update-CloudNetworkPermissions {
    <#
    .SYNOPSIS
        Updates the permissions of a network in the Cloud Server.
     
    .DESCRIPTION
        Changes the permission of a network. Prompts for confirmation.
     
    .PARAMETER ID
        Required. ID of the network.
     
    .PARAMETER OwnerUse
        Optional. True or false, enable OwnerUse
 
    .PARAMETER OwnerManage
        Optional. True or false, enable OwnerManage
 
    .PARAMETER OwnerAdmin
        Optional. True or false, enable OwnerAdmin
 
    .PARAMETER GroupUse
        Optional. True or false, enable GroupUse
         
    .PARAMETER GroupManage
        Optional. True or false, enable GroupManage
         
    .PARAMETER GroupAdmin
        Optional. True or false, enable GroupAdmin
         
    .PARAMETER OtherUse
        Optional. True or false, enable OtherUse
         
    .PARAMETER OtherManage
        Optional. True or false, enable OtherManage
         
    .PARAMETER OtherAdmin
        Optional. True or false, enable OtherManage
         
    .EXAMPLE
        # Give group users permission to use the network
        Update-CloudNetworkPermissions -ID 35 -GroupUse $true
         
    .EXAMPLE
        # Deny group admin permission to the network
        Update-CloudNetworkPermissions -ID 35 -GroupAdmin $false
         
    .EXAMPLE
        # Set multiple permissions at once
        Update-CloudNetworkPermissions -ID 35 -GroupUse $true -GroupManage $true -OtherUse $true
    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [int]$ID,
        
        [Parameter(Mandatory = $false)]
        [bool]$OwnerUse,
        
        [Parameter(Mandatory = $false)]
        [bool]$OwnerManage,
        
        [Parameter(Mandatory = $false)]
        [bool]$OwnerAdmin,
        
        [Parameter(Mandatory = $false)]
        [bool]$GroupUse,
        
        [Parameter(Mandatory = $false)]
        [bool]$GroupManage,
        
        [Parameter(Mandatory = $false)]
        [bool]$GroupAdmin,
        
        [Parameter(Mandatory = $false)]
        [bool]$OtherUse,
        
        [Parameter(Mandatory = $false)]
        [bool]$OtherManage,
        
        [Parameter(Mandatory = $false)]
        [bool]$OtherAdmin
    )
    
    process {
        # Build the permissions object structure as shown in the API spec
        $permissions = @{}
        
        if ($PSBoundParameters.ContainsKey('OwnerUse')) { $permissions['owner_use'] = $OwnerUse }
        if ($PSBoundParameters.ContainsKey('OwnerManage')) { $permissions['owner_manage'] = $OwnerManage }
        if ($PSBoundParameters.ContainsKey('OwnerAdmin')) { $permissions['owner_admin'] = $OwnerAdmin }
        
        if ($PSBoundParameters.ContainsKey('GroupUse')) { $permissions['group_use'] = $GroupUse }
        if ($PSBoundParameters.ContainsKey('GroupManage')) { $permissions['group_manage'] = $GroupManage }
        if ($PSBoundParameters.ContainsKey('GroupAdmin')) { $permissions['group_admin'] = $GroupAdmin }
        
        if ($PSBoundParameters.ContainsKey('OtherUse')) { $permissions['other_use'] = $OtherUse }
        if ($PSBoundParameters.ContainsKey('OtherManage')) { $permissions['other_manage'] = $OtherManage }
        if ($PSBoundParameters.ContainsKey('OtherAdmin')) { $permissions['other_admin'] = $OtherAdmin }
        
        # Create the body with the permissions nested structure
        $body = @{
            permissions = $permissions
        }
        
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/network/${ID}/permissions"
        
        Invoke-CloudResourceUpdate `
            -CallerPSCmdlet $PSCmdlet `
            -ResourceType "Network" `
            -ID $ID `
            -Uri $uri `
            -Body $body `
            -Action "Update permissions" `
            -GetResourceScript { Get-CloudNetwork -ID $ID }
    }
}

function New-CloudNetwork {
    <#
    .SYNOPSIS
        Creates a new virtual network on the Cloud Server.
     
    .DESCRIPTION
        Creates a new virtual network using a template string or by specifying a cluster ID
        and template configuration.
     
    .PARAMETER ClusterID
        Required. The cluster ID where the network will be created.
     
    .PARAMETER TemplateText
        Required. The network template in OpenNebula format. This should be a multi-line string
        containing the network configuration.
     
    .PARAMETER Name
        Optional. Name of the network. Can be included in the template text instead.
     
    .PARAMETER Bridge
        Optional. Bridge name (e.g., 'br0', 'virbr0').
     
    .PARAMETER BridgeType
        Optional. Bridge type (e.g., 'linux', 'openvswitch').
     
    .PARAMETER VNMad
        Optional. Virtual Network Manager driver (e.g., 'fw', 'bridge', 'ovswitch').
     
    .PARAMETER Gateway
        Optional. Gateway IP address.
     
    .PARAMETER NetworkMask
        Optional. Network mask (e.g., '255.255.255.0').
     
    .PARAMETER DNS
        Optional. DNS servers (space-separated string, e.g., "8.8.8.8 8.8.4.4").
     
    .PARAMETER NetworkAddress
        Optional. Network address (e.g., '192.168.1.0').
     
    .PARAMETER PassThru
        Optional. If specified, returns the created network object.
     
    .EXAMPLE
        # Create a network
        New-CloudNetwork -ClusterID 0 -TemplateText @"
        NAME = "Production Network"
        BRIDGE = br0
        BRIDGE_TYPE = linux
        VN_MAD = fw
        GATEWAY = 192.168.1.1
        NETWORK_MASK = 255.255.255.0
        VLAN_ID = 100
        VLAN = YES
        DNS = "8.8.8.8 8.8.4.4"
        "@
         
    .EXAMPLE
        # Create a network using individual parameters
        New-CloudNetwork -ClusterID 0 -Name "Dev Network" -Bridge "virbr0" -BridgeType "linux" -VNMad "fw" -Gateway "10.0.0.1" -NetworkMask "255.255.255.0"
         
    .EXAMPLE
        # Create and return the network object
        $network = New-CloudNetwork -ClusterID 0 -Name "Test Net" -Bridge "br1" -PassThru
    #>

    
    [CmdletBinding(DefaultParameterSetName='Template', SupportsShouldProcess, ConfirmImpact='Medium')]
    param(
        [Parameter(Mandatory = $true, ParameterSetName='Template')]
        [Parameter(Mandatory = $true, ParameterSetName='Parameters')]
        [int]$ClusterID,
        
        [Parameter(Mandatory = $true, ParameterSetName='Template')]
        [string]$TemplateText,
        
        [Parameter(Mandatory = $true, ParameterSetName='Parameters')]
        [string]$Name,
        
        [Parameter(Mandatory = $false, ParameterSetName='Parameters')]
        [string]$Bridge,
        
        [Parameter(Mandatory = $false, ParameterSetName='Parameters')]
        [string]$BridgeType,
        
        [Parameter(Mandatory = $false, ParameterSetName='Parameters')]
        [string]$VNMad,
        
        [Parameter(Mandatory = $false, ParameterSetName='Parameters')]
        [string]$Gateway,
        
        [Parameter(Mandatory = $false, ParameterSetName='Parameters')]
        [string]$NetworkMask,
        
        [Parameter(Mandatory = $false, ParameterSetName='Parameters')]
        [string]$DNS,
        
        [Parameter(Mandatory = $false, ParameterSetName='Parameters')]
        [string]$NetworkAddress,
        
        [Parameter(Mandatory = $false, ParameterSetName='Parameters')]
        [string]$Description,
        
        [Parameter(Mandatory = $false)]
        [switch]$PassThru
    )
    
    process {
        # Build template string based on parameter set
        if ($PSCmdlet.ParameterSetName -eq 'Parameters') {
            $templateComponents = @()
            
            $templateComponents += "NAME = `"$Name`""
            
            if ($Bridge) { $templateComponents += "BRIDGE = $Bridge" }
            if ($BridgeType) { $templateComponents += "BRIDGE_TYPE = $BridgeType" }
            if ($VNMad) { $templateComponents += "VN_MAD = $VNMad" }
            if ($Gateway) { $templateComponents += "GATEWAY = $Gateway" }
            if ($NetworkMask) { $templateComponents += "NETWORK_MASK = $NetworkMask" }
            if ($DNS) { $templateComponents += "DNS = `"$DNS`"" }
            if ($NetworkAddress) { $templateComponents += "NETWORK_ADDRESS = $NetworkAddress" }
            if ($Description) { $templateComponents += "DESCRIPTION = `"$Description`"" }
            
            $TemplateText = $templateComponents -join "`n"
        }
        
        Write-Verbose "Network template:`n$TemplateText"
        
        # Build the request body - template must be a string
        $body = @{
            cluster = $ClusterID
            template = $TemplateText
        }
        
        Write-Verbose "Request body: $($body | ConvertTo-Json)"
        
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/network"
        
        $networkName = if ($PSCmdlet.ParameterSetName -eq 'Parameters') { "'$Name'" } else { "in cluster $ClusterID" }
        
        if ($PSCmdlet.ShouldProcess($networkName, "Create new network")) {
            try {
                $response = Invoke-RestMethod -Uri $uri -Method Post -Body ($body | ConvertTo-Json) -ContentType 'application/json' -Headers $script:CloudConnection.Headers -UseBasicParsing
                
                # Extract the network ID from the response
                $networkId = $response.network
                
                Write-Host "Successfully created network with ID: $networkId" -ForegroundColor Green
                
                if ($PassThru) {
                    # Return the newly created network object
                    Get-CloudNetwork -ID $networkId
                } else {
                    return $networkId
                }
            }
            catch {
                Write-Error "Failed to create network: $_"
                throw
            }
        }
    }
}

function Rename-CloudNetwork {
    <#
    .SYNOPSIS
        Rename a virtual network in the Cloud Server.
     
    .DESCRIPTION
        Renames a virtual network. Automatically handles token refresh.
     
    .PARAMETER Name
        New name of the network
         
    .PARAMETER ID
        ID of the network
             
    .EXAMPLE
        # Rename a cloud network
        Rename-CloudNetwork -Name "new-name" -ID 5
    #>

    
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Name,
        
        [Parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true)]
        [int]$ID
    )
        
    $newname = @{
        name = $Name
    }
    
    $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/network/${ID}/name"
       
    $response = Invoke-CloudApiRequest -Uri $uri -Method Patch -Body $newname
    
    return $response
}

function Remove-CloudNetwork {
    <#
    .SYNOPSIS
        Removes a network from the Cloud Server.
     
    .DESCRIPTION
        Removes a network. Automatically handles token refresh.
     
    .PARAMETER Name
        Required. Network ID.
     
    .EXAMPLE
        # Remove network with confirmation prompt (default behavior):
        Remove-CloudNetwork -ID 3
        Confirm
        Are you sure you want to perform this action?
        Performing the operation "Remove Network" on target "Network ID 3 (MyNetwork)".
        [Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"): Y
 
    .EXAMPLE
        # Remove network without confirmation prompt:
        Remove-CloudNetwork -ID 3 -Confirm:$false
    #>

    
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [int]$ID
    )
    
    process {
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/network/${ID}"
        
        Invoke-CloudResourceRemoval `
            -CallerPSCmdlet $PSCmdlet `
            -ResourceType "Network" `
            -ID $ID `
            -Uri $uri `
            -GetResourceScript { Get-CloudNetwork -ID $ID }
    }
}

function Lock-CloudNetwork {
    <#
    .SYNOPSIS
        Lock a virtual network in the Cloud Server.
     
    .DESCRIPTION
        Lock a virtual network. Automatically handles token refresh.
        
    .PARAMETER ID
        ID of the network
     
    .PARAMETER Level
        Lock level (USE, MANAGE, ADMIN, ALL)
     
    .PARAMETER Test
        When set, performs a test lock instead of a real one.
     
    .EXAMPLE
        # Lock a cloud network to admin operations
        Lock-CloudNetwork -ID 5 -Level ADMIN
     
    .EXAMPLE
        # Test locking a cloud network to use operations
        Lock-CloudNetwork -ID 5 -Level USE -Test
     
    .EXAMPLE
        # Lock a cloud network using a pipeline input
        Get-CloudNetwork -ID 3 | Lock-CloudNetwork -Level MANAGE
    #>

    
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [int]$ID,
        
        [Parameter(Mandatory = $true)]
        [ValidateSet('USE','MANAGE','ADMIN','ALL')]
        [string]$Level,
        
        [Parameter(Mandatory = $false)]
        [switch]$Test
    )
    
    process {
        $body = @{
            level = $Level
            test  = $Test.IsPresent
        }
        
        Write-Verbose "Lock body: $($body | ConvertTo-Json)"
        
        # Use the correct lock endpoint
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/network/${ID}/lock"
        
        if ($PSCmdlet.ShouldProcess("Network ID $ID", "Lock network at level $Level")) {
            try {
                $response = Invoke-RestMethod -Uri $uri -Method PATCH -Body ($body | ConvertTo-Json) -ContentType 'application/json' -Headers $script:CloudConnection.Headers -UseBasicParsing
                
                Write-Host "Successfully locked network ID $ID at level $Level" -ForegroundColor Green
                
            }
            catch {
                Write-Error "Failed to lock network ID ${ID}: $_"
                throw
            }
        }
    }
}

function Unlock-CloudNetwork {
    <#
    .SYNOPSIS
        Unlock a virtual network in the Cloud Server.
     
    .DESCRIPTION
        Unlock a virtual network. Automatically handles token refresh.
        
    .PARAMETER ID
        ID of the network
     
    .PARAMETER Level
        Unlock level (USE, MANAGE, ADMIN, ALL)
     
    .PARAMETER Test
        When set, performs a test unlock instead of a real one.
     
    .EXAMPLE
        # Unlock a cloud network
        Unlock-CloudNetwork -ID 5
     
    .EXAMPLE
        # Unlock a cloud network using a pipeline input
        Get-CloudNetwork -ID 3 | Unlock-CloudNetwork
    #>

    
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [int]$ID,
        
        [Parameter(Mandatory = $false)]
        [switch]$Test
    )
    
    process {
        $body = @{
            test  = $Test.IsPresent
        }
        
        Write-Verbose "Unlock body: $($body | ConvertTo-Json)"
        
        # Use the correct unlock endpoint
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/network/${ID}/unlock"
        
        if ($PSCmdlet.ShouldProcess("Network ID $ID", "Unlock network")) {
            try {
                $response = Invoke-RestMethod -Uri $uri -Method PATCH -Body ($body | ConvertTo-Json) -ContentType 'application/json' -Headers $script:CloudConnection.Headers -UseBasicParsing
                
                Write-Host "Successfully unlocked network ID $ID" -ForegroundColor Green
                
            }
            catch {
                Write-Error "Failed to unlock network ID ${ID}: $_"
                throw
            }
        }
    }
}

function Copy-CloudNetwork {
    <#
    .SYNOPSIS
        Clone a network in the Cloud Server.
     
    .DESCRIPTION
        Creates a clone network. Automatically handles token refresh.
     
    .PARAMETER Name
        Required. Name of the new network
         
    .PARAMETER ID
        Required. ID of the the network you wish to clone
 
    .EXAMPLE
        # Clone the network and do not clone the associated image
        Copy-CloudNetwork -Name "gateway-network" -ID 5
 
    #>

    
    [CmdletBinding(DefaultParameterSetName='Individual')]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Name,
        
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [int]$ID
    )


    $networkData = [PSCustomObject]@{
    NAME=$Name
    }
 
    $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/network/${ID}/clone"
    
    Write-Verbose "Request URI: $uri"
    Write-Verbose "Network:`n$NetworkData"
    
    $response = Invoke-CloudApiRequest -Uri $uri -Method POST -Body $networkData
    
    return $response
}
 
function Lock-CloudNetworkTemplate {
    <#
    .SYNOPSIS
        Lock a virtual network template in the Cloud Server.
     
    .DESCRIPTION
        Lock a virtual network template. Automatically handles token refresh.
        
    .PARAMETER ID
        ID of the network
     
    .PARAMETER Level
        Lock level (USE, MANAGE, ADMIN, ALL)
     
    .PARAMETER Test
        When set, performs a test lock instead of a real one.
     
    .EXAMPLE
        # Lock a cloud network template to admin operations
        Lock-CloudNetworkTemplate -ID 5 -Level ADMIN
     
    .EXAMPLE
        # Test locking a cloud network template to use operations
        Lock-CloudNetworkTemplate -ID 5 -Level USE -Test
     
    .EXAMPLE
        # Lock a network template using a pipeline input
        Get-CloudNetworkTemplate -ID 3 | Lock-CloudNetworkTemplate -Level MANAGE
    #>

    
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [int]$ID,
        
        [Parameter(Mandatory = $true)]
        [ValidateSet('USE','MANAGE','ADMIN','ALL')]
        [string]$Level,
        
        [Parameter(Mandatory = $false)]
        [switch]$Test
    )
    
    process {
        $body = @{
            level = $Level
            test  = $Test.IsPresent
        }
        
        Write-Verbose "Lock body: $($body | ConvertTo-Json)"
        
        # Use the correct lock endpoint
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/network/template/${ID}/lock"
        
        if ($PSCmdlet.ShouldProcess("Network ID $ID", "Lock network at level $Level")) {
            try {
                $response = Invoke-RestMethod -Uri $uri -Method PATCH -Body ($body | ConvertTo-Json) -ContentType 'application/json' -Headers $script:CloudConnection.Headers -UseBasicParsing
                
                Write-Host "Successfully locked network ID $ID at level $Level" -ForegroundColor Green
                
            }
            catch {
                Write-Error "Failed to lock network ID ${ID}: $_"
                throw
            }
        }
    }
}

function Unlock-CloudNetworkTemplate {
    <#
    .SYNOPSIS
        Unlock a virtual network template in the Cloud Server.
     
    .DESCRIPTION
        Unlock a virtual network template. Automatically handles token refresh.
        
    .PARAMETER ID
        ID of the network
     
    .PARAMETER Level
        Unlock level (USE, MANAGE, ADMIN, ALL)
     
    .PARAMETER Test
        When set, performs a test unlock instead of a real one.
     
    .EXAMPLE
        # Unlock a network template
        Unlock-CloudNetworkTemplate -ID 5
        
    .EXAMPLE
        # Unlock a network template using a pipeline input
        Get-CloudNetworkTemplate -ID 3 | Unlock-CloudNetworkTemplate
    #>

    
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [int]$ID,
        
        [Parameter(Mandatory = $false)]
        [switch]$Test
    )
    
    process {
        $body = @{
            test  = $Test.IsPresent
        }
        
        Write-Verbose "Unlock body: $($body | ConvertTo-Json)"
        
        # Use the correct unlock endpoint
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/network/template/${ID}/unlock"
        
        if ($PSCmdlet.ShouldProcess("Network ID $ID", "Unlock network")) {
            try {
                $response = Invoke-RestMethod -Uri $uri -Method PATCH -Body ($body | ConvertTo-Json) -ContentType 'application/json' -Headers $script:CloudConnection.Headers -UseBasicParsing
                
                Write-Host "Successfully unlocked network ID $ID" -ForegroundColor Green
                
            }
            catch {
                Write-Error "Failed to unlock network ID ${ID}: $_"
                throw
            }
        }
    }
}

function Copy-CloudNetworkTemplate {
    <#
    .SYNOPSIS
        Clone a network in the Cloud Server.
     
    .DESCRIPTION
        Creates a clone network. Automatically handles token refresh.
     
    .PARAMETER Name
        Required. Name of the new network
         
    .PARAMETER ID
        Required. ID of the the network you wish to clone
         
    .EXAMPLE
        # Copy a network template
        Copy-CloudNetworkTemplate -Name "new-gateway-network" -ID 5
 
    #>

    
    [CmdletBinding(DefaultParameterSetName='Individual')]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Name,
        
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [int]$ID
    )

    

    $networkData = [PSCustomObject]@{
    NAME=$Name
    }
 
    $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/network/template/${ID}/clone"
    
    Write-Verbose "Request URI: $uri"
    Write-Verbose "Network:`n$NetworkData"
    
    $response = Invoke-CloudApiRequest -Uri $uri -Method POST -Body $networkData
    
    return $response
}
 
function Update-CloudNetworkTemplateOwner {
    <#
    .SYNOPSIS
        Updates the ownership of a network in the Cloud Server.
     
    .DESCRIPTION
        Changes the user and/or group ownership of a network. Prompts for confirmation.
     
    .PARAMETER ID
        Required. ID of the network.
     
    .PARAMETER UserID
        Required. ID of the user to own the network.
         
    .PARAMETER GroupID
        Optional. ID of the group to own the network.
         
    .EXAMPLE
        # Set a network template's owner to a user
        Update-CloudNetworkTemplateOwner -ID 5 -UserID 2
         
    .EXAMPLE
        # Set a network template's owner to a user and group
        Update-CloudNetworkTemplateOwner -ID 5 -UserID 2 -GroupID 100
         
    .EXAMPLE
        # Set a network template's owner to a user, bypassing the confirmation prompt
        Update-CloudNetworkTemplateOwner -ID 5 -UserID 2 -Confirm:$false
    #>

    
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [int]$ID,
        
        [Parameter(Mandatory = $true)]
        [Alias('User')]
        [int]$UserID,
                
        [Parameter(Mandatory = $false)]
        [Alias('Group')]
        [int]$GroupID
    )
    
    process {
        # Build the update body
        $body = [PSCustomObject]@{
            user = $UserID
        }
        
        if ($PSBoundParameters.ContainsKey('GroupID')) {
            $body | Add-Member -NotePropertyName 'group' -NotePropertyValue $GroupID
        }
        
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/network/template/${ID}/ownership"
        
        # Build action description for confirmation
        $actionParts = @("Change owner to User ID $UserID")
        if ($PSBoundParameters.ContainsKey('GroupID')) {
            $actionParts += "Group ID $GroupID"
        }
        $actionDescription = $actionParts -join " and "
        
        # Use the helper function
        Invoke-CloudResourceUpdate `
            -CallerPSCmdlet $PSCmdlet `
            -ResourceType "Network" `
            -ID $ID `
            -Uri $uri `
            -Body $body `
            -Action $actionDescription `
            -GetResourceScript { Get-CloudNetworkTemplate -ID $ID } `
            -SuccessMessage "Network $ID ownership updated successfully."
    }
}

function Update-CloudNetworkTemplatePermissions {
    <#
    .SYNOPSIS
        Updates the permissions of a network in the Cloud Server.
     
    .DESCRIPTION
        Changes the permission of a network. Prompts for confirmation.
     
    .PARAMETER ID
        Required. ID of the network.
     
    .PARAMETER OwnerUse
        Optional. True or false, enable OwnerUse
 
    .PARAMETER OwnerManage
        Optional. True or false, enable OwnerManage
 
    .PARAMETER OwnerAdmin
        Optional. True or false, enable OwnerAdmin
 
    .PARAMETER GroupUse
        Optional. True or false, enable GroupUse
         
    .PARAMETER GroupManage
        Optional. True or false, enable GroupManage
         
    .PARAMETER GroupAdmin
        Optional. True or false, enable GroupAdmin
         
    .PARAMETER OtherUse
        Optional. True or false, enable OtherUse
         
    .PARAMETER OtherManage
        Optional. True or false, enable OtherManage
         
    .PARAMETER OtherAdmin
        Optional. True or false, enable OtherManage
         
    .EXAMPLE
        # Give group users permission to use the network
        Update-CloudNetworkTemplatePermissions -ID 35 -GroupUse $true
         
    .EXAMPLE
        # Deny group admin permission to the network
        Update-CloudNetworkTemplatePermissions -ID 35 -GroupAdmin $false
         
    .EXAMPLE
        # Set multiple permissions at once
        Update-CloudNetworkTemplatePermissions -ID 35 -GroupUse $true -GroupManage $true -OtherUse $true
    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [int]$ID,
        
        [Parameter(Mandatory = $false)]
        [bool]$OwnerUse,
        
        [Parameter(Mandatory = $false)]
        [bool]$OwnerManage,
        
        [Parameter(Mandatory = $false)]
        [bool]$OwnerAdmin,
        
        [Parameter(Mandatory = $false)]
        [bool]$GroupUse,
        
        [Parameter(Mandatory = $false)]
        [bool]$GroupManage,
        
        [Parameter(Mandatory = $false)]
        [bool]$GroupAdmin,
        
        [Parameter(Mandatory = $false)]
        [bool]$OtherUse,
        
        [Parameter(Mandatory = $false)]
        [bool]$OtherManage,
        
        [Parameter(Mandatory = $false)]
        [bool]$OtherAdmin
    )
    
    process {
        # Build the permissions object structure as shown in the API spec
        $permissions = @{}
        
        if ($PSBoundParameters.ContainsKey('OwnerUse')) { $permissions['owner_use'] = $OwnerUse }
        if ($PSBoundParameters.ContainsKey('OwnerManage')) { $permissions['owner_manage'] = $OwnerManage }
        if ($PSBoundParameters.ContainsKey('OwnerAdmin')) { $permissions['owner_admin'] = $OwnerAdmin }
        
        if ($PSBoundParameters.ContainsKey('GroupUse')) { $permissions['group_use'] = $GroupUse }
        if ($PSBoundParameters.ContainsKey('GroupManage')) { $permissions['group_manage'] = $GroupManage }
        if ($PSBoundParameters.ContainsKey('GroupAdmin')) { $permissions['group_admin'] = $GroupAdmin }
        
        if ($PSBoundParameters.ContainsKey('OtherUse')) { $permissions['other_use'] = $OtherUse }
        if ($PSBoundParameters.ContainsKey('OtherManage')) { $permissions['other_manage'] = $OtherManage }
        if ($PSBoundParameters.ContainsKey('OtherAdmin')) { $permissions['other_admin'] = $OtherAdmin }
        
        # Create the body with the permissions nested structure
        $body = @{
            permissions = $permissions
        }
        
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/network/template/${ID}/permissions"
        
        Invoke-CloudResourceUpdate `
            -CallerPSCmdlet $PSCmdlet `
            -ResourceType "Network" `
            -ID $ID `
            -Uri $uri `
            -Body $body `
            -Action "Update permissions" `
            -GetResourceScript { Get-CloudNetworkTemplate -ID $ID }
    }
}

function Remove-CloudNetworkTemplate {
    <#
    .SYNOPSIS
        Removes a network template from the Cloud Server.
     
    .DESCRIPTION
        Removes a network template. Automatically handles token refresh.
     
    .PARAMETER Name
        Required. Network template ID.
     
    .EXAMPLE
        # Remove network template with confirmation prompt (default behavior):
        Remove-CloudNetworkTemplate -ID 3
        Confirm
        Are you sure you want to perform this action?
        Performing the operation "Remove Network Template" on target "Network Template ID 3 (MyNetworkTemplate)".
        [Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"): Y
 
    .EXAMPLE
        # Remove network without confirmation prompt:
        Remove-CloudNetworkTemplate -ID 3 -Confirm:$false
    #>

    
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [int]$ID
    )
    
    process {
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/network/template/${ID}"
        
        Invoke-CloudResourceRemoval `
            -CallerPSCmdlet $PSCmdlet `
            -ResourceType "Network Template" `
            -ID $ID `
            -Uri $uri `
            -GetResourceScript { Get-CloudNetwork -ID $ID }
    }
}

function Resolve-CloudNetwork {
    <#
    .SYNOPSIS
        Recover a Network from the Cloud Server.
     
    .DESCRIPTION
        Recover the specified Network. Automatically handles token refresh.
     
    .PARAMETER ID
        Required. The ID of the Network to be recovered.
 
    .EXAMPLE
        # Recover a network
        Resolve-CloudNetwork -ID 561
         
    .EXAMPLE
        # Recover a network without a confirmation prompt
        Resolve-CloudNetwork -ID 561 -Confirm:$false
 
    .EXAMPLE
        # Simulate the recovery of a network
        Resolve-CloudNetwork -ID 561 -WhatIf
    #>

    
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
        [int]$ID
        
    )
    
    process {
        # Get the Network details for confirmation message
        try {
            $Network = Get-CloudNetwork -ID $ID
        }
        catch {
            Write-Warning "Network with ID $ID not found."
            return
        }
        
        if (-not $Network) {
            Write-Warning "Network with ID $ID not found."
            return
        }
        $body = @{
            recovery = "success"
        }
            
        $NetworkName = if ($Network.name) { $Network.name } else { "Unnamed" }
        
        if ($PSCmdlet.ShouldProcess("Network ID $ID ($NetworkName)", "Start Network")) {
            
            $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/network/${ID}/recover"
            
            Write-Verbose "Starting Network ID ${ID}: $NetworkName"
            
            try {
                $response = Invoke-CloudApiRequest -Uri $uri -Method PATCH -body $body
                Write-Host "Network $ID recovered successfully." -ForegroundColor Green
                return $response
            }
            catch {
                # Check for specific error messages
                if ($_.Exception.Message -match "not found|does not exist") {
                    Write-Warning "Network $ID not found"
                    return
                }
                else {
                    # Re-throw other errors
                    throw
                }
            }
        }
    }
}

function New-CloudNetworkFromTemplate {
    <#
    .SYNOPSIS
        Creates a new virtual network from a template.
     
    .DESCRIPTION
        Instantiates a virtual network from an existing network template.
        Automatically handles token refresh.
     
    .PARAMETER TemplateID
        Required. ID of the network template to instantiate.
     
    .PARAMETER Name
        Required. Name for the new virtual network.
     
    .PARAMETER Extra
        Optional. Additional template attributes to override or add.
        Format as newline-separated "KEY = VALUE" pairs.
        Values with spaces should be quoted.
     
    .EXAMPLE
        # Create a network from template 3
        New-CloudNetworkFromTemplate -TemplateID 3 -Name "my-gateway-network"
         
    .EXAMPLE
        # Create with extra parameters to override template settings
        $extra = @"
        VN_MAD = "802.1Q"
        VLAN_ID = "200"
        BRIDGE = "onebr.200"
        "@
        New-CloudNetworkFromTemplate -TemplateID 3 -Name "custom-network" -Extra $extra
 
    .EXAMPLE
        # Override just the VLAN_ID to avoid conflicts
        New-CloudNetworkFromTemplate -TemplateID 3 -Name "new-network" -Extra 'VLAN_ID = "50"'
         
    .EXAMPLE
        # From pipeline
        Get-CloudNetworkTemplate -ID 3 | New-CloudNetworkFromTemplate -Name "new-network"
    #>

    
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('ID', 'template_id')]
        [int]$TemplateID,
        
        [Parameter(Mandatory = $true)]
        [string]$Name,
        
        [Parameter(Mandatory = $false)]
        [string]$Extra
    )
    
    process {
        # Build the request body
        $body = @{
            name = $Name
        }
        
        # Add extra if provided
        if ($PSBoundParameters.ContainsKey('Extra')) {
            $body.extra = $Extra
        }
        else {
            $body.extra = ""
        }
        
        # Convert to PSCustomObject for proper JSON serialization
        $bodyObject = [PSCustomObject]$body
        
        # Build the URI
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/network/template/${TemplateID}/instantiate"
        
        Write-Verbose "Instantiating network template ID: $TemplateID"
        Write-Verbose "Network name: $Name"
        if ($Extra) {
            Write-Verbose "Extra parameters:`n$Extra"
        }
        Write-Verbose "Request body: $($bodyObject | ConvertTo-Json -Compress)"
        
        try {
            $response = Invoke-CloudApiRequest -Uri $uri -Method Patch -Body $bodyObject
            
            Write-Host "Network '$Name' created successfully from template ID $TemplateID." -ForegroundColor Green
            return $response
        }
        catch {
            Write-Error "Failed to instantiate network template ${TemplateID}: $_"
            throw
        }
    }
}

function Update-CloudNetwork {
    <#
    .SYNOPSIS
        Updates a virtual network.
     
    .DESCRIPTION
        Updates a virtual network's configuration.
     
    .PARAMETER ID
        Required. ID of the network to update.
     
    .PARAMETER Template
        Required. Network template content in OpenNebula format.
     
    .PARAMETER Merge
        Optional. If true (default), merges with existing configuration.
     
    .EXAMPLE
        # Update cloud network description
        $template = 'DESCRIPTION = "Updated network description"'
        Update-CloudNetwork -ID 5 -Template $template
         
    .EXAMPLE
        # Update cloud network 5 without merging
        Update-CloudNetwork -ID 5 -Template $template -Merge:$false
 
    .EXAMPLE
        # Update a network's MAC address range and description
        $template = @"
        DESCRIPTION = "Updated network with custom MAC range"
        MAC_START = "02:00:c0:a8:00:01"
        MAC_END = "02:00:c0:a8:00:FF"
        "@
        Update-CloudNetwork -ID 5 -Template $template -Verbose
    #>

    
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('NetworkID')]
        [int]$ID,
        
        [Parameter(Mandatory = $true)]
        [string]$Template,
        
        [Parameter()]
        [bool]$Merge = $true
    )
    
    process {
        $body = @{
            merge = $Merge
            template = $Template
        }
        
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/network/${ID}"
        
        Write-Verbose "Updating network ID: $ID"
        Write-Verbose "Template:`n$Template"
        
        try {
            Invoke-CloudResourceUpdate `
                -CallerPSCmdlet $PSCmdlet `
                -ResourceType "Network" `
                -ID $ID `
                -Uri $uri `
                -Body $body `
                -Action "Update" `
                -GetResourceScript { Get-CloudNetwork -ID $ID }
            
            Write-Host "Network ID $ID updated successfully." -ForegroundColor Green
        }
        catch {
            Write-Error "Failed to update network ${ID}: $_"
            throw
        }
    }
}

function New-CloudNetworkAddressRange {
    <#
    .SYNOPSIS
        Adds an address range to a virtual network.
     
    .DESCRIPTION
        Creates a new address range in an existing virtual network.
     
    .PARAMETER NetworkID
        Required. ID of the network.
     
    .PARAMETER Template
        Required. Address range template in OpenNebula format.
     
    .EXAMPLE
        # Add a new address range to the network
        $template = @"
    AR = [
        TYPE = "IP4",
        IP = "192.168.1.100",
        SIZE = "50"
    ]
    "@
        New-CloudNetworkAddressRange -NetworkID 5 -Template $template
    #>

    
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('ID')]
        [int]$NetworkID,
        
        [Parameter(Mandatory = $true)]
        [string]$Template
    )
    
    process {
        $body = [PSCustomObject]@{
            template = $Template
        }
        
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/network/${NetworkID}/address-range"
        
        Write-Verbose "Adding address range to network ID: $NetworkID"
        Write-Verbose "Template:`n$Template"
        
        try {
            $response = Invoke-CloudApiRequest -Uri $uri -Method Post -Body $body
            Write-Host "Address range added successfully to network ID $NetworkID." -ForegroundColor Green
            return $response
        }
        catch {
            Write-Error "Failed to add address range to network ${NetworkID}: $_"
            throw
        }
    }
}

function Update-CloudNetworkAddressRange {
    <#
    .SYNOPSIS
        Updates an address range in a virtual network.
     
    .DESCRIPTION
        Modifies an existing address range in a virtual network.
     
    .PARAMETER NetworkID
        Required. ID of the network.
     
    .PARAMETER Template
        Required. Address range template with updates.
     
    .EXAMPLE
        # Update a network range
        $template = 'AR = [ AR_ID = "0", SIZE = "100" ]'
        Update-CloudNetworkAddressRange -NetworkID 5 -Template $template
    #>

    
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('ID')]
        [int]$NetworkID,
        
        [Parameter(Mandatory = $true)]
        [string]$Template
    )
    
    process {
        $body = [PSCustomObject]@{
            template = $Template
        }
        
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/network/${NetworkID}/address-range"
        
        Write-Verbose "Updating address range for network ID: $NetworkID"
        Write-Verbose "Template:`n$Template"
        
        try {
            Invoke-CloudResourceUpdate `
                -CallerPSCmdlet $PSCmdlet `
                -ResourceType "Network Address Range" `
                -ID $NetworkID `
                -Uri $uri `
                -Body $body `
                -Action "Update" `
                -GetResourceScript { Get-CloudNetwork -ID $NetworkID }
            
            Write-Host "Address range updated successfully for network ID $NetworkID." -ForegroundColor Green
        }
        catch {
            Write-Error "Failed to update address range for network ${NetworkID}: $_"
            throw
        }
    }
}

function Remove-CloudNetworkAddressRange {
    <#
    .SYNOPSIS
        Removes an address range from a virtual network.
     
    .DESCRIPTION
        Deletes an address range from a network.
     
    .PARAMETER NetworkID
        Required. ID of the network.
     
    .PARAMETER RangeID
        Required. ID of the address range to remove.
     
    .EXAMPLE
        # Remove range 0 from network 5
        Remove-CloudNetworkAddressRange -NetworkID 5 -RangeID 0
    #>

    
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('ID')]
        [int]$NetworkID,
        
        [Parameter(Mandatory = $true)]
        [int]$RangeID
    )
    
    process {
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/network/${NetworkID}/address-range/${RangeID}"
        
        if ($PSCmdlet.ShouldProcess("Network ID $NetworkID", "Delete address range ID $RangeID")) {
            Write-Verbose "Deleting address range ID $RangeID from network ID: $NetworkID"
            
            try {
                $response = Invoke-CloudApiRequest -Uri $uri -Method Delete
                Write-Host "Address range ID $RangeID deleted successfully from network ID $NetworkID." -ForegroundColor Green
                return $response
            }
            catch {
                Write-Error "Failed to delete address range from network ${NetworkID}: $_"
                throw
            }
        }
    }
}

function Clear-CloudNetworkAddressRange {
    <#
    .SYNOPSIS
        Frees all addresses in an address range.
     
    .DESCRIPTION
        Releases all IP addresses in a specific address range.
     
    .PARAMETER NetworkID
        Required. ID of the network.
     
    .PARAMETER RangeID
        Required. ID of the address range to free.
     
    .EXAMPLE
        # Release all addresses from network 5 in range 0
        Clear-CloudNetworkAddressRange -NetworkID 5 -RangeID 0
    #>

    
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('ID')]
        [int]$NetworkID,
        
        [Parameter(Mandatory = $true)]
        [int]$RangeID
    )
    
    process {
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/network/${NetworkID}/address-range/${RangeID}/free"
        
        if ($PSCmdlet.ShouldProcess("Network ID $NetworkID", "Free address range ID $RangeID")) {
            Write-Verbose "Freeing address range ID $RangeID in network ID: $NetworkID"
            
            try {
                $response = Invoke-CloudApiRequest -Uri $uri -Method Delete
                Write-Host "Address range ID $RangeID freed successfully in network ID $NetworkID." -ForegroundColor Green
                return $response
            }
            catch {
                Write-Error "Failed to free address range in network ${NetworkID}: $_"
                throw
            }
        }
    }
}

function Suspend-CloudNetworkLease {
    <#
    .SYNOPSIS
        Holds a virtual network lease as used.
     
    .DESCRIPTION
        Marks specific IP addresses in a network as used/held, preventing them from being
        automatically assigned to new VMs.
     
    .PARAMETER NetworkID
        Required. ID of the network.
     
    .PARAMETER Template
        Required. Template specifying which addresses to hold.
     
    .EXAMPLE
        # Hold a specific IP address
        $template = 'LEASES = [ IP = "192.168.1.50" ]'
        Suspend-CloudNetwork -NetworkID 5 -Template $template
         
    .EXAMPLE
        # Hold a range of IPs
        $template = @"
        LEASES = [ IP = "192.168.1.100" ]
        LEASES = [ IP = "192.168.1.101" ]
        LEASES = [ IP = "192.168.1.102" ]
        "@
        Suspend-CloudNetworkLease -NetworkID 5 -Template $template
         
    .EXAMPLE
        # Hold by MAC address
        $template = 'LEASES = [ MAC = "02:00:c0:a8:01:32" ]'
        Suspend-CloudNetworkLease -NetworkID 5 -Template $template
    #>

    
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('ID')]
        [int]$NetworkID,
        
        [Parameter(Mandatory = $true)]
        [string]$Template
    )
    
    process {
        $body = [PSCustomObject]@{
            template = $Template
        }
        
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/network/${NetworkID}/hold"
        
        if ($PSCmdlet.ShouldProcess("Network ID $NetworkID", "Hold network addresses")) {
            Write-Verbose "Holding network addresses for network ID: $NetworkID"
            Write-Verbose "Template:`n$Template"
            Write-Verbose "Request body: $($body | ConvertTo-Json -Compress)"
            
            try {
                $response = Invoke-CloudApiRequest -Uri $uri -Method Patch -Body $body
                Write-Host "Network addresses held successfully for network ID $NetworkID." -ForegroundColor Green
                return $response
            }
            catch {
                Write-Error "Failed to hold network addresses for network ${NetworkID}: $_"
                throw
            }
        }
    }
}

function Restore-CloudNetwork {
    <#
    .SYNOPSIS
        Recovers a virtual network.
     
    .DESCRIPTION
        Recovers a network from an error state.
     
    .PARAMETER NetworkID
        Required. ID of the network to recover.
     
    .EXAMPLE
        # Recover network 5
        Restore-CloudNetwork -NetworkID 5
    #>

    
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('ID')]
        [int]$NetworkID
    )
    
    process {
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/network/${NetworkID}/recover"
        
        if ($PSCmdlet.ShouldProcess("Network ID $NetworkID", "Recover network")) {
            Write-Verbose "Recovering network ID: $NetworkID"
            
            try {
                $response = Invoke-CloudApiRequest -Uri $uri -Method Patch
                Write-Host "Network ID $NetworkID recovered successfully." -ForegroundColor Green
                return $response
            }
            catch {
                Write-Error "Failed to recover network ${NetworkID}: $_"
                throw
            }
        }
    }
}

function Resume-CloudNetworkLease {
    <#
    .SYNOPSIS
        Releases held leases on a virtual network.
     
    .DESCRIPTION
        Releases reserved leases a network from hold state, making it available for use.
     
    .PARAMETER NetworkID
        Required. ID of the network to release.
     
    .EXAMPLE
        # Release a range of IPs on network 5
        $template = @"
        LEASES = [ IP = "192.168.1.100" ]
        LEASES = [ IP = "192.168.1.101" ]
        LEASES = [ IP = "192.168.1.102" ]
        "@
        Resume-CloudNetworkLease -NetworkID 5 -Template $template
    #>

    
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('ID')]
        [int]$NetworkID,

        [Parameter(Mandatory = $true)]
        [string]$Template
    )



    process {
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/network/${NetworkID}/release"
        $body = [PSCustomObject]@{
            template = $Template
        }
        if ($PSCmdlet.ShouldProcess("Network ID $NetworkID", "Release network")) {
            Write-Verbose "Releasing network ID: $NetworkID"
            
            try {
                write-verbose "Template: $template"
                $response = Invoke-CloudApiRequest -Uri $uri -Method PATCH -Body $body
                Write-Host "Network ID $NetworkID released successfully." -ForegroundColor Green
                return $response
            }
            catch {
                Write-Error "Failed to release network ${NetworkID}: $_"
                throw
            }
        }
    }
}

function Save-CloudNetworkAddresses {
    <#
    .SYNOPSIS
        Reserves addresses in a virtual network.
     
    .DESCRIPTION
        Creates a reservation of network addresses.
     
    .PARAMETER NetworkID
        Required. ID of the network.
     
    .PARAMETER Template
        Required. Reservation template specifying addresses to reserve.
     
    .EXAMPLE
        # Reserve a range of IPs on cloud network 5
        $template = 'SIZE = "10" NAME = "Reserved-IPs"'
        Save-CloudNetworkAddresses -NetworkID 5 -Name "SystemReserved" -Template $template
    #>

    
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('ID')]
        [int]$NetworkID,
        
        [Parameter(Mandatory = $true)]
        [string]$Template
    )
    
    process {
        $body = [PSCustomObject]@{
            template = $Template
        }
        
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/network/${NetworkID}/reserve"
        
        Write-Verbose "Reserving addresses in network ID: $NetworkID"
        Write-Verbose "Template:`n$Template"
        
        try {
            $response = Invoke-CloudApiRequest -Uri $uri -Method PATCH -Body $body
            Write-Host "Addresses reserved successfully in network ID $NetworkID." -ForegroundColor Green
            return $response
        }
        catch {
            Write-Error "Failed to reserve addresses in network ${NetworkID}: $_"
            throw
        }
    }
}

function Update-CloudNetworkTemplate {
    <#
    .SYNOPSIS
        Updates a network template stored in the network.
     
    .DESCRIPTION
        Updates the template configuration for a network.
     
    .PARAMETER NetworkID
        Required. ID of the network.
     
    .PARAMETER Template
        Required. Updated network template.
     
    .EXAMPLE
        # Update a network template using a variable
        $template = 'DESCRIPTION = "Production Network"'
        Update-CloudNetworkTemplate -NetworkID 5 -Template $template
    #>

    
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('ID')]
        [int]$NetworkID,
        
        [Parameter(Mandatory = $true)]
        [string]$Template
    )
    
    process {
        $body = [PSCustomObject]@{
            template = $Template
        }
        
        $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/network/${NetworkID}/template/update"
        
        Write-Verbose "Updating template for network ID: $NetworkID"
        Write-Verbose "Template:`n$Template"
        
        try {
            Invoke-CloudResourceUpdate `
                -CallerPSCmdlet $PSCmdlet `
                -ResourceType "Network Template" `
                -ID $NetworkID `
                -Uri $uri `
                -Body $body `
                -Action "Update" `
                -GetResourceScript { Get-CloudNetwork -ID $NetworkID }
            
            Write-Host "Network template updated successfully for network ID $NetworkID." -ForegroundColor Green
        }
        catch {
            Write-Error "Failed to update network template for network ${NetworkID}: $_"
            throw
        }
    }
}