Private/Test-MaintenanceWindow.ps1

function Test-MaintenanceWindow {
    <#
    .SYNOPSIS
        Checks if a computer is currently within a defined maintenance window.
    .DESCRIPTION
        Reads maintenance window definitions from the engine configuration file
        at $env:USERPROFILE\.runbookengine\maintenance-windows.json.
        Supports both one-time windows and recurring weekly windows.
        Window format: { ComputerName, Pattern, Start, End, Recurring, DayOfWeek }
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$ComputerName,

        [Parameter()]
        [string]$ConfigPath
    )

    if (-not $ConfigPath) {
        $ConfigPath = Join-Path $env:USERPROFILE '.runbookengine\maintenance-windows.json'
    }

    $result = [PSCustomObject]@{
        ComputerName     = $ComputerName
        InWindow         = $false
        WindowName       = $null
        WindowStart      = $null
        WindowEnd        = $null
        IsRecurring      = $false
        CheckedAt        = (Get-Date).ToString('o')
    }

    if (-not (Test-Path $ConfigPath)) {
        Write-Verbose "No maintenance windows configuration found at $ConfigPath"
        return $result
    }

    try {
        $windowsJson = Get-Content -Path $ConfigPath -Raw -ErrorAction Stop
        $windows = $windowsJson | ConvertFrom-Json -ErrorAction Stop
    }
    catch {
        Write-Warning "Failed to parse maintenance windows configuration: $_"
        return $result
    }

    if (-not $windows -or $windows.Count -eq 0) {
        Write-Verbose "No maintenance windows defined."
        return $result
    }

    $now = Get-Date

    foreach ($window in $windows) {
        # Check if this window applies to this computer
        $matches = $false

        if ($window.ComputerName -and $window.ComputerName -eq $ComputerName) {
            $matches = $true
        }
        elseif ($window.Pattern) {
            if ($ComputerName -like $window.Pattern) {
                $matches = $true
            }
        }

        if (-not $matches) { continue }

        if ($window.Recurring -eq $true) {
            # Recurring window - check day of week and time
            $targetDay = $window.DayOfWeek
            $currentDay = $now.DayOfWeek.ToString()

            if ($targetDay -and $currentDay -eq $targetDay) {
                # Parse start and end as time-only values
                try {
                    $startTime = [DateTime]::Parse($window.Start)
                    $endTime = [DateTime]::Parse($window.End)

                    # Build today's window start and end
                    $windowStart = Get-Date -Hour $startTime.Hour -Minute $startTime.Minute -Second 0
                    $windowEnd = Get-Date -Hour $endTime.Hour -Minute $endTime.Minute -Second 0

                    # Handle overnight windows (end time less than start time)
                    if ($windowEnd -lt $windowStart) {
                        $windowEnd = $windowEnd.AddDays(1)
                    }

                    if ($now -ge $windowStart -and $now -le $windowEnd) {
                        $result.InWindow = $true
                        $result.WindowName = if ($window.Name) { $window.Name } else { "Recurring $targetDay window" }
                        $result.WindowStart = $windowStart.ToString('o')
                        $result.WindowEnd = $windowEnd.ToString('o')
                        $result.IsRecurring = $true
                        return $result
                    }
                }
                catch {
                    Write-Verbose "Failed to parse recurring window times: $_"
                }
            }
        }
        else {
            # One-time window - check exact datetime range
            try {
                $windowStart = [DateTime]::Parse($window.Start)
                $windowEnd = [DateTime]::Parse($window.End)

                if ($now -ge $windowStart -and $now -le $windowEnd) {
                    $result.InWindow = $true
                    $result.WindowName = if ($window.Name) { $window.Name } else { "One-time window" }
                    $result.WindowStart = $windowStart.ToString('o')
                    $result.WindowEnd = $windowEnd.ToString('o')
                    $result.IsRecurring = $false
                    return $result
                }
            }
            catch {
                Write-Verbose "Failed to parse one-time window dates: $_"
            }
        }
    }

    return $result
}