Stepper.psm1

function Clear-StepperState {
    <#
    .SYNOPSIS
        Removes the saved state file.
     
    .DESCRIPTION
        Deletes the Stepper state file from disk, effectively resetting
        all progress. Supports -WhatIf and -Confirm for safety.
     
    .PARAMETER Path
        The path to the state file to remove. If not specified, uses the
        default path from Get-StateFilePath.
     
    .EXAMPLE
        Clear-StepperState
         
        Removes the default state file.
     
    .EXAMPLE
        Clear-StepperState -WhatIf
         
        Shows what would happen without actually removing the file.
     
    .EXAMPLE
        Clear-StepperState -Path 'C:\Temp\my-state.json'
         
        Removes a custom state file.
     
    .NOTES
        If the file doesn't exist, a warning is displayed.
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param(
        [Parameter(Mandatory = $false)]
        [string]$Path = (Get-StateFilePath)
    )
    
    if (Test-Path $Path) {
        if ($PSCmdlet.ShouldProcess($Path, "Remove Stepper state file")) {
            try {
                Remove-Item -Path $Path -Force -ErrorAction Stop
                Write-Host "State file removed: $Path" -ForegroundColor Green
                Write-Verbose "State successfully cleared"
            } catch {
                Write-Error "Failed to remove state file '$Path': $_"
            }
        }
    } else {
        Write-Warning "No state file found at: $Path"
    }
}

function Get-StateFilePath {
    <#
    .SYNOPSIS
        Returns the path to the state file.
     
    .DESCRIPTION
        Returns the full path to the Stepper state file, creating the
        directory if it doesn't exist.
     
    .PARAMETER FileName
        The name of the state file. Default is 'stepper-state.json'.
     
    .OUTPUTS
        System.String - The full path to the state file.
     
    .EXAMPLE
        $statePath = Get-StateFilePath
         
        Gets the default state file path.
     
    .EXAMPLE
        $statePath = Get-StateFilePath -FileName 'custom-state.json'
         
        Gets a custom state file path.
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter(Mandatory = $false)]
        [string]$FileName = 'stepper-state.json'
    )
    
    # Store in user profile for persistence across sessions
    # Alternative: Use temp folder for session-only persistence
    # Cross-platform home directory detection
    $homeDir = if ($IsWindows -or $PSVersionTable.PSVersion.Major -le 5) {
        $env:USERPROFILE
    } else {
        $env:HOME
    }
    
    $stateDir = Join-Path -Path $homeDir -ChildPath '.stepper'
    
    if (-not (Test-Path $stateDir)) {
        New-Item -Path $stateDir -ItemType Directory -Force | Out-Null
        Write-Verbose "Created state directory: $stateDir"
    }
    
    $statePath = Join-Path -Path $stateDir -ChildPath $FileName
    Write-Verbose "State file path: $statePath"
    
    return $statePath
}

function Get-StepperState {
    <#
    .SYNOPSIS
        Loads the Stepper state from file, or creates a new one.
     
    .DESCRIPTION
        Attempts to load a saved Stepper state from disk. If the file
        doesn't exist or can't be loaded, returns a new empty state.
         
        Converts the JSON PSCustomObject back to a hashtable for easier
        manipulation in PowerShell.
     
    .PARAMETER Path
        The path to the state file. If not specified, uses the default
        path from Get-StateFilePath.
     
    .OUTPUTS
        System.Collections.Hashtable - The loaded or new Stepper state.
     
    .EXAMPLE
        $state = Get-StepperState
         
        Loads the state from the default location or creates new.
     
    .EXAMPLE
        $state = Get-StepperState -Path 'C:\Temp\my-state.json'
         
        Loads the state from a custom location.
     
    .NOTES
        If loading fails, warnings are displayed and a new state is returned.
        This ensures the Stepper can always proceed.
    #>

    [CmdletBinding()]
    [OutputType([hashtable])]
    param(
        [Parameter(Mandatory = $false)]
        [string]$Path = (Get-StateFilePath)
    )
    
    if (Test-Path $Path) {
        try {
            Write-Verbose "Loading state from: $Path"
            
            $json = Get-Content -Path $Path -Raw -Encoding UTF8
            $stateObject = $json | ConvertFrom-Json
            
            # Convert PSCustomObject back to hashtable for easier manipulation
            $state = @{
                Version = $stateObject.Version
                StepperId = $stateObject.StepperId
                StartedAt = $stateObject.StartedAt
                LastUpdated = $stateObject.LastUpdated
                CompletedSteps = @($stateObject.CompletedSteps)
                CurrentStepIndex = $stateObject.CurrentStepIndex
                Status = $stateObject.Status
                StepResults = @{}
                Metadata = @{}
            }
            
            # Convert nested objects
            if ($stateObject.StepResults) {
                $stateObject.StepResults.PSObject.Properties | ForEach-Object {
                    $state.StepResults[$_.Name] = $_.Value
                }
            }
            
            if ($stateObject.Metadata) {
                $stateObject.Metadata.PSObject.Properties | ForEach-Object {
                    $state.Metadata[$_.Name] = $_.Value
                }
            }
            
            Write-Verbose "State loaded successfully (ID: $($state.StepperId))"
            return $state
        } catch {
            Write-Warning "Failed to load state file: $_"
            Write-Warning "Starting with fresh state..."
            return New-StepperState
        }
    } else {
        Write-Verbose "No existing state file found, creating new state"
        return New-StepperState
    }
}

function Invoke-StepperStep {
    <#
    .SYNOPSIS
        Executes a single Stepper step and tracks the result.
     
    .DESCRIPTION
        Runs the script block for a given Stepper step, tracks timing,
        handles errors, and records results in the state object.
     
    .PARAMETER Step
        A PSCustomObject containing the step definition with Name, Description,
        ScriptBlock, and AcceptsAllResults properties.
     
    .PARAMETER State
        The Stepper state hashtable to update with results.
     
    .PARAMETER AllData
        Optional array containing results from all previously completed steps.
        Array index matches step number: AllData[1] = Step 1 result,
        AllData[2] = Step 2 result, etc. AllData[0] is reserved for initial input data.
     
    .OUTPUTS
        System.Boolean - $true if step succeeded, $false if it failed.
     
    .EXAMPLE
        $success = Invoke-StepperStep -Step $stepDef -State $state
         
        Executes a step and updates the state.
     
    .EXAMPLE
        $success = Invoke-StepperStep -Step $stepDef -State $state -AllResults $allResults
         
        Executes a step that needs access to previous results.
     
    .NOTES
        Results are automatically added to the state object.
        Duration is tracked automatically.
        Errors are caught and logged in the state.
    #>

    [CmdletBinding()]
    [OutputType([bool])]
    param(
        [Parameter(Mandatory = $true)]
        [PSCustomObject]$Step,
        
        [Parameter(Mandatory = $true)]
        [hashtable]$State,
        
        [Parameter(Mandatory = $false)]
        [array]$AllData = @()
    )
    
    $stepName = $Step.Name
    $startTime = Get-Date
    
    Write-Verbose "Executing step: $stepName"
    
    try {
        # Execute the step's script block
        # Always pass AllData if there are previous results
        # Array index matches step number: AllData[1] = Step 1, etc.
        $result = if ($AllData.Count -gt 1) {
            Write-Verbose "Passing AllData array to step (Count: $($AllData.Count - 1) previous results)"
            & $Step.ScriptBlock -AllData $AllData
        } else {
            Write-Verbose "No previous results to pass, executing directly"
            & $Step.ScriptBlock
        }
        
        $duration = (Get-Date) - $startTime
        
        # Record the result
        $State.StepResults[$stepName] = @{
            Status = 'Completed'
            CompletedAt = Get-Date -Format 'o'
            Duration = "$([math]::Round($duration.TotalSeconds, 2))s"
            Result = $result
        }
        
        # Mark as completed
        if ($State.CompletedSteps -notcontains $stepName) {
            $State.CompletedSteps += $stepName
        }
        
        Write-Host "`n ✓ Step completed successfully in $([math]::Round($duration.TotalSeconds, 2))s" -ForegroundColor Green
        Write-Verbose "Step '$stepName' completed successfully"
        
        return $true
    } catch {
        $duration = (Get-Date) - $startTime
        
        # Record the failure
        $State.StepResults[$stepName] = @{
            Status = 'Failed'
            FailedAt = Get-Date -Format 'o'
            Duration = "$([math]::Round($duration.TotalSeconds, 2))s"
            Error = $_.Exception.Message
            ErrorDetails = $_.ScriptStackTrace
        }
        
        Write-Host "`n ✗ Step failed: $($_.Exception.Message)" -ForegroundColor Red
        Write-Host " $($_.ScriptStackTrace)" -ForegroundColor DarkRed
        Write-Verbose "Step '$stepName' failed: $($_.Exception.Message)"
        
        return $false
    }
}

function New-StepperState {
    <#
    .SYNOPSIS
        Creates a new empty Stepper state object.
     
    .DESCRIPTION
        Initializes a new hashtable containing the default structure for
        tracking Stepper progress, including metadata about the environment.
     
    .OUTPUTS
        System.Collections.Hashtable - A new Stepper state object.
     
    .EXAMPLE
        $state = New-StepperState
         
        Creates a new Stepper state with default values.
     
    .NOTES
        The state object includes:
        - Version: State file format version
        - StepperId: Unique identifier for this Stepper run
        - StartedAt: Timestamp when Stepper started
        - LastUpdated: Timestamp of last state update
        - CompletedSteps: Array of completed step names
        - CurrentStepIndex: Index of current/next step
        - Status: Current status (InProgress, Completed, Failed)
        - StepResults: Hashtable of results per step
        - Metadata: Environment information
    #>

    [CmdletBinding()]
    [OutputType([hashtable])]
    param()
    
    Write-Verbose "Creating new Stepper state"
    
    return @{
        Version = '1.0.0'
        StepperId = [guid]::NewGuid().ToString()
        StartedAt = Get-Date -Format 'o'
        LastUpdated = Get-Date -Format 'o'
        CompletedSteps = @()
        CurrentStepIndex = 0
        Status = 'InProgress'
        StepResults = @{}
        Metadata = @{
            ComputerName = $env:COMPUTERNAME
            UserName = $env:USERNAME
            PSVersion = $PSVersionTable.PSVersion.ToString()
            Domain = $env:USERDNSDOMAIN
        }
    }
}

function Save-StepperState {
    <#
    .SYNOPSIS
        Saves the Stepper state to a JSON file.
     
    .DESCRIPTION
        Serializes the Stepper state hashtable to JSON format and saves
        it to disk. Updates the LastUpdated timestamp automatically.
     
    .PARAMETER State
        The Stepper state hashtable to save.
     
    .PARAMETER Path
        The path where the state file should be saved. If not specified,
        uses the default path from Get-StateFilePath.
     
    .EXAMPLE
        Save-StepperState -State $state
         
        Saves the state to the default location.
     
    .EXAMPLE
        Save-StepperState -State $state -Path 'C:\Temp\my-state.json'
         
        Saves the state to a custom location.
     
    .NOTES
        The state is saved with indentation for readability.
        Throws an error if save fails.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [hashtable]$State,
        
        [Parameter(Mandatory = $false)]
        [string]$Path = (Get-StateFilePath)
    )
    
    try {
        # Update timestamp
        $State.LastUpdated = Get-Date -Format 'o'
        
        Write-Verbose "Saving state to: $Path"
        
        # Convert to JSON with good depth for nested objects
        $json = $State | ConvertTo-Json -Depth 10 -Compress:$false
        
        # Save to file
        $json | Set-Content -Path $Path -Force -Encoding UTF8
        
        Write-Verbose "State saved successfully"
        Write-Debug "State content: $json"
    } catch {
        Write-Error "Failed to save state to '$Path': $_"
        throw
    }
}

function Show-StepperHeader {
    <#
    .SYNOPSIS
        Displays a formatted header for the Stepper.
     
    .DESCRIPTION
        Shows a visually formatted header with the Stepper tool name
        and version at the start of the Stepper.
     
    .PARAMETER Title
        The title to display in the header. Default is "Multi-Step Stepper Tool".
     
    .PARAMETER Version
        The version string to display. Default is "1.0.0".
     
    .EXAMPLE
        Show-StepperHeader
         
        Displays the default header.
     
    .EXAMPLE
        Show-StepperHeader -Title "Security Stepper" -Version "2.0.0"
         
        Displays a custom header.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false)]
        [string]$Title = "Multi-Step Stepper Tool",
        
        [Parameter(Mandatory = $false)]
        [string]$Version = "1.0.0"
    )
    
    $border = "=" * 70
    Write-Host $border -ForegroundColor Cyan
    Write-Host " $Title" -ForegroundColor Cyan
    Write-Host " Version $Version" -ForegroundColor Cyan
    Write-Host $border -ForegroundColor Cyan
    Write-Host ""
}

function Show-StepperProgress {
    <#
    .SYNOPSIS
        Displays current progress summary.
     
    .DESCRIPTION
        Shows a formatted summary of the Stepper progress including:
        - Stepper ID
        - Start and update timestamps
        - Current status
        - Completed steps with durations
        - Overall percentage complete
     
    .PARAMETER State
        The Stepper state hashtable containing progress information.
     
    .PARAMETER TotalSteps
        The total number of steps in the Stepper.
     
    .EXAMPLE
        Show-StepperProgress -State $state -TotalSteps 5
         
        Displays progress for a 5-step Stepper.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [hashtable]$State,
        
        [Parameter(Mandatory = $true)]
        [int]$TotalSteps
    )
    
    $completed = $State.CompletedSteps.Count
    $percentComplete = if ($TotalSteps -gt 0) { 
        [math]::Round(($completed / $TotalSteps) * 100, 1) 
    } else { 
        0 
    }
    
    Write-Host "`nProgress Summary:" -ForegroundColor Cyan
    Write-Host (" " + ("-" * 50)) -ForegroundColor Gray
    Write-Host " Stepper ID : $($State.StepperId)" -ForegroundColor Gray
    Write-Host " Started At : $($State.StartedAt)" -ForegroundColor Gray
    Write-Host " Last Updated : $($State.LastUpdated)" -ForegroundColor Gray
    Write-Host " Status : $($State.Status)" -ForegroundColor $(
        if ($State.Status -eq 'Completed') { 'Green' } 
        elseif ($State.Status -eq 'Failed') { 'Red' } 
        else { 'Yellow' }
    )
    Write-Host " Completed Steps : $completed / $TotalSteps ($percentComplete%)" -ForegroundColor Gray
    Write-Host (" " + ("-" * 50)) -ForegroundColor Gray
    
    if ($State.CompletedSteps.Count -gt 0) {
        Write-Host "`nCompleted Steps:" -ForegroundColor Green
        $State.CompletedSteps | ForEach-Object {
            $stepResult = $State.StepResults[$_]
            $duration = if ($stepResult.Duration) { " ($($stepResult.Duration))" } else { "" }
            Write-Host " ✓ $_$duration" -ForegroundColor Green
        }
    }
    
    Write-Host ""
}

function Show-StepperStepHeader {
    <#
    .SYNOPSIS
        Displays a header for the current Stepper step.
     
    .DESCRIPTION
        Shows a formatted header indicating the current step number,
        total steps, and step name.
     
    .PARAMETER StepName
        The name of the current step.
     
    .PARAMETER StepNumber
        The current step number (1-based).
     
    .PARAMETER TotalSteps
        The total number of steps in the Stepper.
     
    .EXAMPLE
        Show-StepperStepHeader -StepName "Environment Check" -StepNumber 1 -TotalSteps 5
         
        Displays header for step 1 of 5.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$StepName,
        
        [Parameter(Mandatory = $true)]
        [int]$StepNumber,
        
        [Parameter(Mandatory = $true)]
        [int]$TotalSteps
    )
    
    $border = "-" * 70
    Write-Host "`n$border" -ForegroundColor Cyan
    Write-Host "Step $StepNumber of ${TotalSteps}: $StepName" -ForegroundColor Cyan
    Write-Host "$border" -ForegroundColor Cyan
}

function Test-StepperStateValidity {
    <#
    .SYNOPSIS
        Validates if the saved state is still relevant.
     
    .DESCRIPTION
        Checks the Stepper state for validity by verifying:
        - Version compatibility
        - Age of the state (default max 7 days)
        - Valid timestamp format
     
    .PARAMETER State
        The Stepper state hashtable to validate.
     
    .PARAMETER MaxAgeDays
        Maximum age in days before state is considered stale.
        Default is 7 days.
     
    .OUTPUTS
        System.Boolean - $true if state is valid, $false otherwise.
     
    .EXAMPLE
        if (Test-StepperStateValidity -State $state) {
            # State is valid, proceed
        }
         
        Validates state with default 7-day age limit.
     
    .EXAMPLE
        if (Test-StepperStateValidity -State $state -MaxAgeDays 30) {
            # State is valid, proceed
        }
         
        Validates state with custom 30-day age limit.
     
    .NOTES
        Displays warnings if state is invalid.
    #>

    [CmdletBinding()]
    [OutputType([bool])]
    param(
        [Parameter(Mandatory = $true)]
        [hashtable]$State,
        
        [Parameter(Mandatory = $false)]
        [int]$MaxAgeDays = 7
    )
    
    # Check version compatibility
    if ($State.Version -ne '1.0.0') {
        Write-Warning "State file version mismatch. Expected 1.0.0, found $($State.Version)"
        return $false
    }
    
    # Check age
    if ($State.LastUpdated) {
        try {
            $lastUpdate = [DateTime]::Parse($State.LastUpdated)
            $age = (Get-Date) - $lastUpdate
            
            if ($age.TotalDays -gt $MaxAgeDays) {
                Write-Warning "State is $([math]::Round($age.TotalDays, 1)) days old (max: $MaxAgeDays days)"
                return $false
            }
            
            Write-Verbose "State age: $([math]::Round($age.TotalDays, 1)) days (valid)"
        } catch {
            Write-Warning "Invalid LastUpdated timestamp in state: $($State.LastUpdated)"
            return $false
        }
    } else {
        Write-Warning "State missing LastUpdated timestamp"
        return $false
    }
    
    Write-Verbose "State validation passed"
    return $true
}

function Get-StepperSteps {
    <#
    .SYNOPSIS
        Loads Stepper steps from JSON configuration.
     
    .DESCRIPTION
        Reads the stepper-config.json file and dynamically loads
        step scripts from the configured paths. Returns an array of
        PSCustomObjects with Name, Description, ScriptBlock, and AcceptsAllResults properties.
         
        Steps are loaded from individual .ps1 files in the Steps directory.
        Only enabled steps are included in the returned array.
     
    .PARAMETER ConfigPath
        Path to the configuration JSON file. If not specified, attempts to find a JSON file
        with the same base name as the calling script (e.g., MyScript.ps1 -> MyScript.json).
        Falls back to stepper-config.json in the module root if no match is found.
     
    .OUTPUTS
        PSCustomObject[] - Array of step definition objects with Name, Description, ScriptBlock, and AcceptsAllResults properties.
     
    .EXAMPLE
        $steps = Get-StepperSteps
         
        Automatically finds a JSON config with the same name as the calling script.
         
    .EXAMPLE
        $steps = Get-StepperSteps -ConfigPath "C:\MyProject\custom-config.json"
         
        Uses a specific configuration file.
     
    .NOTES
        Configuration file structure:
        {
          "StepperSteps": [
            {
              "name": "StepName",
              "description": "Step description",
              "scriptPath": "Steps/Step-ScriptName.ps1",
              "enabled": true,
              "order": 1,
              "acceptsAllResults": false
            }
          ]
        }
         
        To add new steps:
        1. Create a new .ps1 file in the Steps directory
        2. Add an entry to stepper-config.json
        3. Set "enabled": true and assign an order number
         
        To disable a step without deleting it:
        - Set "enabled": false in the JSON configuration
    #>

    [CmdletBinding()]
    [OutputType([array])]
    param(
        [Parameter(Mandatory = $false)]
        [string]$ConfigPath
    )
    
    Write-Verbose "Loading Stepper step configuration"
    
    # Default config path logic
    if (-not $ConfigPath) {
        # Try to find a JSON config with the same name as the calling script
        # Walk the call stack to find the user's script (not a .psm1 module file)
        $callStack = Get-PSCallStack
        $userScript = $null
        
        foreach ($caller in $callStack | Select-Object -Skip 1) {
            $scriptPath = $caller.ScriptName
            if ($scriptPath -and (Test-Path $scriptPath) -and $scriptPath -notlike '*.psm1') {
                $userScript = $scriptPath
                break
            }
        }
        
        if ($userScript) {
            $scriptDirectory = Split-Path -Parent $userScript
            $scriptBaseName = [System.IO.Path]::GetFileNameWithoutExtension($userScript)
            $matchingConfig = Join-Path -Path $scriptDirectory -ChildPath "$scriptBaseName.json"
            
            if (Test-Path -Path $matchingConfig) {
                $ConfigPath = $matchingConfig
                Write-Verbose "Auto-discovered config: $ConfigPath"
            } else {
                Write-Verbose "No matching config found at: $matchingConfig"
            }
        }
        
        # Fall back to module root config
        if (-not $ConfigPath) {
            $moduleRoot = Split-Path -Parent $PSScriptRoot
            $ConfigPath = Join-Path -Path $moduleRoot -ChildPath 'stepper-config.json'
            Write-Verbose "Using default module config: $ConfigPath"
        }
    }
    
    # Validate config file exists
    if (-not (Test-Path -Path $ConfigPath)) {
        throw "Configuration file not found: $ConfigPath"
    }
    
    Write-Verbose "Reading configuration from: $ConfigPath"
    
    # Load and parse JSON configuration
    try {
        $configContent = Get-Content -Path $ConfigPath -Raw -ErrorAction Stop
        $config = $configContent | ConvertFrom-Json -ErrorAction Stop
    } catch {
        throw "Failed to parse configuration file: $_"
    }
    
    # Validate configuration structure
    if (-not $config.StepperSteps) {
        throw "Invalid configuration: 'StepperSteps' property not found"
    }
    
    Write-Verbose "Found $($config.StepperSteps.Count) step(s) in configuration"
    
    # Build step definitions
    $steps = @()
    
    # Sort by order and filter to enabled only
    $enabledSteps = $config.StepperSteps | Where-Object { $_.enabled -eq $true } | Sort-Object -Property order
    
    Write-Verbose "Processing $($enabledSteps.Count) enabled step(s)"
    
    foreach ($stepConfig in $enabledSteps) {
        Write-Verbose "Loading step: $($stepConfig.name)"
        
        # Resolve script path
        # If path is absolute, use it as-is
        # If path is relative, resolve it relative to the config file's directory
        if ([System.IO.Path]::IsPathRooted($stepConfig.scriptPath)) {
            $scriptPath = $stepConfig.scriptPath
        } else {
            $configDirectory = Split-Path -Parent $ConfigPath
            $scriptPath = Join-Path -Path $configDirectory -ChildPath $stepConfig.scriptPath
        }
        
        # Validate script file exists
        if (-not (Test-Path -Path $scriptPath)) {
            Write-Warning "Step script not found: $scriptPath (skipping step '$($stepConfig.name)')"
            continue
        }
        
        Write-Verbose " Script path: $scriptPath"
        Write-Verbose " Accepts AllResults: $($stepConfig.acceptsAllResults)"
        
        # Load script content
        try {
            $scriptContent = Get-Content -Path $scriptPath -Raw -ErrorAction Stop
            
            # Create scriptblock
            $scriptBlock = [ScriptBlock]::Create($scriptContent)
            
            # Build step definition as PSCustomObject
            $stepDefinition = [PSCustomObject]@{
                Name = $stepConfig.name
                Description = $stepConfig.description
                ScriptBlock = $scriptBlock
                AcceptsAllResults = $stepConfig.acceptsAllResults
            }
            
            $steps += $stepDefinition
            
            Write-Verbose " ✓ Successfully loaded step: $($stepConfig.name)"
        } catch {
            Write-Warning "Failed to load step '$($stepConfig.name)': $_"
            continue
        }
    }
    
    Write-Verbose "Successfully loaded $($steps.Count) step(s)"
    
    if ($steps.Count -eq 0) {
        Write-Warning "No enabled steps were loaded from configuration"
    }
    
    return $steps
}

function New-StepperConfig {
    <#
    .SYNOPSIS
        Creates a new JSON configuration file for a Stepper.
     
    .DESCRIPTION
        Generates a JSON configuration file with step definitions.
        Creates the step script files by default.
     
    .PARAMETER Name
        Name of the configuration (without .json extension).
     
    .PARAMETER Path
        Directory where the JSON file will be created.
        Defaults to current directory.
     
    .PARAMETER StepNames
        Array of step names to include in the configuration.
     
    .PARAMETER SkipStepFiles
        If specified, does not create the step script files.
     
    .PARAMETER Force
        Overwrite existing files if they exist.
     
    .EXAMPLE
        New-StepperConfig -Name "My-Script" -StepNames "Step1", "Step2"
         
        Creates My-Script.json with two step definitions and creates the step files.
     
    .EXAMPLE
        New-StepperConfig -Name "Health-Check" -StepNames "DiskSpace", "Services" -SkipStepFiles
         
        Creates config only, without step files.
     
    .OUTPUTS
        System.IO.FileInfo - The created JSON file.
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param(
        [Parameter(Mandatory = $true, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
        
        [Parameter(Mandatory = $false)]
        [string]$Path = (Get-Location).Path,
        
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string[]]$StepNames,
        
        [Parameter(Mandatory = $false)]
        [switch]$SkipStepFiles,
        
        [Parameter(Mandatory = $false)]
        [switch]$Force
    )
    
    begin {
        Write-Verbose "Starting New-StepperConfig for '$Name'"
        
        # Ensure name doesn't have .json extension
        if ($Name -like '*.json') {
            $Name = [System.IO.Path]::GetFileNameWithoutExtension($Name)
        }
        
        # Validate path and offer to create if it doesn't exist
        if (-not (Test-Path $Path)) {
            $title = "Directory Does Not Exist"
            $message = "Path does not exist: $Path`nCreate directory?"
            $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Create the directory"
            $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Cancel operation"
            $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
            $choice = $host.UI.PromptForChoice($title, $message, $options, 0)
            
            if ($choice -eq 0) {
                Write-Verbose "Creating directory: $Path"
                New-Item -Path $Path -ItemType Directory -Force | Out-Null
            }
            else {
                throw "Path does not exist and was not created: $Path"
            }
        }
        
        $configPath = Join-Path -Path $Path -ChildPath "$Name.json"
        $stepsDir = Join-Path -Path $Path -ChildPath "Steps"
    }
    
    process {
        try {
            # Check if file exists
            if ((Test-Path $configPath) -and -not $Force) {
                throw "Config file already exists: $configPath. Use -Force to overwrite."
            }
            
            if ($PSCmdlet.ShouldProcess($configPath, "Create Stepper configuration")) {
                # Build step definitions
                $steps = @()
                $order = 1
                
                foreach ($stepName in $StepNames) {
                    $steps += @{
                        name = $stepName
                        description = "Description for $stepName"
                        scriptPath = "Steps/Step-$stepName.ps1"
                        enabled = $true
                        order = $order
                        acceptsAllResults = $false
                    }
                    $order++
                }
                
                # Create config object
                $config = @{
                    StepperSteps = $steps
                }
                
                # Convert to JSON and save
                $json = $config | ConvertTo-Json -Depth 10
                $json | Set-Content -Path $configPath -Encoding UTF8 -Force
                
                Write-Host "Created config: $configPath" -ForegroundColor Green
                
                # Create step files by default (unless -SkipStepFiles is specified)
                if (-not $SkipStepFiles) {
                    # Ensure Steps directory exists
                    if (-not (Test-Path $stepsDir)) {
                        Write-Verbose "Creating Steps directory: $stepsDir"
                        New-Item -Path $stepsDir -ItemType Directory -Force | Out-Null
                    }
                    
                    Write-Verbose "Creating step files..."
                    foreach ($stepName in $StepNames) {
                        if ($PSCmdlet.ShouldProcess("Step-$stepName.ps1", "Create step script")) {
                            try {
                                New-StepperStep -Name $stepName -Path $stepsDir -Force:$Force | Out-Null
                                Write-Host " Created step: Step-$stepName.ps1" -ForegroundColor Gray
                            }
                            catch {
                                Write-Warning "Failed to create step file for '$stepName': $_"
                            }
                        }
                    }
                }
                
                # Return the config file
                Get-Item $configPath
            }
        } catch {
            Write-Error "Failed to create Stepper config: $_"
            throw
        }
    }
    
    end {
        Write-Verbose "Completed New-StepperConfig"
    }
}

function New-StepperScript {
    <#
    .SYNOPSIS
        Creates a new Stepper script with configuration and step files.
     
    .DESCRIPTION
        Scaffolds a complete Stepper script structure including:
        - Main script file
        - JSON configuration file
        - Steps directory
        - Optional example step files
     
    .PARAMETER Name
        Name of the Stepper script (e.g., "My-HealthCheck").
        The .ps1 extension will be added automatically.
     
    .PARAMETER Path
        Directory where the Stepper script will be created.
        Defaults to current directory.
     
    .PARAMETER StepNames
        Array of step names to create as examples.
        If not specified, creates one example step.
     
    .PARAMETER Force
        Overwrite existing files if they exist.
     
    .EXAMPLE
        New-StepperScript -Name "My-HealthCheck"
         
        Creates a new Stepper script in the current directory with one example step.
     
    .EXAMPLE
        New-StepperScript -Name "Security-Assessment" -Path "C:\Scripts" -StepNames "Scan", "Analyze", "Report"
         
        Creates a security assessment Stepper with three steps.
     
    .OUTPUTS
        System.IO.FileInfo - The created script file.
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param(
        [Parameter(Mandatory = $true, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
        
        [Parameter(Mandatory = $false)]
        [string]$Path = (Get-Location).Path,
        
        [Parameter(Mandatory = $false)]
        [string[]]$StepNames = @('ExampleStep'),
        
        [Parameter(Mandatory = $false)]
        [switch]$Force
    )
    
    begin {
        Write-Verbose "Starting New-StepperScript for '$Name'"
        
        # Ensure name doesn't have .ps1 extension
        if ($Name -like '*.ps1') {
            $Name = [System.IO.Path]::GetFileNameWithoutExtension($Name)
        }
        
        # Validate path and offer to create if it doesn't exist
        if (-not (Test-Path $Path)) {
            $title = "Directory Does Not Exist"
            $message = "Path does not exist: $Path`nCreate directory?"
            $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Create the directory"
            $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Cancel operation"
            $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
            $choice = $host.UI.PromptForChoice($title, $message, $options, 0)
            
            if ($choice -eq 0) {
                Write-Verbose "Creating directory: $Path"
                New-Item -Path $Path -ItemType Directory -Force | Out-Null
            }
            else {
                throw "Path does not exist and was not created: $Path"
            }
        }
        
        $scriptPath = Join-Path -Path $Path -ChildPath "$Name.ps1"
        $configPath = Join-Path -Path $Path -ChildPath "$Name.json"
        $stepsDir = Join-Path -Path $Path -ChildPath "Steps"
    }
    
    process {
        try {
            # Check if files exist
            if ((Test-Path $scriptPath) -and -not $Force) {
                throw "Script file already exists: $scriptPath. Use -Force to overwrite."
            }
            
            if ((Test-Path $configPath) -and -not $Force) {
                throw "Config file already exists: $configPath. Use -Force to overwrite."
            }
            
            if ($PSCmdlet.ShouldProcess($scriptPath, "Create Stepper script")) {
                # Create Steps directory
                if (-not (Test-Path $stepsDir)) {
                    New-Item -Path $stepsDir -ItemType Directory -Force | Out-Null
                    Write-Verbose "Created Steps directory: $stepsDir"
                }
                
                # Create main script
                $scriptContent = @"
<#
.SYNOPSIS
    $Name Stepper script.
 
.DESCRIPTION
    Multi-step Stepper script using the Stepper module.
    State is automatically saved and can be resumed if interrupted.
 
.PARAMETER Fresh
    Start fresh, ignoring any saved state.
 
.PARAMETER ShowStatus
    Display current progress without running steps.
 
.PARAMETER Reset
    Reset all saved progress.
 
.EXAMPLE
    .\$Name.ps1
     
    Run the Stepper (resumes automatically if interrupted).
 
.EXAMPLE
    .\$Name.ps1 -Fresh
     
    Start fresh, ignoring any saved state.
 
.NOTES
    Requires: Stepper module
    Configuration: $Name.json
#>
[CmdletBinding()]
param(
    [Parameter(Mandatory = `$false)]
    [switch]`$Fresh,
     
    [Parameter(Mandatory = `$false)]
    [switch]`$ShowStatus,
     
    [Parameter(Mandatory = `$false)]
    [switch]`$Reset
)
 
# Import Stepper module
if (-not (Get-Module -Name Stepper -ListAvailable)) {
    Write-Error "Stepper module is not installed. Install it with: Install-Module Stepper"
    return
}
 
Import-Module Stepper -Force
 
# Handle ShowStatus
if (`$ShowStatus) {
    Show-StepperStatus
    return
}
 
# Handle Reset
if (`$Reset) {
    Reset-StepperState
    return
}
 
# Note: Get-StepperSteps will automatically find $Name.json
# in the same directory as this script
 
# Run the Stepper using Stepper module
try {
    if (`$Fresh) {
        Start-Stepper -Fresh
    } else {
        Start-Stepper
    }
     
    # Start-Stepper displays its own completion message when fully complete
    # No need to display additional message here
} catch {
    Write-Error "Stepper failed: `$_"
    return
}
"@

                
                $scriptContent | Set-Content -Path $scriptPath -Encoding UTF8 -Force
                Write-Host "Created script: $scriptPath" -ForegroundColor Green
                
                # Create JSON config using New-StepperConfig
                New-StepperConfig -Name $Name -Path $Path -StepNames $StepNames -Force:$Force | Out-Null
                
                # Return the script file
                Get-Item $scriptPath
            }
        } catch {
            Write-Error "Failed to create Stepper script: $_"
            throw
        }
    }
    
    end {
        Write-Verbose "Completed New-StepperScript"
    }
}

function New-StepperStep {
    <#
    .SYNOPSIS
        Creates a new step script file from a template.
     
    .DESCRIPTION
        Generates a new step script file with proper comment-based help
        and basic structure. The file is ready to be customized with your
        specific step logic.
     
    .PARAMETER Name
        Name of the step (e.g., "DiskSpace").
        The "Step-" prefix will be added automatically if not present.
     
    .PARAMETER Path
        Directory where the step file will be created.
        Defaults to "Steps" subdirectory in current location.
     
    .PARAMETER AcceptsAllResults
        If specified, adds the $AllResults parameter to the step script.
     
    .PARAMETER Force
        Overwrite existing file if it exists.
     
    .EXAMPLE
        New-StepperStep -Name "DiskSpace"
         
        Creates Steps/Step-DiskSpace.ps1 with basic template.
     
    .EXAMPLE
        New-StepperStep -Name "GenerateReport" -AcceptsAllResults
         
        Creates a step that accepts results from previous steps.
     
    .EXAMPLE
        New-StepperStep -Name "CustomCheck" -Path "C:\MySteps"
         
        Creates step file in custom directory.
     
    .OUTPUTS
        System.IO.FileInfo - The created step file.
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param(
        [Parameter(Mandatory = $true, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
        
        [Parameter(Mandatory = $false)]
        [string]$Path,
        
        [Parameter(Mandatory = $false)]
        [switch]$AcceptsAllResults,
        
        [Parameter(Mandatory = $false)]
        [switch]$Force
    )
    
    begin {
        Write-Verbose "Starting New-StepperStep for '$Name'"
        
        # Ensure name has Step- prefix
        if ($Name -notlike 'Step-*') {
            $Name = "Step-$Name"
        }
        
        # Default path to Steps subdirectory
        if (-not $Path) {
            $Path = Join-Path -Path (Get-Location).Path -ChildPath "Steps"
        }
        
        # Create directory if it doesn't exist
        if (-not (Test-Path $Path)) {
            New-Item -Path $Path -ItemType Directory -Force | Out-Null
            Write-Verbose "Created directory: $Path"
        }
        
        $stepPath = Join-Path -Path $Path -ChildPath "$Name.ps1"
    }
    
    process {
        try {
            # Check if file exists
            if ((Test-Path $stepPath) -and -not $Force) {
                throw "Step file already exists: $stepPath. Use -Force to overwrite."
            }
            
            if ($PSCmdlet.ShouldProcess($stepPath, "Create step file")) {
                # Build step content based on parameters
                $paramBlock = if ($AcceptsAllResults) {
                    @"
param(
    [Parameter(Mandatory = `$false)]
    [hashtable]`$AllResults
)
 
"@

                } else {
                    @"
param()
 
"@

                }
                
                $allResultsExample = if ($AcceptsAllResults) {
                    @"
 
    # Access results from previous steps
    # `$previousStep = `$AllResults['PreviousStepName']
    # `$data = `$previousStep.SomeProperty
"@

                } else {
                    ""
                }
                
                $stepContent = @"
# $Name
<#
.SYNOPSIS
    Brief description of what this step does.
 
.DESCRIPTION
    Detailed description of the step's purpose and what it checks or performs.
$(if ($AcceptsAllResults) {
"
.PARAMETER AllResults
    Hashtable containing results from all previously completed steps.
"})
 
.OUTPUTS
    Returns a hashtable with the step's results.
    These results can be accessed by subsequent steps if they have acceptsAllResults: true.
 
.NOTES
    This step is part of a Stepper workflow.
    It will be executed in the order specified in the JSON configuration.
#>
 
$paramBlock
Write-Host "`n--- $($Name.Replace('Step-', '')) ---" -ForegroundColor Yellow
 
try {$allResultsExample
    # Your step logic here
    Write-Host " Running $($Name.Replace('Step-', ''))..." -ForegroundColor Gray
     
    # Example: Perform your checks, gather data, etc.
    `$result = @{
        Status = "Success"
        Details = "Step completed successfully"
        # Add your custom properties here
    }
     
    Write-Host " ✓ Step completed" -ForegroundColor Green
     
    # Return results for potential use by other steps
    return `$result
} catch {
    Write-Error "Failed to execute $Name`: `$_"
    return `$false
}
"@

                
                $stepContent | Set-Content -Path $stepPath -Encoding UTF8 -Force
                Write-Host "Created step: $stepPath" -ForegroundColor Green
                
                # Return the step file
                Get-Item $stepPath
            }
        } catch {
            Write-Error "Failed to create step file: $_"
            throw
        }
    }
    
    end {
        Write-Verbose "Completed New-StepperStep"
    }
}

function Reset-StepperState {
    <#
    .SYNOPSIS
        Clears all saved Stepper state.
     
    .DESCRIPTION
        Removes the saved state file, effectively resetting the Stepper
        to allow starting fresh. Prompts for confirmation before removing.
         
        Supports -WhatIf and -Confirm parameters.
     
    .EXAMPLE
        Reset-StepperState
         
        Prompts for confirmation then clears the state.
     
    .EXAMPLE
        Reset-StepperState -Confirm:$false
         
        Clears the state without prompting.
     
    .EXAMPLE
        Reset-StepperState -WhatIf
         
        Shows what would happen without actually clearing state.
     
    .NOTES
        This action cannot be undone. All Stepper progress will be lost.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    param()
    
    Show-StepperHeader
    
    $statePath = Get-StateFilePath
    
    if (Test-Path $statePath) {
        if ($PSCmdlet.ShouldProcess($statePath, "Remove Stepper state file and reset all progress")) {
            Clear-StepperState -Confirm:$false
            Write-Host "`nStepper state has been reset." -ForegroundColor Green
            Write-Host "Run 'Start-Stepper' to begin a new Stepper." -ForegroundColor Cyan
        }
    } else {
        Write-Host "No saved Stepper state found." -ForegroundColor Yellow
        Write-Host "Nothing to reset." -ForegroundColor Gray
    }
}

function Show-StepperStatus {
    <#
    .SYNOPSIS
        Displays the current Stepper progress without running any steps.
     
    .DESCRIPTION
        Shows the progress summary of the current or last Stepper including
        completed steps, current status, and overall progress percentage.
         
        Useful for checking on Stepper status without starting or resuming.
     
    .EXAMPLE
        Show-StepperStatus
         
        Displays current Stepper progress.
     
    .NOTES
        If no Stepper state exists, indicates that no Stepper has been started.
    #>

    [CmdletBinding()]
    param()
    
    Show-StepperHeader
    
    $state = Get-StepperState
    $steps = Get-StepperSteps
    
    if ($state.CompletedSteps.Count -eq 0 -and $state.Status -eq 'InProgress' -and 
        [DateTime]::Parse($state.StartedAt) -gt (Get-Date).AddMinutes(-1)) {
        # This is a brand new state that was just created
        Write-Host "No Stepper progress found." -ForegroundColor Yellow
        Write-Host "Run 'Start-Stepper' to begin a new Stepper." -ForegroundColor Gray
    } else {
        Show-StepperProgress -State $state -TotalSteps $steps.Count
        
        if ($state.Status -eq 'InProgress') {
            Write-Host "Run 'Start-Stepper -Resume' to continue the Stepper." -ForegroundColor Cyan
        } elseif ($state.Status -eq 'Failed') {
            Write-Host "Run 'Start-Stepper -Resume' to retry from the failed step." -ForegroundColor Yellow
        } else {
            Write-Host "Stepper is complete. Run 'Reset-StepperState' to start fresh." -ForegroundColor Green
        }
    }
}

function Start-Stepper {
    <#
    .SYNOPSIS
        Main function to run the multi-step Stepper.
     
    .DESCRIPTION
        Orchestrates the execution of all Stepper steps, manages state
        persistence, handles errors, and provides progress feedback.
         
        By default, resumes from the last saved state. Use -Fresh to start over.
     
    .PARAMETER Fresh
        Start a completely new Stepper, ignoring any saved state.
     
    .PARAMETER ConfigPath
        Path to the Stepper configuration JSON file. If not specified, attempts to find
        a JSON file with the same base name as the calling script (e.g., MyScript.ps1 -> MyScript.json).
        Falls back to stepper-config.json in the module root if no match is found.
     
    .PARAMETER InitialData
        Optional data to pass to all steps as AllData[0]. This allows passing context
        or parameters from the main script to all step scripts.
     
    .EXAMPLE
        Start-Stepper
         
        Automatically finds a JSON config with the same name as the calling script.
        Resumes from the last checkpoint (default behavior).
     
    .EXAMPLE
        Start-Stepper -Fresh
         
        Starts a completely new Stepper from the beginning.
     
    .EXAMPLE
        Start-Stepper -ConfigPath ".\my-steps.json"
         
        Uses a custom configuration file.
     
    .NOTES
        State is automatically saved after each step.
        The Stepper can be safely interrupted and resumed later.
        Completed steps can be skipped or re-run interactively.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false)]
        [switch]$Fresh,
        
        [Parameter(Mandatory = $false)]
        [string]$ConfigPath,
        
        [Parameter(Mandatory = $false)]
        [object]$InitialData
    )
    
    Show-StepperHeader
    
    # Get all Stepper steps
    if ($ConfigPath) {
        $steps = Get-StepperSteps -ConfigPath $ConfigPath
    } else {
        $steps = Get-StepperSteps
    }
    $totalSteps = $steps.Count
    
    Write-Verbose "Total steps defined: $totalSteps"
    
    # Load or create state (default is to resume)
    if ($Fresh) {
        # User explicitly wants to start fresh
        Write-Host "Starting fresh Stepper (ignoring saved state)..." -ForegroundColor Cyan
        $state = New-StepperState
    } else {
        # Default behavior: try to resume
        $existingState = Get-StepperState
        
        if ($existingState.Status -eq 'Completed') {
            # Previous session completed - automatically start new one
            Write-Host "Starting new Stepper session..." -ForegroundColor Cyan
            $state = New-StepperState
        } elseif (-not (Test-StepperStateValidity -State $existingState)) {
            Write-Host "Saved state is invalid or too old." -ForegroundColor Yellow
            
            $title = "Invalid State"
            $message = "Do you want to start fresh?"
            $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Clear state and start over"
            $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Use existing state anyway"
            $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
            $choice = $host.UI.PromptForChoice($title, $message, $options, 0)
            
            if ($choice -eq 0) {
                Clear-StepperState -Confirm:$false
                $state = New-StepperState
            } else {
                Write-Host "Using existing state anyway..." -ForegroundColor Yellow
                $state = $existingState
            }
        } else {
            # Valid in-progress state found
            Write-Host "Found existing Stepper session in progress." -ForegroundColor Yellow
            
            $title = "Resume or Start Fresh"
            $message = "Do you want to resume from where you left off?"
            $resumeChoice = New-Object System.Management.Automation.Host.ChoiceDescription "&Resume", "Continue from the last completed step"
            $freshChoice = New-Object System.Management.Automation.Host.ChoiceDescription "&Fresh", "Start over from the beginning"
            $cancelChoice = New-Object System.Management.Automation.Host.ChoiceDescription "&Cancel", "Exit without running"
            $options = [System.Management.Automation.Host.ChoiceDescription[]]($resumeChoice, $freshChoice, $cancelChoice)
            $choice = $host.UI.PromptForChoice($title, $message, $options, 0)
            
            if ($choice -eq 0) {
                Write-Host "Resuming from saved state..." -ForegroundColor Cyan
                $state = $existingState
            } elseif ($choice -eq 1) {
                Write-Host "Starting fresh..." -ForegroundColor Cyan
                Clear-StepperState -Confirm:$false
                $state = New-StepperState
            } else {
                Write-Host "Exiting..." -ForegroundColor Gray
                return
            }
        }
    }
    
    # Show current progress
    Show-StepperProgress -State $state -TotalSteps $totalSteps
    
    # Determine starting point
    $startIndex = $state.CurrentStepIndex
    
    if ($startIndex -ge $totalSteps) {
        $startIndex = 0
    }
    
    Write-Verbose "Starting from step index: $startIndex"
    
    # Execute steps
    for ($i = $startIndex; $i -lt $totalSteps; $i++) {
        $step = $steps[$i]
        
        Show-StepperStepHeader -StepName $step.Name -StepNumber ($i + 1) -TotalSteps $totalSteps
        Write-Host " $($step.Description)" -ForegroundColor Gray
        Write-Host ""
        
        # Check if already completed
        if ($state.CompletedSteps -contains $step.Name) {
            Write-Host " This step was already completed." -ForegroundColor Yellow
            
            $title = "Step Already Completed"
            $message = "Do you want to skip this step?"
            $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Skip this step and continue"
            $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Re-run this step"
            $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
            $choice = $host.UI.PromptForChoice($title, $message, $options, 0)
            
            if ($choice -eq 0) {
                Write-Host " Skipping..." -ForegroundColor Gray
                continue
            } else {
                Write-Host " Re-running..." -ForegroundColor Gray
            }
        }
        
        # Prepare all results for steps that need them
        # Array index matches step number: AllData[1] = Step 1 result, etc.
        # AllData[0] is reserved for initial input data (future feature)
        $allData = @($InitialData)
        for ($j = 0; $j -lt $i; $j++) {
            $previousStep = $steps[$j]
            if ($state.CompletedSteps -contains $previousStep.Name -and $state.StepResults.ContainsKey($previousStep.Name)) {
                $allData += $state.StepResults[$previousStep.Name].Result
            } else {
                $allData += $null
            }
        }
        
        # Execute the step
        $success = Invoke-StepperStep -Step $step -State $state -AllData $allData
        
        # Update current step index
        $state.CurrentStepIndex = $i + 1
        
        # Save state after each step
        Save-StepperState -State $state
        
        if (-not $success) {
            $state.Status = 'Failed'
            Save-StepperState -State $state
            
            Write-Host "`nStepper stopped due to error." -ForegroundColor Red
            Write-Host "State saved. You can resume later with the -Resume switch." -ForegroundColor Yellow
            Write-Host "State file location: $(Get-StateFilePath)" -ForegroundColor Gray
            return
        }
        
        # Prompt to continue after each step (except last)
        if ($i -lt ($totalSteps - 1)) {
            Write-Host ""
            $title = "Continue to Next Step"
            $message = "Ready to continue?"
            $continue = New-Object System.Management.Automation.Host.ChoiceDescription "&Continue", "Continue to the next step"
            $stop = New-Object System.Management.Automation.Host.ChoiceDescription "&Stop", "Stop and save progress (Ctrl+C also works)"
            $options = [System.Management.Automation.Host.ChoiceDescription[]]($continue, $stop)
            $choice = $host.UI.PromptForChoice($title, $message, $options, 0)
            
            if ($choice -eq 1) {
                Write-Host "`nStopping... Progress has been saved." -ForegroundColor Yellow
                return
            }
        }
    }
    
    # Mark as completed
    $state.Status = 'Completed'
    $state.CompletedAt = Get-Date -Format 'o'
    Save-StepperState -State $state
    
    # Show final summary
    Write-Host ""
    Write-Host ("=" * 70) -ForegroundColor Green
    Write-Host " Stepper Complete!" -ForegroundColor Green
    Write-Host ("=" * 70) -ForegroundColor Green
    
    Show-StepperProgress -State $state -TotalSteps $totalSteps
    
    $totalDuration = ([DateTime]::Parse($state.CompletedAt)) - ([DateTime]::Parse($state.StartedAt))
    Write-Host "Total Duration: $([math]::Round($totalDuration.TotalMinutes, 2)) minutes`n" -ForegroundColor Gray
}



# Export functions and aliases as required
Export-ModuleMember -Function @('Get-StepperSteps','New-StepperConfig','New-StepperScript','New-StepperStep','Reset-StepperState','Show-StepperStatus','Start-Stepper') -Alias @()