Private/Build-LMSDTSchedule.ps1

function Build-LMSDTSchedule {
    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '', Justification = 'Required for the interactive wizard')]
    param (
        [Switch]$PassThru
    )

    try {
    function Read-RecurringTimeWindow {
        return @{
            StartHour   = Read-LMIntInRange -Prompt 'Start hour (0-23)' -Minimum 0 -Maximum 23
            StartMinute = Read-LMIntInRange -Prompt 'Start minute (0-59)' -Minimum 0 -Maximum 59
            EndHour     = Read-LMIntInRange -Prompt 'End hour (0-23)' -Minimum 0 -Maximum 23
            EndMinute   = Read-LMIntInRange -Prompt 'End minute (0-59)' -Minimum 0 -Maximum 59
        }
    }

    function Read-WeekDaySelection {
        $selectedDays = [System.Collections.Generic.List[string]]::new()
        $availableDays = $Script:LMSDTValidWeekDays

        do {
            Write-Host 'Select weekdays (enter numbers separated by commas, e.g. 1,4):'
            for ($i = 0; $i -lt $availableDays.Count; $i++) {
                Write-Host (" " + ($i + 1) + ". " + $availableDays[$i])
            }

            $inputValue = Read-LMWizardHost 'Weekday selection (q to cancel)'
            $selectedDays.Clear()
            $isValid = $true

            foreach ($part in ($inputValue -split ',')) {
                $dayNumber = 0
                if (-not [int]::TryParse($part.Trim(), [ref]$dayNumber) -or $dayNumber -lt 1 -or $dayNumber -gt $availableDays.Count) {
                    $isValid = $false
                    break
                }

                $dayName = $availableDays[$dayNumber - 1]
                if ($selectedDays -notcontains $dayName) {
                    $selectedDays.Add($dayName)
                }
            }

            if (-not $isValid -or $selectedDays.Count -eq 0) {
                Write-Host 'Invalid selection. Choose at least one weekday using numbers from the list.' -ForegroundColor Red
            }
        } while (-not $isValid -or $selectedDays.Count -eq 0)

        return @($selectedDays)
    }

    function Read-TimezoneSelection {
        if (Get-LMUserConfirmation -Prompt 'Use portal default timezone?' -DefaultAnswer 'y') {
            return $null
        }

        do {
            $timezone = Read-LMWizardHost 'Enter timezone (IANA or Windows name, e.g. America/New_York; q to cancel)'
            try {
                Test-LMTimezoneId -Timezone $timezone | Out-Null
                return $timezone
            }
            catch {
                Write-Host $_.Exception.Message -ForegroundColor Red
            }
        } while ($true)
    }

    $scheduleTypes = @(
        @{ Name = 'One-time: duration only (starts now)'; Value = 'OneTimeDurationOnly' }
        @{ Name = 'One-time: start + duration'; Value = 'OneTimeStartDuration' }
        @{ Name = 'One-time: fixed start and end'; Value = 'OneTimeRange' }
        @{ Name = 'Daily recurring'; Value = 'Daily' }
        @{ Name = 'Weekly recurring'; Value = 'Weekly' }
        @{ Name = 'Monthly: day of month'; Value = 'Monthly' }
        @{ Name = 'Monthly: by week'; Value = 'MonthlyByWeek' }
    )

    $selectedScheduleType = Get-LMUserSelection -Prompt 'Select SDT schedule type:' -Choices $scheduleTypes -ChoiceLabelProperty 'Name'
    $maxDurationMinutes = [int]::MaxValue

    $selection = @{
        ScheduleType = $selectedScheduleType.Value
    }

    switch ($selectedScheduleType.Value) {
        'OneTimeDurationOnly' {
            $selection.Duration = Read-LMIntInRange -Prompt 'Duration in minutes' -Minimum 1 -Maximum $maxDurationMinutes
        }

        'OneTimeStartDuration' {
            $selection.StartDate = Read-LMDateTimeValue -Prompt 'Start date/time' -Default (Get-Date)
            $selection.Duration = Read-LMIntInRange -Prompt 'Duration in minutes' -Minimum 1 -Maximum $maxDurationMinutes
        }

        'OneTimeRange' {
            $selection.StartDate = Read-LMDateTimeValue -Prompt 'Start date/time' -Default (Get-Date)
            do {
                $selection.EndDate = Read-LMDateTimeValue -Prompt 'End date/time' -Default $selection.StartDate.AddHours(1)
                if ($selection.EndDate -le $selection.StartDate) {
                    Write-Host 'End date/time must be after start date/time.' -ForegroundColor Red
                    $selection.Remove('EndDate')
                }
            } while (-not $selection.ContainsKey('EndDate'))
        }

        'Daily' {
            $selection.SdtType = 'Daily'
            $selection += Read-RecurringTimeWindow
        }

        'Weekly' {
            $selection.SdtType = 'Weekly'
            $selection += Read-RecurringTimeWindow
            $selection.WeekDay = Read-WeekDaySelection
        }

        'Monthly' {
            $selection.SdtType = 'Monthly'
            $selection += Read-RecurringTimeWindow

            $monthDayChoices = @(
                @{ Name = 'Specific day (1-31)'; Value = 'SpecificDay' }
                @{ Name = 'Last day of month'; Value = 'LastDay' }
            )
            $monthDayChoice = Get-LMUserSelection -Prompt 'Select monthly day type:' -Choices $monthDayChoices -ChoiceLabelProperty 'Name'
            if ($monthDayChoice.Value -eq 'LastDay') {
                $selection.DayOfMonth = -3
            }
            else {
                $selection.DayOfMonth = Read-LMIntInRange -Prompt 'Day of month (1-31)' -Minimum 1 -Maximum 31
            }
        }

        'MonthlyByWeek' {
            $selection.SdtType = 'MonthlyByWeek'
            $selection += Read-RecurringTimeWindow
            $selection.WeekDay = Read-WeekDaySelection

            $weekOfMonthChoices = @(
                @{ Name = 'First'; Value = 'First' }
                @{ Name = 'Second'; Value = 'Second' }
                @{ Name = 'Third'; Value = 'Third' }
                @{ Name = 'Fourth'; Value = 'Fourth' }
                @{ Name = 'Last'; Value = 'Last' }
            )
            $weekOfMonthChoice = Get-LMUserSelection -Prompt 'Select week of month:' -Choices $weekOfMonthChoices -ChoiceLabelProperty 'Name'
            $selection.WeekOfMonth = $weekOfMonthChoice.Value
        }
    }

    $timezone = Read-TimezoneSelection
    if ($timezone) {
        $selection.Timezone = $timezone
    }

    $scheduleParameters = ConvertTo-LMSDTScheduleSelection -Selection $selection
    $previewPayload = Build-LMSDTSchedulePayload -BoundParameters $scheduleParameters

    Write-Host ''
    Write-Host '--- SDT Schedule Preview ---' -ForegroundColor Cyan
    Write-LMSDTSchedulePreview -PreviewPayload $previewPayload
    Write-Host '----------------------------'

    if (-not (Get-LMUserConfirmation -Prompt 'Use this schedule?' -DefaultAnswer 'y')) {
        Write-Host 'Schedule wizard cancelled.' -ForegroundColor Yellow
        return $null
    }

    return $scheduleParameters
    }
    catch [LMSDTWizardCancelledException] {
        Write-Host $_.Exception.Message -ForegroundColor Yellow
        return $null
    }
}