PsTimeTracking.psm1

#Region './Classes/1.class1.ps1' -1

class Class1
{
    [string]$Name = 'Class1'

    Class1()
    {
        #default Constructor
    }

    [String] ToString()
    {
        # Typo "calss" is intentional
        return ( 'This calss is {0}' -f $this.Name)
    }
}
#EndRegion './Classes/1.class1.ps1' 16
#Region './Classes/2.class2.ps1' -1

class Class2
{
    [string]$Name = 'Class2'

    Class2()
    {
        #default constructor
    }

    [String] ToString()
    {
        return ( 'This calss is {0}' -f $this.Name)
    }
}
#EndRegion './Classes/2.class2.ps1' 15
#Region './Classes/3.class11.ps1' -1

class Class11 : Class1
{
    [string]$Name = 'Class11'

    Class11 ()
    {
    }

    [String] ToString()
    {
        return ( 'This calss is {0}:{1}' -f $this.Name,'class1')
    }
}
#EndRegion './Classes/3.class11.ps1' 14
#Region './Classes/4.class12.ps1' -1

class Class12 : Class1
{
    [string]$Name = 'Class12'

    Class12 ()
    {
    }

    [String] ToString()
    {
        return ( 'This calss is {0}:{1}' -f $this.Name,'class1')
    }
}
#EndRegion './Classes/4.class12.ps1' 14
#Region './Private/Get-PstConfig.ps1' -1

<#
.SYNOPSIS
Gets the PsTimeTracking configuration from the JSON file.

.DESCRIPTION
Internal function that loads the configuration file containing clients and projects.
If the file doesn't exist, it creates a default configuration.

.EXAMPLE
PS> Get-PstConfig

Returns the configuration object with all clients and projects.
#>

function Get-PstConfig {
    [CmdletBinding()]
    param()

    # Cross-platform config folder location
    if ($IsWindows -or $PSVersionTable.PSVersion.Major -le 5) {
        $folder = Join-Path $env:LOCALAPPDATA 'PstTimeTracker'
    }
    else {
        # Linux/macOS
        $folder = Join-Path $HOME '.local/share/PstTimeTracker'
    }

    $configFile = Join-Path $folder 'config.json'

    Write-Debug "Config file location: $configFile"

    if (!(Test-Path $folder)) {
        New-Item $folder -ItemType Directory | Out-Null
    }

    if (!(Test-Path $configFile)) {
        # Create default configuration
        $defaultConfig = [PSCustomObject]@{
            Clients = @(
                [PSCustomObject]@{
                    Name = 'ClientA'
                    Projects = @('Project Alpha', 'Project Beta', 'Support')
                }
                [PSCustomObject]@{
                    Name = 'ClientB'
                    Projects = @('Website Redesign', 'Database Migration')
                }
                [PSCustomObject]@{
                    Name = 'MMG - Data'
                    Projects = @('Data Analysis', 'ETL Pipeline', 'Reporting')
                }
                [PSCustomObject]@{
                    Name = 'MMG - DevOps'
                    Projects = @('CI/CD Setup', 'Infrastructure', 'Monitoring')
                }
            )
        }

        $defaultConfig | ConvertTo-Json -Depth 10 | Out-File $configFile -Encoding UTF8
    }

    $config = Get-Content $configFile -Raw | ConvertFrom-Json
    return $config
}
#EndRegion './Private/Get-PstConfig.ps1' 64
#Region './Private/Save-PstConfig.ps1' -1

<#
.SYNOPSIS
Saves the PsTimeTracking configuration to the JSON file.

.DESCRIPTION
Internal function that saves the configuration object to the JSON file.

.PARAMETER Config
The configuration object to save.

.EXAMPLE
PS> Save-PstConfig -Config $config

Saves the configuration object to the JSON file.
#>

function Save-PstConfig {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [PSCustomObject]$Config
    )

    # Cross-platform config folder location
    if ($IsWindows -or $PSVersionTable.PSVersion.Major -le 5) {
        $folder = Join-Path $env:LOCALAPPDATA 'PstTimeTracker'
    }
    else {
        # Linux/macOS
        $folder = Join-Path $HOME '.local/share/PstTimeTracker'
    }

    $configFile = Join-Path $folder 'config.json'

    if (!(Test-Path $folder)) {
        New-Item $folder -ItemType Directory | Out-Null
    }

    $Config | ConvertTo-Json -Depth 10 | Out-File $configFile -Encoding UTF8
}
#EndRegion './Private/Save-PstConfig.ps1' 40
#Region './Public/Add-PstClient.ps1' -1

<#
.SYNOPSIS
Adds a new client to the configuration.

.DESCRIPTION
Adds a new client with an optional list of projects to the configuration.

.PARAMETER Name
The name of the client to add.

.PARAMETER Projects
Optional array of project names for this client.

.EXAMPLE
PS> Add-PstClient -Name 'ClientC' -Projects @('Project 1', 'Project 2')

Adds a new client called ClientC with two projects.

.EXAMPLE
PS> Add-PstClient -Name 'ClientD'

Adds a new client called ClientD with no projects.
#>

function Add-PstClient {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$Name,

        [Parameter()]
        [string[]]$Projects = @()
    )

    $config = Get-PstConfig

    # Check if client already exists
    $existingClient = $config.Clients | Where-Object { $_.Name -eq $Name }
    if ($existingClient) {
        Write-Warning "Client '$Name' already exists. Use Update-PstClient to modify it."
        return
    }

    # Create new client
    $newClient = [PSCustomObject]@{
        Name = $Name
        Projects = $Projects
    }

    # Convert to ArrayList for easier manipulation
    $clientsList = [System.Collections.ArrayList]::new()
    if ($config.Clients) {
        foreach ($client in $config.Clients) {
            $null = $clientsList.Add($client)
        }
    }
    $null = $clientsList.Add($newClient)

    # Recreate config object to avoid mutation issues
    $newConfig = [PSCustomObject]@{
        Clients = $clientsList.ToArray()
    }

    Save-PstConfig -Config $newConfig
}
#EndRegion './Public/Add-PstClient.ps1' 65
#Region './Public/Add-PstProject.ps1' -1

<#
.SYNOPSIS
Adds a project to a client.

.DESCRIPTION
Adds a new project to the specified client's project list.

.PARAMETER Client
The name of the client to add the project to.

.PARAMETER Project
The name of the project to add.

.EXAMPLE
PS> Add-PstProject -Client 'ClientA' -Project 'New Project'

Adds 'New Project' to ClientA's project list.
#>

function Add-PstProject {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ArgumentCompleter({
            param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
            if ($IsWindows -or $PSVersionTable.PSVersion.Major -le 5) {
                $configFile = Join-Path $env:LOCALAPPDATA 'PstTimeTracker\config.json'
            } else {
                $configFile = Join-Path $HOME '.local/share/PstTimeTracker/config.json'
            }
            if (Test-Path $configFile) {
                $config = Get-Content $configFile -Raw | ConvertFrom-Json
                $config.Clients.Name | Where-Object { $_ -like "$wordToComplete*" }
            }
        })]
        [string]$Client,

        [Parameter(Mandatory)]
        [string]$Project
    )

    $config = Get-PstConfig

    $clientObj = $config.Clients | Where-Object { $_.Name -eq $Client }
    if (!$clientObj) {
        Write-Warning "Client '$Client' not found. Use Add-PstClient to create it first."
        return
    }

    # Check if project already exists
    if ($clientObj.Projects -contains $Project) {
        Write-Warning "Project '$Project' already exists for client '$Client'."
        return
    }

    # Convert to ArrayList for easier manipulation
    $projectsList = [System.Collections.ArrayList]::new()
    if ($clientObj.Projects) {
        foreach ($proj in $clientObj.Projects) {
            $null = $projectsList.Add($proj)
        }
    }
    $null = $projectsList.Add($Project)

    # Recreate the client list with updated projects
    $updatedClients = foreach ($client in $config.Clients) {
        if ($client.Name -eq $Client) {
            [PSCustomObject]@{
                Name = $client.Name
                Projects = $projectsList.ToArray()
            }
        } else {
            [PSCustomObject]@{
                Name = $client.Name
                Projects = $client.Projects
            }
        }
    }

    # Recreate config object
    $newConfig = [PSCustomObject]@{
        Clients = $updatedClients
    }

    Save-PstConfig -Config $newConfig

    Write-Host "Project '$Project' added to client '$Client' successfully." -ForegroundColor Green
}
#EndRegion './Public/Add-PstProject.ps1' 88
#Region './Public/Add-PstTime.ps1' -1

<#
.SYNOPSIS
Adds a chunk of time to the day so far.

.DESCRIPTION
Adds a chunk of time to the day so far.

.PARAMETER Client
The client to add time to. Must be a client configured in the system.

.PARAMETER Project
The project to add time to. Must be a project associated with the client.

.PARAMETER Minutes
The number of minutes to add.

.PARAMETER StartTime
If provided this will be added to the day summary.

.EXAMPLE
PS> Add-PstTime -Client ClientA -Project 'Project Alpha' -Minutes 60

Adds 60 mins to the day so far for ClientA - Project Alpha.

#>


function Add-PstTime {
    param (
        [Parameter(Mandatory)]
        [ArgumentCompleter({
            param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
            if ($IsWindows -or $PSVersionTable.PSVersion.Major -le 5) {
                $configFile = Join-Path $env:LOCALAPPDATA 'PstTimeTracker\config.json'
            } else {
                $configFile = Join-Path $HOME '.local/share/PstTimeTracker/config.json'
            }
            if (Test-Path $configFile) {
                $config = Get-Content $configFile -Raw | ConvertFrom-Json
                $config.Clients.Name | Where-Object { $_ -like "$wordToComplete*" }
            }
        })]
        [string]$Client,

        [Parameter(Mandatory)]
        [ArgumentCompleter({
            param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
            $clientName = $fakeBoundParameters['Client']
            if ($clientName) {
                if ($IsWindows -or $PSVersionTable.PSVersion.Major -le 5) {
                    $configFile = Join-Path $env:LOCALAPPDATA 'PstTimeTracker\config.json'
                } else {
                    $configFile = Join-Path $HOME '.local/share/PstTimeTracker/config.json'
                }
                if (Test-Path $configFile) {
                    $config = Get-Content $configFile -Raw | ConvertFrom-Json
                    $client = $config.Clients | Where-Object { $_.Name -eq $clientName }
                    if ($client) {
                        $client.Projects | Where-Object { $_ -like "$wordToComplete*" }
                    }
                }
            }
        })]
        [string]$Project,

        [Parameter(Mandatory)]
        [int]$Minutes,

        [Parameter()]
        [datetime]$StartTime
    )

    # Validate client exists
    $config = Get-PstConfig
    $clientObj = $config.Clients | Where-Object { $_.Name -eq $Client }
    if (!$clientObj) {
        Write-Warning "Client '$Client' not found in configuration. Use Get-PstClient to see available clients."
        return
    }

    # Validate project exists for this client
    if ($clientObj.Projects -notcontains $Project) {
        Write-Warning "Project '$Project' not found for client '$Client'. Use Get-PstProject -Client '$Client' to see available projects."
        return
    }
    Clear-Host

    Write-host ('Adding {0} minutes for {1} - {2}' -f $Minutes, $Client, $Project)

    [Array]$TodaysWork = Restore-Pstday

    $Addtime = $Client | Select-Object  @{l='Client';e={$Client}},@{l='Project';e={$Project}},@{l='StartTime';e={$StartTime}}, @{l='Elapsed';e={New-TimeSpan -Minutes $Minutes}}
    $Addtime | Format-Table -AutoSize | Out-String | Write-Host  -ForegroundColor White
    $TodaysWork += $AddTime

    # backup the day so far just in case
    Backup-PstDay -TodaysWork $TodaysWork

    # Output day summary
    Get-PstDaySummary

}
#EndRegion './Public/Add-PstTime.ps1' 102
#Region './Public/Backup-PstDay.ps1' -1

<#
.SYNOPSIS
Backups the day so far to a json file in the local appdata folder.

.DESCRIPTION
backups the day so far to a json file in the local appdata folder.

This is used to restore the day so far if the script is closed.

.PARAMETER TodaysWork
The day so far to backup.

.EXAMPLE
PS> Backup-PstDay -TodaysWork $TodaysWork

Backups the day so far to a json file in the local appdata folder.

#>

function Backup-PstDay {
    param (
        [parameter(ValueFromPipeline=$true, Mandatory)]
        $TodaysWork
    )
    if ($IsWindows -or $PSVersionTable.PSVersion.Major -le 5) {
        $folder = Join-Path $env:LOCALAPPDATA 'PstTimeTracker'
    } else {
        $folder = Join-Path $HOME '.local/share/PstTimeTracker'
    }

    if (!(Test-Path $folder)) {
        New-Item $folder -ItemType Directory | Out-Null
    }

    if($TodaysWork) {

        $fileName = Join-Path $folder ('todayswork-{0}.json' -f (Get-Date -Format 'yyyy-MM-dd'))
        $TodaysWork | Select-Object Client, Project, StartTime, @{l='ElapsedTotalSeconds';e={$_.Elapsed.TotalSeconds}} | ConvertTo-Json | Out-File $fileName
    }

    # remove json files older than 10 days
    Get-ChildItem $folder *.json | where-object lastWriteTime -lt (get-date).AddDays(-10) | Remove-Item
}
#EndRegion './Public/Backup-PstDay.ps1' 43
#Region './Public/Get-PstClient.ps1' -1

<#
.SYNOPSIS
Gets all configured clients or a specific client.

.DESCRIPTION
Returns all clients from the configuration or a specific client by name.

.PARAMETER Name
Optional. The name of the client to retrieve. If not specified, all clients are returned.

.EXAMPLE
PS> Get-PstClient

Returns all configured clients.

.EXAMPLE
PS> Get-PstClient -Name 'ClientA'

Returns the ClientA configuration including its projects.
#>

function Get-PstClient {
    [CmdletBinding()]
    param(
        [Parameter()]
        [ArgumentCompleter({
            param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
            if ($IsWindows -or $PSVersionTable.PSVersion.Major -le 5) {
                $configFile = Join-Path $env:LOCALAPPDATA 'PstTimeTracker\config.json'
            } else {
                $configFile = Join-Path $HOME '.local/share/PstTimeTracker/config.json'
            }
            if (Test-Path $configFile) {
                $config = Get-Content $configFile -Raw | ConvertFrom-Json
                $config.Clients.Name | Where-Object { $_ -like "$wordToComplete*" }
            }
        })]
        [string]$Name
    )

    $config = Get-PstConfig

    if ($Name) {
        $client = $config.Clients | Where-Object { $_.Name -eq $Name }
        if ($client) {
            return $client
        } else {
            Write-Warning "Client '$Name' not found."
            return $null
        }
    } else {
        return $config.Clients
    }
}
#EndRegion './Public/Get-PstClient.ps1' 54
#Region './Public/Get-PstDaySummary.ps1' -1

<#
.SYNOPSIS
Gets the day summary.

.DESCRIPTION
Gets the day summary.

If a date is provided it will restore the day so far from the json file in the local appdata folder and display the summary.

.PARAMETER date
If provided this will restore the specified day from the json file in the local appdata folder and display the summary.

.EXAMPLE
PS> Get-PstDaySummary

This will display the day summary for the day so far.

.EXAMPLE
PS> Get-PstDaySummary -date 2023-01-01

This will restore the json from the local appdata folder for 2023-01-01 and display the summary.

#>

function Get-PstDaySummary {
    param (
        $date
    )
    if($date) {
        $RestoredWork = Restore-PstDay -date $date

        if (-not $RestoredWork) {
            Write-Host ('No work found for {0}...' -f $date) -ForegroundColor DarkRed -BackgroundColor White
            return
        }

        [Array]$results = $RestoredWork | Group-Object Client, Project | Select-Object Name, @{l='Total';e={New-TimeSpan -Seconds (($_.Group.Elapsed.TotalSeconds | Measure-Object -sum ).sum)}}
        $results += [PSCustomObject]@{
            Name = '===Total'
            Total = New-TimeSpan -Seconds ($RestoredWork | Select-Object @{l='TotalSecs';e={$_.elapsed.TotalSeconds}} | Measure-object -Property TotalSecs -Sum).Sum
        }
        $results | Out-String | Write-Host  -ForegroundColor White

    } else {
        $TodaysWork = Restore-PstDay

        if($TodaysWork) {

            Write-Host '--------------------------' -ForegroundColor DarkGreen -BackgroundColor White
            Write-Host 'So far today: ' -ForegroundColor DarkGreen -BackgroundColor White
            Write-Host '--------------------------' -ForegroundColor DarkGreen -BackgroundColor White

            [Array]$results = $TodaysWork | Group-Object Client, Project | Select-Object Name, @{l='Total';e={New-TimeSpan -Seconds (($_.Group.Elapsed.TotalSeconds | Measure-Object -sum ).sum)}}
            $results += [PSCustomObject]@{
                Name = '===Total'
                Total = New-TimeSpan -Seconds ($TodaysWork | Select-Object @{l='TotalSecs';e={$_.elapsed.TotalSeconds}} | Measure-object -Property TotalSecs -Sum).Sum
            }
            $results | Out-String | Write-Host  -ForegroundColor White
        } else {
            Write-Host 'Nothing recorded yet...' -ForegroundColor DarkRed -BackgroundColor White
        }
    }

}
#EndRegion './Public/Get-PstDaySummary.ps1' 64
#Region './Public/Get-PstProject.ps1' -1

<#
.SYNOPSIS
Gets projects for a specific client or all projects.

.DESCRIPTION
Returns projects for a specified client from the configuration.

.PARAMETER Client
The name of the client whose projects to retrieve.

.EXAMPLE
PS> Get-PstProject -Client 'ClientA'

Returns all projects for ClientA.
#>

function Get-PstProject {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ArgumentCompleter({
            param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
            if ($IsWindows -or $PSVersionTable.PSVersion.Major -le 5) {
                $configFile = Join-Path $env:LOCALAPPDATA 'PstTimeTracker\config.json'
            } else {
                $configFile = Join-Path $HOME '.local/share/PstTimeTracker/config.json'
            }
            if (Test-Path $configFile) {
                $config = Get-Content $configFile -Raw | ConvertFrom-Json
                $config.Clients.Name | Where-Object { $_ -like "$wordToComplete*" }
            }
        })]
        [string]$Client
    )

    $config = Get-PstConfig

    $clientObj = $config.Clients | Where-Object { $_.Name -eq $Client }
    if (!$clientObj) {
        Write-Warning "Client '$Client' not found."
        return $null
    }

    return $clientObj.Projects
}
#EndRegion './Public/Get-PstProject.ps1' 45
#Region './Public/Remove-PstClient.ps1' -1

<#
.SYNOPSIS
Removes a client from the configuration.

.DESCRIPTION
Removes the specified client and all its projects from the configuration.

.PARAMETER Name
The name of the client to remove.

.PARAMETER Force
If specified, removes the client without confirmation.

.EXAMPLE
PS> Remove-PstClient -Name 'ClientC'

Prompts for confirmation, then removes ClientC from the configuration.

.EXAMPLE
PS> Remove-PstClient -Name 'ClientC' -Force

Removes ClientC without confirmation.
#>

function Remove-PstClient {
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory)]
        [ArgumentCompleter({
            param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
            if ($IsWindows -or $PSVersionTable.PSVersion.Major -le 5) {
                $configFile = Join-Path $env:LOCALAPPDATA 'PstTimeTracker\config.json'
            } else {
                $configFile = Join-Path $HOME '.local/share/PstTimeTracker/config.json'
            }
            if (Test-Path $configFile) {
                $config = Get-Content $configFile -Raw | ConvertFrom-Json
                $config.Clients.Name | Where-Object { $_ -like "$wordToComplete*" }
            }
        })]
        [string]$Name,

        [Parameter()]
        [switch]$Force
    )

    $config = Get-PstConfig

    $existingClient = $config.Clients | Where-Object { $_.Name -eq $Name }
    if (!$existingClient) {
        Write-Warning "Client '$Name' not found."
        return
    }

    if ($Force -or $PSCmdlet.ShouldProcess($Name, "Remove client")) {
        $updatedClients = $config.Clients | Where-Object { $_.Name -ne $Name }

        # Recreate config object to avoid mutation issues
        $newConfig = [PSCustomObject]@{
            Clients = $updatedClients
        }

        Save-PstConfig -Config $newConfig
        Write-Host "Client '$Name' removed successfully." -ForegroundColor Green
    }
}
#EndRegion './Public/Remove-PstClient.ps1' 66
#Region './Public/Remove-PstProject.ps1' -1

<#
.SYNOPSIS
Removes a project from a client.

.DESCRIPTION
Removes the specified project from a client's project list.

.PARAMETER Client
The name of the client to remove the project from.

.PARAMETER Project
The name of the project to remove.

.PARAMETER Force
If specified, removes the project without confirmation.

.EXAMPLE
PS> Remove-PstProject -Client 'ClientA' -Project 'Old Project'

Prompts for confirmation, then removes 'Old Project' from ClientA.

.EXAMPLE
PS> Remove-PstProject -Client 'ClientA' -Project 'Old Project' -Force

Removes 'Old Project' from ClientA without confirmation.
#>

function Remove-PstProject {
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory)]
        [ArgumentCompleter({
            param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
            if ($IsWindows -or $PSVersionTable.PSVersion.Major -le 5) {
                $configFile = Join-Path $env:LOCALAPPDATA 'PstTimeTracker\config.json'
            } else {
                $configFile = Join-Path $HOME '.local/share/PstTimeTracker/config.json'
            }
            if (Test-Path $configFile) {
                $config = Get-Content $configFile -Raw | ConvertFrom-Json
                $config.Clients.Name | Where-Object { $_ -like "$wordToComplete*" }
            }
        })]
        [string]$Client,

        [Parameter(Mandatory)]
        [ArgumentCompleter({
            param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
            $clientName = $fakeBoundParameters['Client']
            if ($clientName) {
                if ($IsWindows -or $PSVersionTable.PSVersion.Major -le 5) {
                    $configFile = Join-Path $env:LOCALAPPDATA 'PstTimeTracker\config.json'
                } else {
                    $configFile = Join-Path $HOME '.local/share/PstTimeTracker/config.json'
                }
                if (Test-Path $configFile) {
                    $config = Get-Content $configFile -Raw | ConvertFrom-Json
                    $client = $config.Clients | Where-Object { $_.Name -eq $clientName }
                    if ($client) {
                        $client.Projects | Where-Object { $_ -like "$wordToComplete*" }
                    }
                }
            }
        })]
        [string]$Project,

        [Parameter()]
        [switch]$Force
    )

    $config = Get-PstConfig

    $clientObj = $config.Clients | Where-Object { $_.Name -eq $Client }
    if (!$clientObj) {
        Write-Warning "Client '$Client' not found."
        return
    }

    if ($clientObj.Projects -notcontains $Project) {
        Write-Warning "Project '$Project' not found for client '$Client'."
        return
    }

    if ($Force -or $PSCmdlet.ShouldProcess("$Client - $Project", "Remove project")) {
        $updatedProjects = $clientObj.Projects | Where-Object { $_ -ne $Project }

        # Recreate the client list with updated projects
        $updatedClients = foreach ($client in $config.Clients) {
            if ($client.Name -eq $Client) {
                [PSCustomObject]@{
                    Name = $client.Name
                    Projects = $updatedProjects
                }
            } else {
                [PSCustomObject]@{
                    Name = $client.Name
                    Projects = $client.Projects
                }
            }
        }

        # Recreate config object
        $newConfig = [PSCustomObject]@{
            Clients = $updatedClients
        }

        Save-PstConfig -Config $newConfig
        Write-Host "Project '$Project' removed from client '$Client' successfully." -ForegroundColor Green
    }
}
#EndRegion './Public/Remove-PstProject.ps1' 110
#Region './Public/Restore-PstDay.ps1' -1

<#
.SYNOPSIS
Restores the day so far from the json file in the local appdata folder.

.DESCRIPTION
Restores the day so far from the json file in the local appdata folder.

This is used to restore the day so far if the script is closed.

.PARAMETER date
If provided this will restore the specified day from the json file in the local appdata folder.

.EXAMPLE
PS> Restore-PstDay

This will restore the day so far from the json file in the local appdata folder.

.EXAMPLE
PS> Restore-PstDay -date 2023-01-01

This will restore the json from the local appdata folder for 2023-01-01.

#>

function Restore-PstDay {
    param (
        $date = (Get-Date).Date
    )

    if ($IsWindows -or $PSVersionTable.PSVersion.Major -le 5) {
        $folder = Join-Path $env:LOCALAPPDATA 'PstTimeTracker'
    } else {
        $folder = Join-Path $HOME '.local/share/PstTimeTracker'
    }

    $fileName = Join-Path $folder ('todayswork-{0}.json' -f (Get-Date($date) -Format 'yyyy-MM-dd'))

    Write-Verbose "Restoring day from file: $fileName"

    if (Test-Path $fileName) {

        Get-Content $fileName | ConvertFrom-Json | Select-Object Client, Project, StartTime, @{l='Elapsed';e={New-TimeSpan -Seconds $_.ElapsedTotalSeconds}}
    }
}
#EndRegion './Public/Restore-PstDay.ps1' 44
#Region './Public/Start-PstDay.ps1' -1

<#
.SYNOPSIS
Starts the day.

.DESCRIPTION
Starts the day.

Restore the day so far from the json file in the local appdata folder if it exists.

.EXAMPLE
PS> Start-PstDay

This will start the day and restore the day so far from the json file in the local appdata folder if it exists.

#>

function Start-PstDay {

    [Array]$TodaysWork = Restore-PstDay

}
#EndRegion './Public/Start-PstDay.ps1' 21
#Region './Public/Start-PstTimer.ps1' -1

<#
.SYNOPSIS
Starts a timer for a client and project.

.DESCRIPTION
Starts a timer for a client and project. The client and project must be configured in the system.

.PARAMETER Client
Client to track time against. Must be a client configured in the system.

.PARAMETER Project
Project to track time against. Must be a project associated with the client.

.EXAMPLE
PS> Start-PstTimer -Client ClientA -Project 'Project Alpha'

This will start a timer for ClientA - Project Alpha.

#>

function Start-PstTimer {
    param (
        [Parameter(Mandatory)]
        [ArgumentCompleter({
            param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
            if ($IsWindows -or $PSVersionTable.PSVersion.Major -le 5) {
                $configFile = Join-Path $env:LOCALAPPDATA 'PstTimeTracker\config.json'
            } else {
                $configFile = Join-Path $HOME '.local/share/PstTimeTracker/config.json'
            }
            if (Test-Path $configFile) {
                $config = Get-Content $configFile -Raw | ConvertFrom-Json
                $config.Clients.Name | Where-Object { $_ -like "$wordToComplete*" }
            }
        })]
        [string]$Client,

        [Parameter(Mandatory)]
        [ArgumentCompleter({
            param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
            $clientName = $fakeBoundParameters['Client']
            if ($clientName) {
                if ($IsWindows -or $PSVersionTable.PSVersion.Major -le 5) {
                    $configFile = Join-Path $env:LOCALAPPDATA 'PstTimeTracker\config.json'
                } else {
                    $configFile = Join-Path $HOME '.local/share/PstTimeTracker/config.json'
                }
                if (Test-Path $configFile) {
                    $config = Get-Content $configFile -Raw | ConvertFrom-Json
                    $client = $config.Clients | Where-Object { $_.Name -eq $clientName }
                    if ($client) {
                        $client.Projects | Where-Object { $_ -like "$wordToComplete*" }
                    }
                }
            }
        })]
        [string]$Project
    )

    # Validate client exists
    $config = Get-PstConfig
    $clientObj = $config.Clients | Where-Object { $_.Name -eq $Client }
    if (!$clientObj) {
        Write-Warning "Client '$Client' not found in configuration. Use Get-PstClient to see available clients."
        return
    }

    # Validate project exists for this client
    if ($clientObj.Projects -notcontains $Project) {
        Write-Warning "Project '$Project' not found for client '$Client'. Use Get-PstProject -Client '$Client' to see available projects."
        return
    }
    Clear-Host

    $startTime = (Get-Date)

    $timer = New-Object System.Diagnostics.Stopwatch
    $timer.Start()

    [Array]$TodaysWork = Restore-PstDay

    if($TodaysWork) {
        # Output day summary
        Get-PstDaySummary
    }

    $null = Read-host ('Working on {0} - {1}, since {2} - Press any key to stop?' -f $Client, $Project, $startTime)

    $timer.Stop()

    Write-Host '--------------------------' -ForegroundColor DarkMagenta -BackgroundColor White
    Write-Host 'Just worked on: ' -ForegroundColor DarkMagenta -BackgroundColor White
    Write-Host '--------------------------' -ForegroundColor DarkMagenta -BackgroundColor White

    $Addtime = $timer | Select-Object  @{l='Client';e={$Client}},@{l='Project';e={$Project}},@{l='StartTime';e={$startTime}},  Elapsed
    $Addtime | Format-Table -AutoSize | Out-String | Write-Host  -ForegroundColor White
    $TodaysWork += $AddTime

    # backup the day so far just in case
    Backup-PstDay -TodaysWork $TodaysWork

    # Output day summary
    Get-PstDaySummary

}
#EndRegion './Public/Start-PstTimer.ps1' 105
#Region './Public/Update-PstClient.ps1' -1

<#
.SYNOPSIS
Updates an existing client's name.

.DESCRIPTION
Updates the name of an existing client in the configuration.

.PARAMETER Name
The current name of the client.

.PARAMETER NewName
The new name for the client.

.EXAMPLE
PS> Update-PstClient -Name 'ClientA' -NewName 'Client Alpha'

Renames ClientA to 'Client Alpha'.
#>

function Update-PstClient {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ArgumentCompleter({
            param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
            if ($IsWindows -or $PSVersionTable.PSVersion.Major -le 5) {
                $configFile = Join-Path $env:LOCALAPPDATA 'PstTimeTracker\config.json'
            } else {
                $configFile = Join-Path $HOME '.local/share/PstTimeTracker/config.json'
            }
            if (Test-Path $configFile) {
                $config = Get-Content $configFile -Raw | ConvertFrom-Json
                $config.Clients.Name | Where-Object { $_ -like "$wordToComplete*" }
            }
        })]
        [string]$Name,

        [Parameter(Mandatory)]
        [string]$NewName
    )

    $config = Get-PstConfig

    $existingClient = $config.Clients | Where-Object { $_.Name -eq $Name }
    if (!$existingClient) {
        Write-Warning "Client '$Name' not found."
        return
    }

    # Check if new name already exists
    $conflictClient = $config.Clients | Where-Object { $_.Name -eq $NewName }
    if ($conflictClient) {
        Write-Warning "A client with name '$NewName' already exists."
        return
    }

    # Recreate the client list with updated name
    $updatedClients = foreach ($client in $config.Clients) {
        if ($client.Name -eq $Name) {
            [PSCustomObject]@{
                Name = $NewName
                Projects = $client.Projects
            }
        } else {
            [PSCustomObject]@{
                Name = $client.Name
                Projects = $client.Projects
            }
        }
    }

    # Recreate config object
    $newConfig = [PSCustomObject]@{
        Clients = $updatedClients
    }

    Save-PstConfig -Config $newConfig

    Write-Host "Client renamed from '$Name' to '$NewName' successfully." -ForegroundColor Green
}
#EndRegion './Public/Update-PstClient.ps1' 80
#Region './Public/Update-PstProject.ps1' -1

<#
.SYNOPSIS
Updates a project name for a client.

.DESCRIPTION
Renames an existing project for the specified client.

.PARAMETER Client
The name of the client whose project to update.

.PARAMETER Project
The current name of the project.

.PARAMETER NewName
The new name for the project.

.EXAMPLE
PS> Update-PstProject -Client 'ClientA' -Project 'Project Alpha' -NewName 'Alpha Project v2'

Renames the project from 'Project Alpha' to 'Alpha Project v2' for ClientA.
#>

function Update-PstProject {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ArgumentCompleter({
            param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
            if ($IsWindows -or $PSVersionTable.PSVersion.Major -le 5) {
                $configFile = Join-Path $env:LOCALAPPDATA 'PstTimeTracker\config.json'
            } else {
                $configFile = Join-Path $HOME '.local/share/PstTimeTracker/config.json'
            }
            if (Test-Path $configFile) {
                $config = Get-Content $configFile -Raw | ConvertFrom-Json
                $config.Clients.Name | Where-Object { $_ -like "$wordToComplete*" }
            }
        })]
        [string]$Client,

        [Parameter(Mandatory)]
        [ArgumentCompleter({
            param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
            $clientName = $fakeBoundParameters['Client']
            if ($clientName) {
                if ($IsWindows -or $PSVersionTable.PSVersion.Major -le 5) {
                    $configFile = Join-Path $env:LOCALAPPDATA 'PstTimeTracker\config.json'
                } else {
                    $configFile = Join-Path $HOME '.local/share/PstTimeTracker/config.json'
                }
                if (Test-Path $configFile) {
                    $config = Get-Content $configFile -Raw | ConvertFrom-Json
                    $client = $config.Clients | Where-Object { $_.Name -eq $clientName }
                    if ($client) {
                        $client.Projects | Where-Object { $_ -like "$wordToComplete*" }
                    }
                }
            }
        })]
        [string]$Project,

        [Parameter(Mandatory)]
        [string]$NewName
    )

    $config = Get-PstConfig

    $clientObj = $config.Clients | Where-Object { $_.Name -eq $Client }
    if (!$clientObj) {
        Write-Warning "Client '$Client' not found."
        return
    }

    if ($clientObj.Projects -notcontains $Project) {
        Write-Warning "Project '$Project' not found for client '$Client'."
        return
    }

    # Check if new name already exists
    if ($clientObj.Projects -contains $NewName) {
        Write-Warning "A project with name '$NewName' already exists for client '$Client'."
        return
    }

    # Create updated project list
    $updatedProjects = $clientObj.Projects | ForEach-Object {
        if ($_ -eq $Project) { $NewName } else { $_ }
    }

    # Recreate the client list with updated projects
    $updatedClients = foreach ($client in $config.Clients) {
        if ($client.Name -eq $Client) {
            [PSCustomObject]@{
                Name = $client.Name
                Projects = $updatedProjects
            }
        } else {
            [PSCustomObject]@{
                Name = $client.Name
                Projects = $client.Projects
            }
        }
    }

    # Recreate config object
    $newConfig = [PSCustomObject]@{
        Clients = $updatedClients
    }

    Save-PstConfig -Config $newConfig

    Write-Host "Project renamed from '$Project' to '$NewName' for client '$Client' successfully." -ForegroundColor Green
}
#EndRegion './Public/Update-PstProject.ps1' 113