src/public/Orchestration/Start-AitherSchedule.ps1

#Requires -Version 7.0

<#
.SYNOPSIS
    Schedule a playbook or script to run at specified intervals

.DESCRIPTION
    Creates scheduled tasks (Windows Task Scheduler) or cron jobs (Linux/macOS)
    to execute playbooks or scripts automatically.

.PARAMETER Name
    Name for the scheduled task

.PARAMETER Playbook
    Name of the playbook to schedule

.PARAMETER Script
    Script number or name to schedule

.PARAMETER Schedule
    Schedule frequency: Daily, Weekly, Hourly, AtStartup, OnLogon, or cron expression

.PARAMETER Time
    Time to run (for Daily/Weekly schedules)

.PARAMETER DaysOfWeek
    Days of week for Weekly schedule

.PARAMETER Interval
    Interval in minutes for recurring schedules

.PARAMETER Enabled
    Enable the schedule immediately (default: true)

.PARAMETER Remove
    Remove an existing schedule

.EXAMPLE
    Start-AitherSchedule -Name 'DailyValidation' -Playbook 'pr-validation' -Schedule Daily -Time '02:00'

.EXAMPLE
    Start-AitherSchedule -Name 'HourlyHealthCheck' -Script 0501 -Schedule Hourly -Interval 60

.EXAMPLE
    Start-AitherSchedule -Name 'DailyValidation' -Remove

.NOTES
    Cross-platform scheduling support using platform-native schedulers.
#>

function Start-AitherSchedule {
[CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Create')]
param(
    [Parameter(Mandatory=$false, Position = 0)]
    [string]$Name,

    [Parameter(ParameterSetName = 'Create')]
    [string]$Playbook,

    [Parameter(ParameterSetName = 'Create')]
    [string]$Script,

    [Parameter(ParameterSetName = 'Create')]
    [ValidateSet('Daily', 'Weekly', 'Hourly', 'AtStartup', 'OnLogon', 'Cron')]
    [string]$Schedule = 'Daily',

    [Parameter(ParameterSetName = 'Create')]
    [string]$Time = '00:00',

    [Parameter(ParameterSetName = 'Create')]
    [ValidateSet('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday')]
    [string[]]$DaysOfWeek = @('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'),

    [Parameter(ParameterSetName = 'Create')]
    [int]$Interval = 60,

    [Parameter(ParameterSetName = 'Create')]
    [bool]$Enabled = $true,

    [Parameter(ParameterSetName = 'Remove')]
    [switch]$Remove,

    [switch]$ShowOutput
)

begin {
    # Save original log targets
    $originalLogTargets = $script:AitherLogTargets

    # Set log targets based on ShowOutput parameter
    if ($ShowOutput) {
        # Ensure Console is in the log targets
        if ($script:AitherLogTargets -notcontains 'Console') {
            $script:AitherLogTargets += 'Console'
        }
    }
    else {
        # Remove Console from log targets if present (default behavior)
        if ($script:AitherLogTargets -contains 'Console') {
            $script:AitherLogTargets = $script:AitherLogTargets | Where-Object { $_ -ne 'Console' }
        }
    }

    $moduleRoot = Get-AitherModuleRoot
    $schedulesPath = Join-Path $moduleRoot 'library' 'schedules'

    if (-not (Test-Path $schedulesPath)) {
        New-Item -ItemType Directory -Path $schedulesPath -Force | Out-Null
    }
}

process {
    try {
        try {
        if ($Remove) {
            # Remove schedule
            if ($IsWindows) {
                Unregister-ScheduledTask -TaskName "AitherZero_$Name" -Confirm:$false -ErrorAction SilentlyContinue
            }
            else {
                # Remove cron job
                $cronFile = Join-Path $schedulesPath "$Name.cron"
                if (Test-Path $cronFile) {
                    Remove-Item $cronFile -Force
                }
            }

            $scheduleFile = Join-Path $schedulesPath "$Name.psd1"
            if (Test-Path $scheduleFile) {
                Remove-Item $scheduleFile -Force
            }

            Write-AitherLog -Level Information -Message "Schedule removed: $Name" -Source $PSCmdlet.MyInvocation.MyCommand.Name
            return
        }

        # Validate inputs
        if (-not $Playbook -and -not $Script) {
            # During module validation, parameters may be empty - skip validation
            if ($PSCmdlet.MyInvocation.InvocationName -eq '.') {
                return
            }
            throw "Either -Playbook or -Script must be specified"
        }

        # Build command
        $command = if ($Playbook) {
            "Invoke-AitherPlaybook -Name '$Playbook'"
        }
        else {
            "Invoke-AitherScript -Script '$Script'"
        }

        # Create schedule definition
        $scheduleDef = @{
            Name = $Name
            Command = $command
            Schedule = $Schedule
            Enabled = $Enabled
            Created = Get-Date
        }
        if ($Time) { $scheduleDef.Time = $Time }
        if ($DaysOfWeek) { $scheduleDef.DaysOfWeek = $DaysOfWeek }
        if ($Interval) { $scheduleDef.Interval = $Interval }

        # Save schedule definition
        $scheduleFile = Join-Path $schedulesPath "$Name.psd1"
        $scheduleDef | ConvertTo-Json -Depth 10 | Set-Content $scheduleFile

        # Create platform-specific schedule
        if ($IsWindows) {
            $taskName = "AitherZero_$Name"
            $action = New-ScheduledTaskAction -Execute 'pwsh.exe' -Argument "-NoProfile -Command `"$command`""

            $trigger = switch ($Schedule) {
                'Daily' { New-ScheduledTaskTrigger -Daily -At $Time }
                'Weekly' { New-ScheduledTaskTrigger -Weekly -DaysOfWeek $DaysOfWeek -At $Time }
                'Hourly' { New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes $Interval) -RepetitionDuration (New-TimeSpan -Days 365) }
                'AtStartup' { New-ScheduledTaskTrigger -AtStartup }
                'OnLogon' { New-ScheduledTaskTrigger -AtLogOn }
            }

            $principal = New-ScheduledTaskPrincipal -UserId "$env:USERDOMAIN\$env:USERNAME" -LogonType Interactive
            $settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries

            Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Principal $principal -Settings $settings -Description "AitherZero scheduled task: $Name" | Out-Null

            if (-not $Enabled) {
                Disable-ScheduledTask -TaskName $taskName | Out-Null
            }
        }
        else {
            # Linux/macOS cron
            $cronFile = Join-Path $schedulesPath "$Name.cron"
            $cronLine = switch ($Schedule) {
                'Daily' { "0 $($Time.Split(':')[1]) $($Time.Split(':')[0]) * * * pwsh -NoProfile -Command `"$command`"" }
                'Hourly' { "0 * * * * pwsh -NoProfile -Command `"$command`"" }
                default { "0 0 * * * pwsh -NoProfile -Command `"$command`"" }
            }

            Set-Content -Path $cronFile -Value $cronLine

            Write-AitherLog -Level Information -Message "Cron file created: $cronFile" -Source $PSCmdlet.MyInvocation.MyCommand.Name
            Write-AitherLog -Level Information -Message "To install, run: crontab $cronFile" -Source $PSCmdlet.MyInvocation.MyCommand.Name
        }

        Write-AitherLog -Level Information -Message "Schedule created: $Name" -Source $PSCmdlet.MyInvocation.MyCommand.Name
        return Get-AitherSchedule -Name $Name
    }
    catch {
        Write-AitherLog -Level Error -Message "Failed to create schedule: $_" -Source 'Start-AitherSchedule' -Exception $_
        throw
    }
    }
    finally {
        # Restore original log targets
        $script:AitherLogTargets = $originalLogTargets
    }
}

}