SherpaShell.psm1

Function Add-SDTicket {
    [cmdletbinding(
        DefaultParameterSetName = 'ByParameter'
    )]
    Param(
        [Parameter(ParameterSetName = 'ByParameter')] [string]$Status,
        [Parameter(ParameterSetName = 'ByParameter')] [string]$Subject,
        [Parameter(ParameterSetName = 'ByParameter')] [string]$FirstPost,
        [Parameter(ParameterSetName = 'ByParameter')] [int]$Class,
        [Parameter(ParameterSetName = 'ByParameter')] [int]$Account,
        [Parameter(ParameterSetName = 'ByParameter')] [int]$Location,
        [Parameter(ParameterSetName = 'ByParameter')] [int]$User,
        [Parameter(ParameterSetName = 'ByParameter')] [int]$Tech,
        [Parameter(ParameterSetName = 'ByBody')] [hashtable]$Body,
        
        [string]$Organization = $authConfig.WorkingOrganization,
        [string]$Instance = $authConfig.WorkingInstance,
        [string]$ApiKey = $authConfig.ApiKey
    )
    $NewTicketParams = @{
        Status    = 'status'
        Subject   = 'subject'
        FirstPost = 'initial_post'
        Class     = 'class_id'
        Account   = 'account_id'
        Location  = 'location_id'
        User      = 'user_id'
        Tech      = 'tech_id'
    }

    $resource = "tickets"
    
    If ($PSCmdlet.ParameterSetName -eq 'ByParameter') {
        $body = @{}
        ForEach ($param in $NewTicketParams.GetEnumerator()) {
            If ($PSBoundParameters.ContainsKey($param.key)) {
                $body["$($param.value)"] = $PSBoundParameters["$($param.key)"]
            }
        }
    }

    $jsonbody = $body | ConvertTo-Json

    Write-Verbose $jsonbody

    Invoke-SherpaDeskAPICall -Method Post -Resource $resource -Organization $Organization -Instance $Instance -ApiKey $ApiKey -Body $jsonbody
}
Function Add-SDUser {
    Param(
        [Parameter(
            ParameterSetName = 'ByParameter',
            Mandatory = $true
        )]
        [string]$FirstName,
        [Parameter(
            ParameterSetName = 'ByParameter',
            Mandatory = $true
        )]
        [string]$LastName,
        [Parameter(
            ParameterSetName = 'ByParameter',
            Mandatory = $true
        )]
        [string]$Email,
        [Parameter(
            ParameterSetName = 'ByParameter',
            Mandatory = $true
        )]
        [int]$Account,
        [Parameter(
            ParameterSetName = 'ByParameter'
        )]
        [int]$Location,
        [Parameter(
            ParameterSetName = 'ByBody'
        )]
        [hashtable]$Body,
        [string]$Organization = $authConfig.WorkingOrganization,
        [string]$Instance = $authConfig.WorkingInstance,
        [string]$ApiKey = $authConfig.ApiKey
    )
    $NewUserParams = @{
        FirstName = 'firstname'
        LastName = 'lastname'
        Email = 'email'
        Account = 'account'
        Location = 'location'
    }

    $resource = 'users'

    If($PSCmdlet.ParameterSetName -eq 'ByParameter'){
        $body = @{}
        ForEach($param in $NewUserParams.GetEnumerator()){
            If($PSBoundParameters.ContainsKey($param.key)){
                $body["$($param.value)"] = $PSBoundParameters["$($param.key)"]
            }
        }
    }

    $jsonbody = $body | ConvertTo-Json

    Write-Verbose $jsonbody

    Invoke-SherpaDeskAPICall -Method Post -Resource $resource -Organization $Organization -Instance $Instance -ApiKey $ApiKey -Body $jsonbody
}
Function Get-SDAccount{
    [cmdletbinding(DefaultParameterSetName = 'All')]
    Param(
        [parameter(Mandatory = $true, ParameterSetName = 'ByParameter')] [int]$AccountID,
        [parameter(ParameterSetName = 'ByParameter')] [switch]$Statistics,
        [parameter(ParameterSetName = 'ByParameter')] [string]$Note,
        [parameter(ParameterSetName = 'ByParameter')] [switch]$AssetInfo,
        [parameter(ParameterSetName = 'ByParameter')] [switch]$LocationInfo,
        [parameter(ParameterSetName = 'ByParameter')] [switch]$FileInfo,
        [parameter(ParameterSetName = 'ByParameter')] [switch]$ProjectInfo,
        [parameter(ParameterSetName = 'ByParameter')] [switch]$UserInfo,
        [parameter(ParameterSetName = 'ByBody')] [hashtable]$Body,
        [parameter(ParameterSetName = 'All')] [switch]$All,

        [string]$Organization = $authConfig.WorkingOrganization,
        [string]$Instance = $authConfig.WorkingInstance,
        [string]$ApiKey = $authConfig.ApiKey
    )
    $accountParams = @{
        AccountID = "account_id"
        Statistics = "is_with_statistics"
        Note = "note"
        AssetInfo = "is_assets_info"
        LocationInfo = "is_locations_info"
        FileInfo = "is_files_info"
        ProjectInfo = "is_projects_info"
        UserInfo = "is_users_info"
    }
    
    $resource = 'accounts'
    If ($PSCmdlet.ParameterSetName -eq 'ByParameter') {
        $resource = "$resource/$AccountID"
        $Body = @{}
        ForEach ($param in $accountParams.GetEnumerator()) {
            If ($PSBoundParameters.ContainsKey($param.key)) {
                If ($($PSBoundParameters["$($param.key)"]).IsPresent) {
                    $Body["$($param.value)"] = $PSBoundParameters["$($param.key)"].IsPresent
                } Else {
                    $Body["$($param.value)"] = $PSBoundParameters["$($param.key)"]
                }
            }
        }
        } ElseIf ($PSCmdlet.ParameterSetName -eq 'ByBody') {
            $Body = $Body
        } ElseIf ($PSCmdlet.ParameterSetName -eq 'All') {
            $Body = @{} # Empty body for all accounts
        } ElseIf ($PSCmdlet.ParameterSetName -eq 'ByPage') {
            $Body = @{}
            $resource = "${resource}?page=${Page}"
    }

    $jsonbody = $Body | ConvertTo-Json

     Invoke-SherpaDeskAPICall -Resource $resource -Method Get -Organization $Organization -Instance $Instance -ApiKey $ApiKey -Body $jsonbody
}
Function Get-SDAccountStatistics {
    [cmdletbinding()]
    Param(
        [parameter(Mandatory = $true, ParameterSetName = 'ByAccount')] [string]$Account,

        [string]$Organization = $authConfig.WorkingOrganization,
        [string]$Instance = $authConfig.WorkingInstance,
        [string]$ApiKey = $authConfig.ApiKey
    )
    $resource = 'accounts/statistics'
    If($PSCmdlet.ParameterSetName -eq 'ByAccount'){
        $resource = "$resource/$Account"
    } 

    Invoke-SherpaDeskAPICall -Resource $resource -Method Get -Organization $Organization -Instance $Instance -ApiKey $ApiKey
}
Function Get-SDActivity {
    [cmdletbinding(DefaultParameterSetName = 'All')]
    Param(
        [parameter(ParameterSetName = 'ByUserID')] [string]$UserID,
        [parameter(ParameterSetName = 'All')] [switch]$All, # Get all activity. Value is ignored. This is the default if no other params are sent.


        [string]$Organization = $authConfig.WorkingOrganization,
        [string]$Instance = $authConfig.WorkingInstance,
        [string]$ApiKey = $authConfig.ApiKey
    )
    $resource = 'activity'
    If($PSCmdlet.ParameterSetName -eq 'ByUserID'){
        $resource = "${resource}?user=${UserID}"
    }

    Invoke-SherpaDeskAPICall -Resource $resource -Method Get -Organization $Organization -Instance $Instance -ApiKey $ApiKey
}
Function Get-SDAPIKey {
    [cmdletbinding(DefaultParameterSetName = 'EmailOnly')]
    Param(
        [Parameter(ParameterSetName = 'EmailOnly')] [string]$Email,
        [Parameter(ParameterSetName = 'Credential')] [pscredential]$Credential,
        [switch]$PassThru
    )

    If($PSCmdlet.ParameterSetName -eq 'EmailOnly'){
        $Credential = Get-Credential -UserName $Email -Message 'Retrieving API key from Sherpa Desk'
    }

    $up = "$($Credential.GetNetworkCredential().UserName)`:$($Credential.GetNetworkCredential().Password)"
    $encodedUP = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("$up"))

    $header = @{
        Authorization = "Basic $encodedUP"
        Accept = 'application/json'
    }
    $resp = Invoke-RestMethod -Method Get -Uri 'https://api.sherpadesk.com/login' -Headers $header
    $Script:AuthConfig = @{
        ApiKey = $resp.api_token
        WorkingOrganization = ''
        WorkingInstance = ''
    }
    If($PassThru.IsPresent){
        $resp.api_token
    }
}
Function Get-SDArticle {
    [cmdletbinding()]
    Param(
        [parameter(ParameterSetName = 'BySearch')] [string]$Search,

        [string]$Organization = $authConfig.WorkingOrganization,
        [string]$Instance = $authConfig.WorkingInstance,
        [string]$ApiKey = $authConfig.ApiKey
    )
    $resource = 'article'
    If($PSCmdlet.ParameterSetName -eq 'BySearch'){
        $resource = "${resource}?search=${key}"
    }

    Invoke-SherpaDeskAPICall -Resource $resource -Method Get -Organization $Organization -Instance $Instance -ApiKey $ApiKey
}
Function Get-SDAsset{
    [cmdletbinding(DefaultParameterSetName = 'All')]
    Param(
        [Parameter(ParameterSetName = 'ByParameter')] [string]$Search, # string (max 255 chars), search assets by any field
        [Parameter(ParameterSetName = 'ByParameter')] [string]$Filter, # string (max 255 chars), use 'my' to show only my owned assets
        [Parameter(ParameterSetName = 'ByParameter')] [int]$UserId, # integer, show assets checked out by user with id=11
        [Parameter(ParameterSetName = 'ByParameter')] [int]$OwnerId, # integer, show assets owned by user with id=12
        [Parameter(ParameterSetName = 'ByParameter')] [int]$AccountId, # integer, show assets in account with id=1
        [Parameter(ParameterSetName = 'ByParameter')] [int]$LocationId, # integer, show assets in location with id=2 and all child locations
        [Parameter(ParameterSetName = 'ByParameter')] [switch]$IsActive, # boolean, show only active (true) or inactive (false) or all (undefined) assets
        [Parameter(ParameterSetName = 'ByParameter')] [int]$Status, # integer, show only assets with status id=6,
        [Parameter(ParameterSetName = 'ByParameter')] [int]$CategoryId, # integer, show assets with categoryId=111
        [Parameter(ParameterSetName = 'ByParameter')] [int]$TypeId, # integer
        [Parameter(ParameterSetName = 'ByParameter')] [int]$MakeId, # integer
        [Parameter(ParameterSetName = 'ByParameter')] [int]$ModelId, # integer,
        [Parameter(ParameterSetName = 'ByParameter')] [switch]$ShowCustomFields, # boolean, show custom_fields (true) or no (false) or all assets
        [Parameter(ParameterSetName = 'ByBody')] [hashtable]$Body, # pre-defined body to send to the API.
        [Parameter(ParameterSetName = 'ByPage')] [int]$Page, # page number to retrieve. Default is 0
        [parameter(ParameterSetName = 'All')] [switch]$All, # Get all assets. Value is ignored. This is the default if no other params are sent.

        [string]$Organization = $authConfig.WorkingOrganization,
        [string]$Instance = $authConfig.WorkingInstance,
        [string]$ApiKey = $authConfig.ApiKey
    )
    $AssetParams = @{
        Search        = 'search'
        Filter        = 'filter'
        UserId        = 'user_id'
        OwnerId        = 'owner_id'
        AccountId    = 'account_id'
        LocationId    = 'location_id'
        IsActive    = 'is_active'
        Status        = 'status'
        CategoryId    = 'category_id'
        TypeId        = 'type_id'
        MakeId        = 'make_id'
        ModelId        = 'model_id'
        ShowCustomFields = 'is_with_custom_fields'
    }

    # Parse the parameters if provided. The API docs lie, and the API does not support all of the parameters listed in the docs.
    $resource = 'assets'

    
    If ($PSCmdlet.ParameterSetName -eq 'ByParameter') {
        $resource += "?"
        ForEach ($param in $AssetParams.GetEnumerator()) { 
            If ($PSBoundParameters.ContainsKey($param.key)) {
                If ($($PSBoundParameters["$($param.key)"]).IsPresent) {
                    $resource += "$($param.value)=$($PSBoundParameters["$($param.key)"].IsPresent)&"
                } Else {
                    $resource += "$($param.value)=$($PSBoundParameters["$($param.key)"])&"
                }
            }
        }
        $resource = $resource.TrimEnd('&') # Remove the last ampersand in the string
    } ElseIf ($PSCmdlet.ParameterSetName -eq 'ByBody') {
        $Body = $Body
    } ElseIf ($PSCmdlet.ParameterSetName -eq 'All') {
        $Body = @{} # Empty body for all assets
    } ElseIf ($PSCmdlet.ParameterSetName -eq 'ByPage') {
        $Body = @{}
        $resource = "${resource}?page=${Page}"
    }

    $jsonbody = $Body | ConvertTo-Json -Depth 10 # Convert the body to JSON format

     Invoke-SherpaDeskAPICall -Resource $resource -Method Get -Organization $Organization -Instance $Instance -ApiKey $ApiKey -Body $jsonbody
}
Function Get-SDAssetCategory {
    [cmdletbinding()]
    Param(
        [string]$Organization = $authConfig.WorkingOrganization,
        [string]$Instance = $authConfig.WorkingInstance,
        [string]$ApiKey = $authConfig.ApiKey
    )
    $resource = 'asset_categories'

    Invoke-SherpaDeskAPICall -Resource $resource -Method Get -Organization $Organization -Instance $Instance -ApiKey $ApiKey
}
Function Get-SDAssetCustomField {
    [cmdletbinding()]
    Param(
        [string]$Organization = $authConfig.WorkingOrganization,
        [string]$Instance = $authConfig.WorkingInstance,
        [string]$ApiKey = $authConfig.ApiKey
    )
    $resource = 'customfields'

    Invoke-SherpaDeskAPICall -Resource $resource -Method Get -Organization $Organization -Instance $Instance -ApiKey $ApiKey
}
Function Get-SDAssetMakes {
    [cmdletbinding()]
    Param(
        [string]$Organization = $authConfig.WorkingOrganization,
        [string]$Instance = $authConfig.WorkingInstance,
        [string]$ApiKey = $authConfig.ApiKey
    )
    $resource = 'asset_makes'

    Invoke-SherpaDeskAPICall -Resource $resource -Method Get -Organization $Organization -Instance $Instance -ApiKey $ApiKey
}
Function Get-SDAssetModels {
    [cmdletbinding()]
    Param(
        [string]$Organization = $authConfig.WorkingOrganization,
        [string]$Instance = $authConfig.WorkingInstance,
        [string]$ApiKey = $authConfig.ApiKey
    )
    $resource = 'asset_models'

    Invoke-SherpaDeskAPICall -Resource $resource -Method Get -Organization $Organization -Instance $Instance -ApiKey $ApiKey
}
Function Get-SDAssetSearch{
    [cmdletbinding(DefaultParameterSetName = 'All')]
    Param(
        [Parameter(ParameterSetName = 'ByParameter')] [string]$Text, # string (max 255 chars), search assets by any Unique field
        [Parameter(ParameterSetName = 'ByParameter')] [int]$AccountID, # string (max 255 chars), search assets by any field
        [Parameter(ParameterSetName = 'ByParameter')] [string]$ExcludeFields, # string (max 255 chars), use 'my' to show only my owned assets
        [Parameter(ParameterSetName = 'ByParameter')] [string]$Query, # integer, show assets checked out by user with id=11
        [Parameter(ParameterSetName = 'ByParameter')] [int]$OriginalLimit, # integer, show assets owned by user with id=12
        [Parameter(ParameterSetName = 'ByParameter')] [int]$Limit, # integer, show assets in account with id=1
        [Parameter(ParameterSetName = 'ByParameter')] [string]$SortOrder, # boolean, show only active (true) or inactive (false) or all (undefined) assets
        [Parameter(ParameterSetName = 'ByParameter')] [string]$SortBy, # integer, show only assets with status id=6,
        [Parameter(ParameterSetName = 'ByParameter')] [int]$StartDate, # integer, show assets with categoryId=111
        [Parameter(ParameterSetName = 'ByParameter')] [int]$EndDate, # integer
        [Parameter(ParameterSetName = 'ByBody')] [hashtable]$Body, # pre-defined body to send to the API.
        [Parameter(ParameterSetName = 'ByPage')] [int]$Page, # pre-defined body to send to the API.
        [parameter(ParameterSetName = 'All')] [switch]$All, # Get all assets. Value is ignored. This is the default if no other params are sent.

        [string]$Organization = $authConfig.WorkingOrganization,
        [string]$Instance = $authConfig.WorkingInstance,
        [string]$ApiKey = $authConfig.ApiKey
    )
    $AssetParams = @{
        Text                = 'text'
        AccountId            = 'account_id'
        ExcludeFields       = 'exclude_fields'
        Query                = 'query'
        OriginalLimit       = 'original_limit'
        Limit                = 'limit'
        Page                = 'page'
        SortOrder            = 'sort_order'
        SortBy                = 'sort_by'
        StartDate            = 'start_date'
        EndDate                = 'end_date'
    }

    # Parse the parameters if provided. The API docs lied, and none of the body parameters actually work. You just get it all.
    $resource = 'assets/search'
    If ($PSCmdlet.ParameterSetName -eq 'ByParameter') {
        $resource += '?c=1'
        $Body = @{}
        ForEach ($param in $AssetParams.GetEnumerator()) {
            If ($PSBoundParameters.ContainsKey($param.key)) {
                If ($($PSBoundParameters["$($param.key)"]).IsPresent) {
                    $Body["$($param.value)"] = $PSBoundParameters["$($param.key)"].IsPresent
                } Else {
                    $Body["$($param.value)"] = $PSBoundParameters["$($param.key)"]
                }
            }
        }
        } ElseIf ($PSCmdlet.ParameterSetName -eq 'ByBody') {
            $Body = $Body
        } ElseIf ($PSCmdlet.ParameterSetName -eq 'All') {
            $Body = @{} # Empty body for all assets
        } ElseIf ($PSCmdlet.ParameterSetName -eq 'ByPage') {
            $Body = @{}
            $resource = "${resource}?page=${Page}"
    }

    $jsonbody = $Body | ConvertTo-Json

     Invoke-SherpaDeskAPICall -Resource $resource -Method Get -Organization $Organization -Instance $Instance -ApiKey $ApiKey -Body $jsonbody
}
Function Get-SDAssetStatuses {
    [cmdletbinding()]
    Param(
        [string]$Organization = $authConfig.WorkingOrganization,
        [string]$Instance = $authConfig.WorkingInstance,
        [string]$ApiKey = $authConfig.ApiKey
    )
    $resource = 'asset_statuses'

    Invoke-SherpaDeskAPICall -Resource $resource -Method Get -Organization $Organization -Instance $Instance -ApiKey $ApiKey
}
Function Get-SDAssetTypes {
    [cmdletbinding()]
    Param(
        [string]$Organization = $authConfig.WorkingOrganization,
        [string]$Instance = $authConfig.WorkingInstance,
        [string]$ApiKey = $authConfig.ApiKey
    )
    $resource = 'asset_types'

    Invoke-SherpaDeskAPICall -Resource $resource -Method Get -Organization $Organization -Instance $Instance -ApiKey $ApiKey
}
Function Get-SDAuthConfig {
    Param(
        [switch]$Silent
    )
    If($AuthConfig){
        If(-not ($Silent.IsPresent)){
            $AuthConfig
        }
    }Else{
        $dir = Get-SDSavePath
        If(Test-Path $dir\credentials.json){
            $encryptedAuth = Get-Content $dir\credentials.json | ConvertFrom-Json
        }
        $script:AuthConfig = @{}
        ForEach($property in $encryptedAuth.psobject.Properties){
            $AuthConfig."$($property.name)" = [pscredential]::New('user',(ConvertTo-SecureString $property.value)).GetNetworkCredential().password
        }
        If(-not ($Silent.IsPresent)){
            $AuthConfig
        }
    }
}
Function Get-SDClass {
    [cmdletbinding(DefaultParameterSetName = 'All')]
    Param(
        [parameter(ParameterSetName = 'ByClassID')] [string]$ClassID,
        [parameter(ParameterSetName = 'All')] [switch]$All, # Get all activity. Value is ignored. This is the default if no other params are sent.


        [string]$Organization = $authConfig.WorkingOrganization,
        [string]$Instance = $authConfig.WorkingInstance,
        [string]$ApiKey = $authConfig.ApiKey
    )
    $resource = 'classes'
    If($PSCmdlet.ParameterSetName -eq 'ByClassID'){
        $resource = "${resource}/${UserID}"
    }

    Invoke-SherpaDeskAPICall -Resource $resource -Method Get -Organization $Organization -Instance $Instance -ApiKey $ApiKey
}
Function Get-SDConfig{
    [cmdletbinding()]
    Param(
        [string]$Organization = $authConfig.WorkingOrganization,
        [string]$Instance = $authConfig.WorkingInstance,
        [string]$ApiKey = $authConfig.ApiKey
    )

    # Validate the key parameter if provided
    $resource = 'config'
 
     Invoke-SherpaDeskAPICall -Resource $resource -Method Get -Organization $Organization -Instance $Instance -ApiKey $ApiKey

}
Function Get-SDInvoice{
    [cmdletbinding(DefaultParameterSetName = 'ByKey')]
    Param(
        [parameter(ParameterSetName = 'ByKey')] [string]$Key,
        [parameter(ParameterSetName = 'ByAccount')] [string]$Account,
        [parameter(ParameterSetName = 'ByContract')] [string]$Contract,

        [string]$Organization = $authConfig.WorkingOrganization,
        [string]$Instance = $authConfig.WorkingInstance,
        [string]$ApiKey = $authConfig.ApiKey
    )

    # Validate the key parameter if provided
    $resource = 'invoices'
    If ($PSCmdlet.ParameterSetName -eq 'ByKey') {
        $resource = "$resource/$key"
    } ElseIf ($PSCmdlet.ParameterSetName -eq 'ByAccount') {
        $resource = "${$resource}?account=${$AccountKey}"
    } ElseIf ($PSCmdlet.ParameterSetName -eq 'ByContract') {
        $resource = "${$resource}?contract_id=${$ContractKey}"
    }

     Invoke-SherpaDeskAPICall -Resource $resource -Method Get -Organization $Organization -Instance $Instance -ApiKey $ApiKey

}
Function Get-SDMetadata {
    Param(
        [string]$ApiKey = $AuthConfig.ApiKey,
        [switch]$PassThru
    )
    
    $encodedAuth = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("x:$ApiKey"))

    $header = @{
        Authorization = "Basic $encodedAuth"
        Accept = 'application/json'
    }

    $response = Invoke-RestMethod -Uri 'https://api.sherpadesk.com/organizations/' -Method Get -Headers $header
    
    if ($response.Count -gt 1) {
        Write-Host "Multiple organizations found. Please select one:"
        for ($i = 0; $i -lt $response.Count; $i++) {
            Write-Host "$i - $($response[$i].name)"
        }
        $selection = Read-Host "Enter the number corresponding to your choice"
        if ($selection -match '^\d+$' -and [int]$selection -lt $response.Count) {
            $selectedOrg = $response[$selection]
        } else {
            throw "Invalid selection. Please try again."
        }
    } else {
        $selectedOrg = $response[0]
    }

    $Script:AuthConfig.WorkingOrganization = $selectedOrg.key
    $Script:AuthConfig.WorkingInstance = $selectedOrg.instances[0].key

    if ($PassThru.IsPresent) {
        $response
    }
}
Function Get-SDProfile {
    [cmdletbinding()]
    Param(
        [parameter(ParameterSetName = 'ByID')] [string]$ID,

        [string]$Organization = $authConfig.WorkingOrganization,
        [string]$Instance = $authConfig.WorkingInstance,
        [string]$ApiKey = $authConfig.ApiKey
    )
    $resource = 'profile'
    If($PSCmdlet.ParameterSetName -eq 'ByID'){
        $resource = "$resource/$key"
    }

    Invoke-SherpaDeskAPICall -Resource $resource -Method Get -Organization $Organization -Instance $Instance -ApiKey $ApiKey
}
Function Get-SDProject {
    [cmdletbinding(DefaultParameterSetName = 'All')]
    Param(
        [parameter(ParameterSetName = 'ByAccount')] [string]$AccountID,
        [parameter(ParameterSetName = 'ByTech')] [string]$TechID,
        [parameter(ParameterSetName = 'HasStatistics')] [switch]$HasStats,
        [parameter(ParameterSetName = 'ByStatusID')] [string]$StatusID,
        [parameter(ParameterSetName = 'All')] [switch]$All,

        [string]$Organization = $authConfig.WorkingOrganization,
        [string]$Instance = $authConfig.WorkingInstance,
        [string]$ApiKey = $authConfig.ApiKey
    )
    $resource = 'projects'
    If($PSCmdlet.ParameterSetName -eq 'ByAccount'){
        $resource = "${resource}?account=${AccountID}"
    } ElseIf ($PSCmdlet.ParameterSetName -eq 'ByTech') {
        $resource = "${resource}?tech=${TechID}"
    } ElseIf ($PSCmdlet.ParameterSetName -eq 'HasStatistics') {
        $resource = "${resource}?is_with_statistics=true"
    } ElseIf ($PSCmdlet.ParameterSetName -eq 'ByStatusID') {
        $resource = "${resource}?active_status=${StatusID}"
    } 

    Invoke-SherpaDeskAPICall -Resource $resource -Method Get -Organization $Organization -Instance $Instance -ApiKey $ApiKey
}
Function Get-SDTaskTypes {
    [cmdletbinding()]
    Param(
        [string]$Organization = $authConfig.WorkingOrganization,
        [string]$Instance = $authConfig.WorkingInstance,
        [string]$ApiKey = $authConfig.ApiKey
    )
    $resource = 'task_types'

    Invoke-SherpaDeskAPICall -Resource $resource -Method Get -Organization $Organization -Instance $Instance -ApiKey $ApiKey
}
Function Get-SDTechs {
    [cmdletbinding()]
    Param(
        [string]$Organization = $authConfig.WorkingOrganization,
        [string]$Instance = $authConfig.WorkingInstance,
        [string]$ApiKey = $authConfig.ApiKey
    )
    $resource = 'technicians'

    Invoke-SherpaDeskAPICall -Resource $resource -Method Get -Organization $Organization -Instance $Instance -ApiKey $ApiKey
}
Function Get-SDTicket{
    [cmdletbinding(DefaultParameterSetName = 'All')]
    Param(
        [parameter(ParameterSetName = 'ByID')] [string]$ID,
        [parameter(ParameterSetName = 'ByPage')] [int]$Page,
        [parameter(ParameterSetName = 'ByStatus')] [string]$Status,
        [parameter(ParameterSetName = 'ByStatus')] [string]$Role,
        [parameter(ParameterSetName = 'ByDateRange')] [string]$StartDate,
        [parameter(ParameterSetName = 'ByDateRange')] [string]$EndDate,
        [parameter(ParameterSetName = 'BySearch')] [string]$Search,
        [parameter(ParameterSetName = 'All')] [switch]$All, # Get all tickets. Value is ignored. This is the default if no other params are sent.

        [string]$Organization = $authConfig.WorkingOrganization,
        [string]$Instance = $authConfig.WorkingInstance,
        [string]$ApiKey = $authConfig.ApiKey
    )

    # TODO: Validate the parameters if provided
    $resource = 'tickets'
    If($PSCmdlet.ParameterSetName -eq 'ByKey'){
        $resource = "$resource/$ID"
    } ElseIf ($PSCmdlet.ParameterSetName -eq 'ByPage') {
        $resource = "${resource}?page=${Page}"
    } ElseIf ($PSCmdlet.ParameterSetName -eq 'ByStatus') {
        $resource = "${resource}?status=${Status}"
        If ($Role) {
            $resource = "${resource}&role=${Role}"
        }
    } ElseIf ($PSCmdlet.ParameterSetName -eq 'ByDateRange') {
        If ($StartDate -And $EndDate) {
            $resource = "${resource}?start_date=${StartDate}&end_date=${EndDate}"
        } ElseIf ($StartDate) {
            $resource = "${resource}?start_date=${StartDate}"
        } ElseIf ($EndDate) {
            $resource = "${resource}?end_date=${EndDate}"
        }
    } ElseIf ($PSCmdlet.ParameterSetName -eq 'BySearch') {
        $resource = "${resource}?search=${Search}&c=1"
    }

     Invoke-SherpaDeskAPICall -Resource $resource -Method Get -Organization $Organization -Instance $Instance -ApiKey $ApiKey

}
Function Get-SDTime {
    [cmdletbinding()]
    Param(
        [switch]$Recent,
        [switch]$UnlinkedFreshBooks,
        [switch]$LinkedFreshBooks,
        [switch]$Invoiced,
        [switch]$Uninvoiced,
        [switch]$UnlinkedQuickBooks,
        [switch]$LinkedQuickBooks,
        [switch]$Hidden,
        [switch]$DoNotInvoice,
        [string]$Account,
        [string]$Tech,
        [string]$Project,
        [string]$TicketTimeID,
        [string]$ProjectTimeID,
        [string]$Organization = $authConfig.WorkingOrganization,
        [string]$Instance = $authConfig.WorkingInstance,
        [string]$ApiKey = $authConfig.ApiKey
    )
    $arrParameters = @()
    If($AccountName){
        $arrParameters += "account=$AccountName"
    }
    If($Tech){
        $arrParameters += "tech=$Tech"
    }
    If($Project){
        $arrParameters += "project=$Project"
    }
    $strParameters = $arrParameters -join '&'
    
    Write-Verbose $strParameters

    If($strParameters){
        Invoke-SherpaDeskAPICall -Resource "time?$strParameters" -Method Get -Organization $Organization -Instance $Instance -ApiKey $ApiKey
    }Else{
        Invoke-SherpaDeskAPICall -Resource time -Method Get -Organization $Organization -Instance $Instance -ApiKey $ApiKey
    }
}
Function Get-SDUser {
    [cmdletbinding()]
    Param(
        [string]$Organization = $authConfig.WorkingOrganization,
        [string]$Instance = $authConfig.WorkingInstance,
        [string]$ApiKey = $authConfig.ApiKey
    )
    $resource = 'users'

    Invoke-SherpaDeskAPICall -Resource $resource -Method Get -Organization $Organization -Instance $Instance -ApiKey $ApiKey
}
Function Save-SDAuthConfig {
    [cmdletbinding(
        DefaultParameterSetName = 'FromAuthConfig'
    )]
    Param(
        [parameter(
            ParameterSetName = 'Passed'
        )]
        [ValidateNotNullOrEmpty()]
        [string]$Organization,
        [parameter(
            ParameterSetName = 'Passed'
        )]
        [ValidateNotNullOrEmpty()]
        [string]$Instance,
        [parameter(
            ParameterSetName = 'Passed'
        )]
        [ValidateNotNullOrEmpty()]
        [string]$ApiKey
    )
    $dir = Get-SDSavePath
    If(-not(Test-Path $dir -PathType Container)){
        New-Item $dir -ItemType Directory
    }
    If(-not(Test-Path $dir\credentials.json -PathType Leaf)){
        New-Item $dir\credentials.json -ItemType File
    }
    If($PSCmdlet.ParameterSetName -eq 'Passed'){
        $Script:AuthConfig = @{
            ApiKey = $ApiKey
            WorkingOrganization = $Organization
            WorkingInstance = $Instance
        }
    }
    $encryptedAuth = @{}
    ForEach($property in $AuthConfig.GetEnumerator()){
        $encryptedAuth."$($property.Name)" = (ConvertFrom-SecureString (ConvertTo-SecureString $property.Value -AsPlainText -Force))
    }
    $encryptedAuth | ConvertTo-Json | Set-Content $dir\credentials.json
}
<#
    $body = @{
    "is_bulk: false, // boolean
    "is_active: true, // boolean
    "is_force_dublicate: true, // boolean
    "checkout_id: 1, // integer
    "owner_id: 12, // integer
    "account_id: 13, // integer
    "serial_number: "11", // string (max 50 chars),
    "category_id: 111, // integer
    "type_id: 112, // integer
    "make_id: 113, // integer
    "model_id: 114, // integer
    "unique1_value: "u111", // string (max 100 chars),
    "unique2_value: "u112", // string (max 100 chars),
    "unique3_value: "u113", // string (max 100 chars),
    "unique4_value: "u114", // string (max 100 chars),
    "unique5_value: "u115", // string (max 100 chars),
    "unique6_value: "u116", // string (max 100 chars),
    "unique7_value: "u117", // string (max 100 chars),
    "unique_motherboard: "m11", // string (max 100 chars),
    "unique_bios: "b11", // string (max 100 chars),
    "name: "name11", // string (max 50 chars),
    "description: "d11", // string (max 250 chars),
    "note: "my note", // string (max 500 chars),
    "location_id: 0 // integer,
    "status_id": 6 // integer, show only assets with status id=6
    "entered_date": "2017-06-20T13:12:01.3600000" //string, Represent date in iso format
    "acquired_date": "2017-06-20T13:12:01.3600000" //string, Represent date in iso format
    "po_number: "2345 n/a", // string,
    "paid_value: "4.80", // string,
    "funding_source: "test", // string
}
#>


Function Set-SDAsset {
    [cmdletbinding(DefaultParameterSetName = 'ByParameter')]
    Param(
        [Parameter(ParameterSetName = 'ByParameter')] [string]$Status,
        [Parameter(ParameterSetName = 'ByBody')] [hashtable]$Body,

        [string]$key,
        [string]$Organization = $authConfig.WorkingOrganization,
        [string]$Instance = $authConfig.WorkingInstance,
        [string]$ApiKey = $authConfig.ApiKey
    )
    $resource = "assets/$key"
    
    If($PSCmdlet.ParameterSetName -eq 'ByParameter'){
        $body = @{}
        $body['status'] = $Status
    }

    $jsonbody = $body | ConvertTo-Json

    Write-Verbose $jsonbody
    # not ready yet
    #Invoke-SherpaDeskAPICall -Method Put -Resource $resource -Organization $Organization -Instance $Instance -ApiKey $ApiKey -Body $jsonbody
}
<#
    $body = @{
    "status" : "closed",
    "note_text" : "some note"
    "level_id" : 3,
    "project_id": 66,
    "class_id" : 3
    "priority_id" : 3
    "account_id" : 3,
    "account_location_id" : 0,
    "is_transfer_user_to_account": "false",
    "is_waiting_on_response" : "true",
    "creation_category_id" : 0,
    "creation_category_name" : "",
    "customfields_xml" : "<root><field id="4724"><caption>www.sherpadesk.com</caption><value>Yes</value></field></root>",
    "default_contract_id" : 0,
    "default_contract_name: "( Not Set )",
    "location_id" : 0,
    "submission_category" : "( Not Set )",
    "tech_id" : 496558,
    "user_id" : 496558,
    "board_list_id" : "77b764099b854452bf2e470825442677" // leave empty to not update, or "0" to reset
}
#>


Function Set-SDTicket {
    [cmdletbinding(DefaultParameterSetName = 'ByParameter')]
    Param(
        [Parameter(ParameterSetName = 'ByParameter')]
        [string]$Status,
        [Parameter(ParameterSetName = 'ByBody')]
        [hashtable]$Body,
        [string]$key,
        [string]$Organization = $authConfig.WorkingOrganization,
        [string]$Instance = $authConfig.WorkingInstance,
        [string]$ApiKey = $authConfig.ApiKey
    )
    $resource = "tickets/$key"
    
    If($PSCmdlet.ParameterSetName -eq 'ByParameter'){
        $body = @{}
        $body['status'] = $Status
    }

    $jsonbody = $body | ConvertTo-Json

    Write-Verbose $jsonbody

    Invoke-SherpaDeskAPICall -Method Put -Resource $resource -Organization $Organization -Instance $Instance -ApiKey $ApiKey -Body $jsonbody
}
Function Get-SDSavePath {
    Param (

    )
    If($PSVersionTable.PSVersion.Major -ge 6){
        # PS Core
        If($IsLinux){
            $saveDir = $env:HOME
        }ElseIf($IsWindows){
            $saveDir = $env:USERPROFILE
        }
    }Else{
        # Windows PS
        $saveDir = $env:USERPROFILE
    }
    "$saveDir\.sherpashell"
}
Function Invoke-SherpaDeskAPICall {
    Param(
        [string]$Resource,
        [ValidateSet('Get','Put','Post')]
        [string]$Method,
        [string]$Body,
        [string]$Organization,
        [string]$Instance,
        [string]$ApiKey
    )
    $baseUri = 'https://api.sherpadesk.com'

    $encodedAuth = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("$Organization-$Instance`:$ApiKey"))

    $header = @{
        Authorization = "Basic $encodedAuth"
        Accept = 'application/json'
    }
    
    If($Method -eq 'Get') {
        # Start a Timer to keep track of calls per second
        $Timer = [Diagnostics.Stopwatch]::StartNew()
        # Get Page zero
        $response = Invoke-RestMethod -Method $Method -Uri "$baseUri/$Resource" -Headers $header -ContentType 'application/json' -Body $Body
        # Check if a single page was requested, or the response has fewer than 25 items, indicating no more pages
        If ($Resource.Contains('?page=') -Or $response.Count -lt 25){
            $hasMoreResults = $false # If the response is less than 25 items, there are no more pages
        } else {
            $hasMoreResults = $true # If the response is 25 items, there are more pages to fetch
        }
        $page = 1   # Set page counter to 1 for the next page
        
        while ($hasMoreResults) {
            Write-Information "total seconds elapsed: $($Timer.elapsed.totalseconds) retrieving page ${page}" -InformationAction Continue
            # Invoke the API call for the next page of results
            # The page number is passed as a query parameter to the API call
            $currentPageResults = Invoke-RestMethod -Method $Method -Uri "$baseUri/$resource/?page=$page" -Headers $header -Body $Body
            # Flatten the results by adding each object from the current page to $response
            $currentPageResults | ForEach-Object { $response += $_ }
        
            # Check if the current page has fewer than 25 objects, indicating no more pages
            if ($currentPageResults.Count -lt 25) {
                $hasMoreResults = $false
            } else {
                $page++ # Increment the page counter to fetch the next page
            }
        }
        Write-Information "finished retrieval. total seconds elapsed: $($Timer.elapsed.totalseconds)" -InformationAction Continue
        $Timer.Stop() # Stop the Timer
        # $response now contains a single array of objects from all pages
        return $response

    }
    ElseIf(@('Post','Put','Delete') -contains $Method) {
        Invoke-RestMethod -Method $Method -Uri "$baseUri/$Resource" -Headers $header -ContentType 'application/json' -Body $Body
    }
}