#Requires -Version 7.4

function Test-PortableDepsMode {
    <#
    .SYNOPSIS
        Detects whether the module is running in portable mode by inspecting PSModulePath.

    .DESCRIPTION
        Portable mode is identified when PSModulePath has exactly 2 entries and one
        entry's leaf folder is 'deps'. This matches the environment set by the Go CLI's
        BuildEnv() in portable mode, which restricts PSModulePath to only the bundled
        module directory and the bundled deps directory.

    .OUTPUTS
        [bool] $true if portable mode detected, $false otherwise.
    #>
    [CmdletBinding()]
    [OutputType([bool])]
    param()

    $paths = $env:PSModulePath -split [System.IO.Path]::PathSeparator
    if ($paths.Count -ne 2) {
        return $false
    }

    # Check if one of the two entries has a leaf folder named 'deps'
    foreach ($p in $paths) {
        if ((Split-Path -Leaf $p) -eq 'deps') {
            return $true
        }
    }

    return $false
}

function Get-PortableDepsPath {
    <#
    .SYNOPSIS
        Returns the bundled deps path from PSModulePath in portable mode.

    .OUTPUTS
        [string] The deps path, or $null if not in portable mode.
    #>
    [CmdletBinding()]
    [OutputType([string])]
    param()

    $paths = $env:PSModulePath -split [System.IO.Path]::PathSeparator
    foreach ($p in $paths) {
        if ((Split-Path -Leaf $p) -eq 'deps') {
            return $p
        }
    }

    return $null
}

function Test-ModuleDependenciesPrerequisite {
    <#
    .SYNOPSIS
        Validates that all required PowerShell modules are available with minimum versions.

    .DESCRIPTION
        Checks for the presence and version of required module dependencies:
        - PSToml >= 0.4.0

        In portable mode (detected via PSModulePath shape), validates the bundled
        deps path directly. In dev mode, uses standard Get-Module -ListAvailable.

        Reports all missing or outdated modules in a single check result.

    .EXAMPLE
        Test-ModuleDependenciesPrerequisite

        Returns a result object indicating whether all module dependencies are satisfied.

    .OUTPUTS
        [PSCustomObject] with Name, Passed, Message, Required, Current, and HelpUrl properties.
    #>
    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param()

    # Define required modules with minimum versions
    $requiredModules = @(
        @{ Name = 'PSToml'; MinVersion = [version]'0.4.0' }
    )

    # Detect portable vs dev mode
    $isPortable = Test-PortableDepsMode
    $depsPath = if ($isPortable) { Get-PortableDepsPath } else { $null }

    # Track results
    $missingModules = [System.Collections.Generic.List[string]]::new()
    $outdatedModules = [System.Collections.Generic.List[string]]::new()
    $satisfiedModules = [System.Collections.Generic.List[string]]::new()
    $installCommands = [System.Collections.Generic.List[string]]::new()

    foreach ($required in $requiredModules) {
        $moduleName = $required.Name
        $minVersion = $required.MinVersion

        if ($isPortable) {
            # Portable mode: check bundled deps path directly
            $moduleManifestPath = Join-Path -Path $depsPath -ChildPath "$moduleName/$moduleName.psd1"

            if (-not (Test-Path -Path $moduleManifestPath -PathType Leaf)) {
                # Bundled module not found
                $missingModules.Add($moduleName)
                $installCommands.Add("Re-extract the portable distribution or verify the deps folder is intact.")
            }
            else {
                # Parse version from the bundled manifest
                try {
                    $manifestData = Import-PowerShellDataFile -Path $moduleManifestPath -ErrorAction Stop
                    $bundledVersion = [version]$manifestData.ModuleVersion

                    if ($bundledVersion -lt $minVersion) {
                        $outdatedModules.Add("$moduleName (found $bundledVersion, need $($minVersion.ToString())+)")
                        $installCommands.Add("Re-extract the portable distribution.")
                    }
                    else {
                        $satisfiedModules.Add("$moduleName $bundledVersion")
                    }
                }
                catch {
                    # Manifest exists but cannot be parsed
                    $missingModules.Add($moduleName)
                    $installCommands.Add("Re-extract the portable distribution or verify the deps folder is intact.")
                }
            }
        }
        else {
            # Dev mode: existing behavior unchanged - use Get-Module -ListAvailable
            $availableModule = Get-Module -Name $moduleName -ListAvailable -ErrorAction SilentlyContinue |
                Sort-Object -Property Version -Descending |
                Select-Object -First 1

            if ($null -eq $availableModule) {
                # Module not installed
                $missingModules.Add($moduleName)
                $installCommands.Add("Install-Module $moduleName -MinimumVersion $($minVersion.ToString())")
            }
            elseif ($availableModule.Version -lt $minVersion) {
                # Module installed but version too low
                $outdatedModules.Add("$moduleName (found $($availableModule.Version), need $($minVersion.ToString())+)")
                $installCommands.Add("Update-Module $moduleName -MinimumVersion $($minVersion.ToString())")
            }
            else {
                # Module satisfies requirement
                $satisfiedModules.Add("$moduleName $($availableModule.Version)")
            }
        }
    }

    # Determine overall result
    $passed = ($missingModules.Count -eq 0) -and ($outdatedModules.Count -eq 0)

    # Build required description
    $requiredDescription = ($requiredModules | ForEach-Object {
        "$($_.Name) >= $($_.MinVersion.ToString())"
    }) -join ', '

    # Build current state description
    $currentParts = [System.Collections.Generic.List[string]]::new()
    if ($satisfiedModules.Count -gt 0) {
        $currentParts.Add("Satisfied: $($satisfiedModules -join ', ')")
    }
    if ($missingModules.Count -gt 0) {
        $currentParts.Add("Missing: $($missingModules -join ', ')")
    }
    if ($outdatedModules.Count -gt 0) {
        $currentParts.Add("Outdated: $($outdatedModules -join ', ')")
    }
    $currentValue = if ($currentParts.Count -gt 0) { $currentParts -join '; ' } else { 'No modules checked' }

    # Build message
    $message = if ($passed) {
        'All module dependencies are satisfied.'
    }
    else {
        $messageParts = [System.Collections.Generic.List[string]]::new()

        if ($missingModules.Count -gt 0) {
            foreach ($missing in $missingModules) {
                $minVer = ($requiredModules | Where-Object { $_.Name -eq $missing }).MinVersion
                if ($isPortable) {
                    $messageParts.Add("Bundled $missing not found at '$depsPath\$missing'. Re-extract the portable distribution or verify the deps folder is intact.")
                }
                else {
                    $messageParts.Add("Missing $missing >= $($minVer.ToString()). Install: Install-Module $missing -MinimumVersion $($minVer.ToString())")
                }
            }
        }

        if ($outdatedModules.Count -gt 0) {
            foreach ($outdated in $outdatedModules) {
                # Extract module name from the formatted string
                $moduleName = ($outdated -split ' \(')[0]
                $minVer = ($requiredModules | Where-Object { $_.Name -eq $moduleName }).MinVersion
                if ($isPortable) {
                    $messageParts.Add("Bundled $outdated. Re-extract the portable distribution.")
                }
                else {
                    $messageParts.Add("$outdated. Update: Update-Module $moduleName -MinimumVersion $($minVer.ToString())")
                }
            }
        }

        $messageParts -join ' '
    }

    [PSCustomObject]@{
        Name     = 'Module Dependencies'
        Passed   = $passed
        Message  = $message
        Required = $requiredDescription
        Current  = $currentValue
        HelpUrl  = 'https://learn.microsoft.com/en-us/powershell/module/powershellget/install-module'
    }
}
