functions/System/Set-TaskScheduleCatchup.ps1

function Set-TaskScheduleCatchup {
    <#
    .SYNOPSIS
    Configures WinHarden scheduled tasks with advanced catchup & recovery settings.

    .DESCRIPTION
    Applies reliable task execution settings to all WinHarden scheduled tasks:
    - Start when available (catchup on missed schedules)
    - Execution timeout limits (prevents runaway tasks)
    - Battery/offline execution (continuous security monitoring)
    - Hard termination (forces stuck tasks to stop)

    Requires admin rights. Uses COM-based Task Scheduler API for advanced configuration.

    .PARAMETER EnableCatchup
    Enable automatic task execution when system comes online (default: $true).

    .PARAMETER MaxTaskDurationHours
    Maximum execution time per task in hours (default: 2 hours).

    .PARAMETER EnableRetry
    Enable retry behavior documentation in output (default: $true).

    .PARAMETER RetryIntervalMinutes
    Suggested retry interval in minutes. Note: Actual retry config requires manual Task Scheduler UI setup (default: 15).

    .PARAMETER MaxRetries
    Maximum retry attempts. Documented for reference; applies to triggers via manual UI (default: 3).

    .EXAMPLE
    Set-TaskScheduleCatchup -EnableCatchup $true -MaxTaskDurationHours 2

    Configures all WinHarden tasks with 2-hour timeout and catchup enabled.

    .NOTES
    DEPENDENCIES: Requires Core module (Write-Log, Write-ErrorLog, Test-NotNullOrEmpty)
    REQUIRES: Administrator privileges
    MODIFIES: Scheduled Tasks in \Hardening\ folder
    ADR REFERENCE: ADR-004 (Error Handling), ADR-005 (Logging)
    #>


    param(
        [Parameter(Mandatory = $false)]
        [bool]$EnableCatchup = $true,

        [Parameter(Mandatory = $false)]
        [bool]$EnableRetry = $true,

        [Parameter(Mandatory = $false)]
        [int]$MaxTaskDurationHours = 2,

        [Parameter(Mandatory = $false)]
        [int]$RetryIntervalMinutes = 15,

        [Parameter(Mandatory = $false)]
        [int]$MaxRetries = 3
    )

    $ErrorActionPreference = 'Continue'

    function _CheckAdminRights {
        $windowsIdentity = [Security.Principal.WindowsIdentity]::GetCurrent()
        $principal = New-Object Security.Principal.WindowsPrincipal $windowsIdentity
        return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
    }

    function _GetTaskScheduler {
        $scheduler = New-Object -ComObject Schedule.Service
        $scheduler.Connect()
        return $scheduler
    }

    function _GetTaskSettings {
        param(
            [Parameter(Mandatory = $true)]
            [object]$Scheduler,

            [Parameter(Mandatory = $true)]
            [string]$TaskPath,

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

        try {
            $folder = $Scheduler.GetFolder($TaskPath)
            $task = $folder.GetTask($TaskName)
            return $task.Definition.Settings
        }
        catch {
            Write-ErrorLog -Message "Failed to get settings for task [$TaskName]" -ErrorRecord $_
            return $null
        }
    }

    function _ApplyTaskSettings {
        param(
            [Parameter(Mandatory = $true)]
            [object]$Settings,

            [Parameter(Mandatory = $true)]
            [bool]$EnableCatchup,

            [Parameter(Mandatory = $true)]
            [int]$MaxTaskDurationHours
        )

        try {
            $Settings.StartWhenAvailable = $EnableCatchup
            $timeSpan = New-TimeSpan -Hours $MaxTaskDurationHours
            $Settings.ExecutionTimeLimit = $timeSpan.ToString()
            $Settings.RunOnlyIfNetworkAvailable = $false
            $Settings.DisallowStartIfOnBatteries = $false
            $Settings.Compatibility = 2
            $Settings.AllowHardTerminate = $true
            return $true
        }
        catch {
            Write-ErrorLog -Message 'Failed to apply task settings' -ErrorRecord $_
            return $false
        }
    }

    function _UpdateTaskDefinition {
        param(
            [Parameter(Mandatory = $true)]
            [object]$Scheduler,

            [Parameter(Mandatory = $true)]
            [string]$TaskPath,

            [Parameter(Mandatory = $true)]
            [string]$TaskName,

            [Parameter(Mandatory = $true)]
            [object]$TaskDefinition
        )

        try {
            $folder = $Scheduler.GetFolder($TaskPath)
            $folder.UpdateDefinition($TaskName, $TaskDefinition)
            return $true
        }
        catch {
            Write-ErrorLog -Message "Failed to update task definition for [$TaskName]" -ErrorRecord $_
            return $false
        }
    }

    function _DiscoverWinHardenTasks {
        try {
            $tasks = @()
            $allTasks = Get-ScheduledTask -TaskPath '\Hardening\*' -ErrorAction SilentlyContinue
            $tasks = $allTasks | Where-Object { $_.TaskPath -like '*Hardening*' }
            return $tasks
        }
        catch {
            Write-ErrorLog -Message 'Failed to discover WinHarden tasks' -ErrorRecord $_
            return @()
        }
    }

    function _VerifyTaskSettings {
        param(
            [Parameter(Mandatory = $true)]
            [object]$Scheduler,

            [Parameter(Mandatory = $true)]
            [string]$TaskPath,

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

        try {
            $settings = _GetTaskSettings -Scheduler $Scheduler -TaskPath $TaskPath -TaskName $TaskName
            if ($settings) {
                $catchupStatus = if ($settings.StartWhenAvailable) {
                    'ENABLED'
                }
                else {
                    'DISABLED'
                }
                $timeoutHours = [int]([timespan]::Parse($settings.ExecutionTimeLimit).TotalHours)
                return @{ Catchup = $catchupStatus; TimeoutHours = $timeoutHours }
            }
            return $null
        }
        catch {
            Write-ErrorLog -Message "Failed to verify settings for task [$TaskName]" -ErrorRecord $_
            return $null
        }
    }

    if (-not (_CheckAdminRights)) {
        Write-ErrorLog -Message 'This function requires administrator privileges' -Severity ERROR
        return 1
    }

    Write-Log -Message 'Starting WinHarden task catchup configuration' -Level INFO

    $tasks = _DiscoverWinHardenTasks
    if ($tasks.Count -eq 0) {
        Write-ErrorLog -Message 'No WinHarden tasks found in Hardening folder. Run Set-ScheduledTasksHardening.ps1 first.' -Severity WARN
        return 1
    }

    Write-Log -Message "Discovered $($tasks.Count) WinHarden tasks" -Level INFO

    $configuredCount = 0
    $failureCount = 0

    try {
        $scheduler = _GetTaskScheduler

        foreach ($task in $tasks) {
            $taskName = $task.TaskName
            $taskPath = $task.TaskPath

            try {
                $settings = _GetTaskSettings -Scheduler $scheduler -TaskPath $taskPath -TaskName $taskName
                if ($null -eq $settings) {
                    $failureCount++
                    continue
                }

                if (_ApplyTaskSettings -Settings $settings -EnableCatchup $EnableCatchup -MaxTaskDurationHours $MaxTaskDurationHours) {
                    $taskDef = $scheduler.GetFolder($taskPath).GetTask($taskName).Definition
                    if (_UpdateTaskDefinition -Scheduler $scheduler -TaskPath $taskPath -TaskName $taskName -TaskDefinition $taskDef) {
                        Write-Log -Message "Configured task [$taskName] with catchup enabled (timeout: $MaxTaskDurationHours hours)" -Level INFO
                        $configuredCount++
                    }
                    else {
                        Write-ErrorLog -Message "Failed to update task [$taskName]" -Severity WARN
                        $failureCount++
                    }
                }
                else {
                    $failureCount++
                }
            }
            catch {
                Write-ErrorLog -Message "Error configuring task [$taskName]" -ErrorRecord $_
                $failureCount++
            }
        }

        Write-Log -Message "Task catchup configuration complete: $configuredCount success, $failureCount failed" -Level INFO

        Write-Log -Message 'Verifying task configuration' -Level INFO
        foreach ($task in $tasks) {
            $verification = _VerifyTaskSettings -Scheduler $scheduler -TaskPath $task.TaskPath -TaskName $task.TaskName
            if ($verification) {
                Write-Log -Message "Verified [$($task.TaskName)] - Catchup: $($verification.Catchup), Timeout: $($verification.TimeoutHours)h" -Level INFO
            }
        }

        if ($configuredCount -eq $tasks.Count) {
            Write-Log -Message "All $($tasks.Count) tasks configured successfully with catchup enabled" -Level INFO
            return 0
        }
        else {
            Write-ErrorLog -Message "Configuration completed with warnings: $configuredCount/$($tasks.Count) tasks successful" -Severity WARN
            return 1
        }
    }
    catch {
        Write-ErrorLog -Message 'Unexpected error during task configuration' -ErrorRecord $_
        return 1
    }
}