Public/New-PstFunction.ps1

function New-PstFunction {
    <#
.SYNOPSIS
    Creates a new PowerShell function from a template or using intelligent code generation.
.DESCRIPTION
    This function takes an approved verb and noun, and creates a new PowerShell function based on the
    specified complexity level. It supports two modes:

    1. Template Mode (default): Copies a function template and renames it
    2. Generator Mode (-UseGenerator): Uses the intelligent code generation system to create
       customized functions with proper structure based on complexity level

    Complexity Levels:
    - Basic: Simple function with minimal structure (no CmdletBinding, simple process block)
    - Standard: Function with CmdletBinding, begin/process/end blocks, pipeline support
    - Advanced: Full cmdlet features including ShouldProcess, parameter sets, comprehensive error handling

    Legacy ComplexityLevel values (Basic, Moderate, Complex) are automatically mapped to the new system.
.PARAMETER Verb
    (Required) The approved verb for the function name.
.PARAMETER Noun
    (Required) The noun for the function name.
.PARAMETER Complexity
    (Optional) The complexity level for code generation. Valid values: Basic, Standard, Advanced.
    Defaults to Standard. Only used with -UseGenerator switch.
.PARAMETER ComplexityLevel
    (Optional) Legacy parameter for template mode. Valid values: Basic, Moderate, Complex.
    Defaults to Complex for backward compatibility. Ignored when -UseGenerator is specified.
.PARAMETER Parameters
    (Optional) Array of parameter definitions for the generated function. Each can be:
    - A string (parameter name, defaults to [string] type)
    - A hashtable with Name, Type, Mandatory, Pipeline, DefaultValue, etc.
    Only used with -UseGenerator switch.
.PARAMETER Synopsis
    (Optional) Brief description for the function's help. Only used with -UseGenerator.
.PARAMETER UseGenerator
    (Optional) Switch to use the intelligent code generation system instead of templates.
    When specified, uses New-PstGeneratedCode to create the function.
.PARAMETER Force
    (Optional) Forces creation even if file exists, useful in automation scenarios.
.PARAMETER IncludeProjectLayout
    (Optional) Creates a complete project structure including src/, tests/, docs/ directories.
.EXAMPLE
    New-PstFunction -Verb Get -Noun Item
    Creates a new function using the Complex template (legacy mode).
.EXAMPLE
    New-PstFunction -Verb Get -Noun UserData -UseGenerator -Complexity Standard
    Creates a Standard complexity function using the intelligent code generator.
.EXAMPLE
    New-PstFunction -Verb Set -Noun Configuration -UseGenerator -Complexity Advanced -Parameters @('ConfigPath', @{Name='Force';Type='switch'})
    Creates an Advanced function with custom parameters using the code generator.
.EXAMPLE
    New-PstFunction -Verb Get -Noun UserData -UseGenerator -IncludeProjectLayout
    Creates a complete project structure with generated function code.
.NOTES
    Template mode requires template files in Resources/Samples directory.
    Generator mode uses the new complexity schema system for dynamic code creation.
.LINK
    https://docs.microsoft.com/en-us/powershell/scripting/developer/cmdlet/approved-verbs-for-windows-powershell-commands
#>

    [CmdletBinding(SupportsShouldProcess)]
    param (
        [Parameter(Mandatory = $true, Position = 0,
            HelpMessage = "The approved verb for the function name.")]
        [ValidateSet("Add", "Approve", "Assert", "Backup", "Block", "Checkpoint", "Clear", "Close", "Compare", "Complete", "Compress", "Confirm", "Connect", "Convert", "ConvertFrom", "ConvertTo", "Copy", "Debug", "Deny", "Disable", "Disconnect", "Dismount", "Edit", "Enable", "Enter", "Exit", "Expand", "Export", "Find", "Format", "Get", "Grant", "Group", "Hide", "Import", "Initialize", "Install", "Invoke", "Join", "Limit", "Lock", "Measure", "Merge", "Mount", "Move", "New", "Open", "Optimize", "Out", "Ping", "Pop", "Protect", "Publish", "Push", "Read", "Receive", "Redo", "Register", "Remove", "Rename", "Repair", "Request", "Reset", "Resize", "Resolve", "Restart", "Restore", "Resume", "Revoke", "Save", "Search", "Select", "Send", "Set", "Show", "Skip", "Split", "Start", "Step", "Stop", "Submit", "Suspend", "Switch", "Sync", "Test", "Trace", "Unblock", "Undo", "Uninstall", "Unlock", "Unprotect", "Unpublish", "Unregister", "Update", "Use", "Wait", "Watch", "Write")]
        [string]$Verb,

        [Parameter(Mandatory = $true, Position = 1,
            HelpMessage = "The noun for the function name.")]
        [ValidatePattern('^[A-Za-z][A-Za-z0-9]*$')]
        [string]$Noun,

        [Parameter(Mandatory = $false,
            HelpMessage = "Complexity level for code generation (Basic, Standard, Advanced).")]
        [ValidateSet("Basic", "Standard", "Advanced")]
        [string]$Complexity = "Standard",

        [Parameter(Mandatory = $false,
            HelpMessage = "Legacy complexity level for template mode.")]
        [ValidateSet("Basic", "Moderate", "Complex")]
        [string]$ComplexityLevel = "Complex",

        [Parameter(Mandatory = $false,
            HelpMessage = "Parameter definitions for the generated function.")]
        [object[]]$Parameters = @(),

        [Parameter(Mandatory = $false,
            HelpMessage = "Brief description for the function's help.")]
        [string]$Synopsis,

        [Parameter(Mandatory = $false,
            HelpMessage = "Use the intelligent code generation system instead of templates.")]
        [switch]$UseGenerator,

        [Parameter(Mandatory = $false)]
        [switch]$Force,

        [Parameter(Mandatory = $false)]
        [switch]$IncludeProjectLayout
    )
    begin {
        Write-Debug -Message "Begin '$($MyInvocation.MyCommand.Name)' at '$(Get-Date)'"

        $newFunctionName = "$Verb-$Noun"

        # Determine file path based on project layout option
        if ($IncludeProjectLayout) {
            $projectRoot = $newFunctionName
            $newFilePath = Join-Path $projectRoot "src" "$newFunctionName.ps1"
        } else {
            $newFilePath = "$newFunctionName.ps1"
        }

        if ($UseGenerator) {
            # Using new code generation system
            Write-Verbose "Using intelligent code generation with complexity: $Complexity"
        } else {
            # Using legacy template mode
            # Select template based on complexity level
            $templateFileName = switch ($ComplexityLevel) {
                "Basic"     { "Function-Basic.ps1" }
                "Moderate"  { "Function-Moderate.ps1" }
                "Complex"   { "Function-Complex.ps1" }
                default     { "Function-Complex.ps1" }  # Fallback to Complex
            }

            $templatePath = $Samples[$templateFileName]

            Write-Verbose "Using template mode with complexity level: $ComplexityLevel"
            Write-Verbose "Selected template: $templateFileName"

            if (-not $templatePath) {
                throw "Template '$templateFileName' not found in Samples hashtable. Available templates: $($Samples.Keys -join ', ')"
            }
            if (-not (Test-Path $templatePath)) {
                throw "Template file not found: $templatePath"
            } else {
                Write-Verbose "Template file found: $templatePath"
            }
        }
    }
    process {
        try {
            # Check if we're in automation mode - inline check to avoid scope issues
            $isAutomationMode = $Force -or
            ($Global:PstAutomationMode -eq $true) -or
            ($env:CI -eq 'true') -or
            ($env:GITHUB_ACTIONS -eq 'true') -or
            ($Host.Name -eq 'ServerRemoteHost') -or
            (-not [Environment]::UserInteractive) -or
            ($global:ConfirmPreference -eq 'None')

            # Create project structure if IncludeProjectLayout is specified
            if ($IncludeProjectLayout) {
                $projectRoot = $newFunctionName

                # Create directory structure
                $directories = @(
                    $projectRoot,
                    (Join-Path $projectRoot "src"),
                    (Join-Path $projectRoot "tests"),
                    (Join-Path $projectRoot "tests" "Unit"),
                    (Join-Path $projectRoot "tests" "Integration"),
                    (Join-Path $projectRoot "docs")
                )

                foreach ($dir in $directories) {
                    if (-not (Test-Path $dir)) {
                        if ($isAutomationMode -or $PSCmdlet.ShouldProcess($dir, "Create directory")) {
                            New-Item -Path $dir -ItemType Directory -Force | Out-Null
                            Write-Verbose "Created directory: $dir"
                        }
                    }
                }

                # Create README.md in root
                $readmeContent = @"
# $newFunctionName

PowerShell function for [brief description].

## Installation

``````powershell
Import-Module .\src\$newFunctionName.ps1
``````

## Usage

``````powershell
$newFunctionName -Parameter Value
``````

## Testing

``````powershell
Invoke-Pester -Path .\tests\
``````

## License

[Specify license]
"@

                $readmePath = Join-Path $projectRoot "README.md"
                if ($isAutomationMode -or $PSCmdlet.ShouldProcess($readmePath, "Create README.md")) {
                    $readmeContent | Set-Content -Path $readmePath
                    Write-Verbose "Created: $readmePath"
                }

                # Create .gitignore
                $gitignoreContent = @"
# Build artifacts
build/
*.nupkg

# Test results
TestResults*.xml
coverage*.xml

# Temporary files
temp/
*.tmp
*.log
"@

                $gitignorePath = Join-Path $projectRoot ".gitignore"
                if ($isAutomationMode -or $PSCmdlet.ShouldProcess($gitignorePath, "Create .gitignore")) {
                    $gitignoreContent | Set-Content -Path $gitignorePath
                    Write-Verbose "Created: $gitignorePath"
                }

                # Create docs README.md
                $docsReadmeContent = @"
# $newFunctionName Documentation

## Overview

[Function overview and purpose]

## Parameters

[Parameter documentation]

## Examples

[Usage examples]

## Notes

[Additional notes]
"@

                $docsReadmePath = Join-Path $projectRoot "docs" "README.md"
                if ($isAutomationMode -or $PSCmdlet.ShouldProcess($docsReadmePath, "Create docs README.md")) {
                    $docsReadmeContent | Set-Content -Path $docsReadmePath
                    Write-Verbose "Created: $docsReadmePath"
                }

                # Create test file
                $testContent = @"
BeforeAll {
    Import-Module "`$PSScriptRoot\..\..\src\$newFunctionName.ps1" -Force
}

Describe "$newFunctionName" {
    Context "When valid parameters are provided" {
        It "Should execute without errors" {
            # Arrange

            # Act

            # Assert
            `$true | Should -Be `$true
        }
    }

    Context "When invalid parameters are provided" {
        It "Should throw an error" {
            # Arrange & Act & Assert
            { $newFunctionName -Parameter `$null } | Should -Throw
        }
    }
}
"@

                $testFilePath = Join-Path $projectRoot "tests" "Unit" "$newFunctionName.Tests.ps1"
                if ($isAutomationMode -or $PSCmdlet.ShouldProcess($testFilePath, "Create test file")) {
                    $testContent | Set-Content -Path $testFilePath
                    Write-Verbose "Created: $testFilePath"
                }
            }

            # Check if file already exists
            if ((Test-Path $newFilePath) -and -not $isAutomationMode) {
                if (-not $PSCmdlet.ShouldProcess($newFilePath, "Overwrite existing function file")) {
                    Write-Warning "Operation cancelled by user."
                    return
                }
            }
            elseif ((Test-Path $newFilePath) -and $isAutomationMode -and -not $Force) {
                Write-Warning "File '$newFilePath' already exists. Use -Force to overwrite in automation mode."
                return
            }

            # Proceed with file creation - skip ShouldProcess in automation mode
            if ($isAutomationMode -or $PSCmdlet.ShouldProcess($newFilePath, "Create new function file")) {
                # Create parent directory if it doesn't exist (for project layout)
                $parentDir = Split-Path -Path $newFilePath -Parent
                if ($parentDir -and -not (Test-Path $parentDir)) {
                    New-Item -Path $parentDir -ItemType Directory -Force | Out-Null
                }

                if ($UseGenerator) {
                    # Use intelligent code generation
                    Write-Verbose "Generating function code with complexity: $Complexity"

                    $generatorParams = @{
                        FunctionName = $newFunctionName
                        Type = 'Function'
                        Complexity = $Complexity
                    }

                    if ($Parameters.Count -gt 0) {
                        $generatorParams.Parameters = $Parameters
                    }

                    if ($Synopsis) {
                        $generatorParams.Synopsis = $Synopsis
                    }

                    $newContent = New-PstGeneratedCode @generatorParams
                } else {
                    # Use template mode
                    Write-Verbose "Getting file content from '$($templatePath)'"
                    $templateContent = (Get-Content -Path $templatePath) -join "`n"
                    Write-Verbose "Replacing 'Function Verb-Noun' with 'Function $($newFunctionName)'"
                    $newContent = $templateContent -replace "Function Verb-Noun", "Function $newFunctionName"
                }

                Write-Verbose "Set-Content to path '$($newFilePath)'"
                $newContent | Set-Content -Path $newFilePath

                if ($IncludeProjectLayout) {
                    Write-Output "New function project created: $projectRoot"
                    Write-Output " - Function file: $newFilePath"
                    Write-Output " - Test file: tests\Unit\$newFunctionName.Tests.ps1"
                    Write-Output " - Documentation: docs\README.md"
                    if ($UseGenerator) {
                        Write-Output " - Generated with: $Complexity complexity"
                    }
                } else {
                    if ($UseGenerator) {
                        Write-Output "New function file created: $newFilePath (Generated: $Complexity complexity)"
                    } else {
                        Write-Output "New function file created: $newFilePath"
                    }
                }
            }
            else {
                Write-Verbose "Operation cancelled or skipped."
            }
        }
        catch {
            if ($_.Exception -and $_.Exception.Message) {
                Write-Error "An error occurred: $($_.Exception.Message)"
            } else {
                Write-Error "An error occurred, but no additional information is available."
            }
        }
    }
    end {
        if ($?) {
            Write-Debug -Message "End '$($MyInvocation.MyCommand.Name)' at '$(Get-Date)'"
        }
    }
}