pwsh_shortcut_alias.psm1

$ModuleRoot = $PSScriptRoot
$YamlCfgPath = Join-Path $ModuleRoot 'shortcout_aliases.yaml'

# Load private implementation helpers
. "$ModuleRoot\Private\alias_yaml.ps1"

# Module-scope launch entry point. Tests can temporarily replace this script block.
$script:ShortcutAliasLaunchInvoker = {
    param (
        [Parameter(Mandatory)]
        [string]$Target
    )

    Invoke-ShortcutAliasLaunch -Target $Target
}

# Private helper: check whether a key exists in either OrderedDictionary or Hashtable
function Test-AliasKeyExists {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [object]$Dictionary,

        [Parameter(Mandatory)]
        [string]$Key
    )
    process {
        if ($Dictionary -is [System.Collections.Specialized.OrderedDictionary]) {
            return $Dictionary.Contains($Key)
        }
        elseif ($Dictionary -is [hashtable]) {
            return $Dictionary.ContainsKey($Key)
        }
        return $false
    }
}

# Private helper: resolve alias targets and centralize URL and local path normalization
function Resolve-ShortcutAliasTarget {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [string]$ShortcutPath,

        [Parameter()]
        [string]$AliasName = "",

        [Parameter()]
        [switch]$AllowUnresolvedLocalPath
    )

    $uri = $null
    if ([System.Uri]::TryCreate($ShortcutPath, [System.UriKind]::Absolute, [ref]$uri) -and $uri.Scheme -in 'http', 'https') {
        return [PSCustomObject]@{
            Name  = $AliasName
            Path  = $ShortcutPath
            IsUrl = $true
        }
    }

    if ($AllowUnresolvedLocalPath) {
        return [PSCustomObject]@{
            Name  = $AliasName
            Path  = $ShortcutPath
            IsUrl = $false
        }
    }

    $resolvedPath = Resolve-Path $ShortcutPath -ErrorAction Stop
    return [PSCustomObject]@{
        Name  = $AliasName
        Path  = $resolvedPath.Path
        IsUrl = $false
    }
}

# Private helper: convert YAML alias data into the shared runtime entry shape
function Get-ShortcutAliasRegistryEntries {
    [CmdletBinding()]
    param ()

    $aliases = Read-AliasYaml -Path $YamlCfgPath
    $entries = foreach ($name in $aliases.Keys) {
        Resolve-ShortcutAliasTarget -AliasName $name -ShortcutPath $aliases[$name] -AllowUnresolvedLocalPath
    }

    return @($entries)
}

# Private helper: isolate the actual alias launch behavior from registration logic
function Invoke-ShortcutAliasLaunch {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [string]$Target
    )

    Start-Process explorer.exe $Target
}

# Private helper: build search results separately from any console presentation concerns
function Get-ShortcutAliasSearchResults {
    [CmdletBinding()]
    param (
        [Parameter()]
        [string]$AliasName = "*"
    )

    $filter = "*$AliasName*"
    $entries = Get-ShortcutAliasRegistryEntries | Where-Object { $_.Name -like $filter }

    return @($entries)
}

function Use-ShortcutAlias {
    [CmdletBinding(DefaultParameterSetName = "Default")]
    [Alias("usa")] # Short alias for interactive use
    param (
        [Parameter(Position = 0, Mandatory)]
        [ValidateSet("add", "remove", "search", "update")]
        [string]$Action,

        [Parameter(Position = 1)]
        [ValidatePattern('^[A-Za-z0-9][A-Za-z0-9_-]*$')]
        [string]$AliasName,

        [Parameter(Position = 2)]
        [ValidateScript({
            try {
                Resolve-ShortcutAliasTarget -ShortcutPath $_ | Out-Null
                return $true
            }
            catch {
                return $false
            }
        })] # Validate the target early
        [string]$ShortcutPath
    )

    switch ($Action) {
        "add" {
            Add-ShortcutAlias -AliasName $AliasName -ShortcutPath $ShortcutPath
        }
        "remove" {
            Remove-ShortcutAlias -AliasName $AliasName
        }
        "search" {
             Search-ShortcutAlias -AliasName $AliasName
        }
        "update" {
            Update-ShortcutAlias
        }
    }
}

function Add-ShortcutAlias {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [ValidatePattern('^[A-Za-z0-9][A-Za-z0-9_-]*$')]
        [string]$AliasName,

        [Parameter(Mandatory)]
        [string]$ShortcutPath
    )

    Write-Verbose "Attempting to add alias '$AliasName' with path '$ShortcutPath'"

    try {
        $resolvedTarget = Resolve-ShortcutAliasTarget -AliasName $AliasName -ShortcutPath $ShortcutPath
        Add-AliasPath -Path $YamlCfgPath -AliasName $resolvedTarget.Name -ShortcutPath $resolvedTarget.Path
        Write-Host "Alias '$AliasName' added successfully -> $($resolvedTarget.Path)" -ForegroundColor Green
    }
    catch {
        Write-Host "Failed to add alias: $($_.Exception.Message)" -ForegroundColor Red
    }
}

function Remove-ShortcutAlias {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [string]$AliasName
    )

    try {
        $removed = Remove-AliasPath -Path $YamlCfgPath -AliasName $AliasName
        if (-not $removed) {
            Write-Host "Alias '$AliasName' not found" -ForegroundColor Red
            return
        }

        # Remove the corresponding global function as well
        $funcPath = "Function:\Global:$AliasName"
        if (Test-Path $funcPath) {
            Remove-Item -Path $funcPath -ErrorAction Stop
            Write-Verbose "Removed global function: $AliasName"
        }

        Write-Host "Alias '$AliasName' removed successfully" -ForegroundColor Green
    }
    catch {
        Write-Host "Failed to remove alias: $($_.Exception.Message)" -ForegroundColor Red
    }
}

function Search-ShortcutAlias {
    [CmdletBinding()]
    param (
        [Parameter(Position = 0)]
        [string]$AliasName = "*" # Fuzzy match everything by default
    )

    $searchResults = Get-ShortcutAliasSearchResults -AliasName $AliasName

    if (-not $searchResults) {
        Write-Host "No alias matching '$AliasName' found" -ForegroundColor Yellow
        return
    }

    return $searchResults
}

function Update-ShortcutAlias {
    [CmdletBinding()]
    param ()

    $entries = Get-ShortcutAliasRegistryEntries
    if (-not $entries) {
        Write-Verbose "No aliases found to update"
        return
    }

    $updatedCount = 0

    foreach ($entry in $entries) {
        $name = $entry.Name
        $target = $entry.Path

        # Only validate local path existence for non-URL targets
        if (-not $entry.IsUrl -and -not (Test-Path $target)) {
            Write-Warning "Target path not found for alias '$name': $target"
            continue
        }

        try {
            $launchInvoker = $script:ShortcutAliasLaunchInvoker
            $scriptBlock = {
                & $launchInvoker -Target $target
            }.GetNewClosure()

            Set-Item -Path "Function:\Global:$name" -Value $scriptBlock -ErrorAction Stop
            Write-Verbose "Updated $name -> $target"
            $updatedCount++
        }
        catch {
            Write-Warning "Failed to update alias '$name': $($_.Exception.Message)"
        }
    }

    Write-Host "Updated $updatedCount/$($entries.Count) aliases successfully" -ForegroundColor Green
}

Export-ModuleMember -Function Use-ShortcutAlias -Alias usa