PoshJohn.psm1

$dllPath = Join-Path -Path $PSScriptRoot -ChildPath 'PoshJohn.dll'
Import-Module $dllPath

# Download John assets from GitHub releases
function Install-JohnAssets {
    param(
        [Parameter(Mandatory = $true)]
        [PSObject[]]$Assets,

        [Parameter(Mandatory = $false)]
        [string]$GitHubToken
    )

    # Get module version from manifest
    $manifestPath = Join-Path $PSScriptRoot 'PoshJohn.psd1'
    $manifest = Import-PowerShellDataFile -Path $manifestPath
    $moduleVersion = $manifest.ModuleVersion
    $preRelease = $manifest.PrivateData.PSData.Prerelease
    if ($preRelease) {
        $moduleVersion += "-$preRelease"
    }

    # Get specific release from GitHub matching module version
    $repo = 'ArmaanMcleod/PoshJohn'
    $tag = "v$moduleVersion"
    $apiUrl = "https://api.github.com/repos/$repo/releases/tags/$tag"

    Write-Information "Fetching release info from: $apiUrl" -InformationAction Continue

    # Prepare headers for authenticated requests if token is provided
    $headers = @{}
    if ($GitHubToken) {
        $headers['Authorization'] = "Bearer $GitHubToken"
    }

    $release = Invoke-RestMethod -Uri $apiUrl -Headers $headers

    foreach ($assetInfo in $Assets) {
        $AssetName = $assetInfo.AssetName
        $OutputDir = $assetInfo.OutputDir

        Write-Information "Downloading John the Ripper GitHub release asset: $AssetName" -InformationAction Continue
        Write-Information "Output directory: $OutputDir" -InformationAction Continue

        $asset = $release.assets | Where-Object { $_.name -eq $AssetName } | Select-Object -First 1
        if (-not $asset) {
            throw "Could not find $AssetName in release $tag"
        }

        # Download the asset
        $downloadUrl = $asset.browser_download_url
        $tempFile = Join-Path ([System.IO.Path]::GetTempPath()) $AssetName

        Write-Information "Downloading from: $downloadUrl" -InformationAction Continue

        try {
            Invoke-WebRequest -Uri $downloadUrl -OutFile $tempFile -ErrorAction Stop

            # Extract the archive
            New-Item -ItemType Directory -Force -Path $OutputDir -ErrorAction Stop | Out-Null

            if ($AssetName.EndsWith('.zip')) {
                # Windows
                Expand-Archive -Path $tempFile -DestinationPath $OutputDir -Force -ErrorAction Stop
            } else {
                # Linux/macOS (tar.gz)
                & tar -xzvf $tempFile -C $OutputDir
                if ($LASTEXITCODE -ne 0) {
                    throw "Tar extraction failed with exit code $LASTEXITCODE"
                }
            }

            Write-Information "Successfully downloaded and extracted John the Ripper assets to: $OutputDir" -InformationAction Continue
        }
        catch {
            # Cleanup output directory on failure
            # This prevents partial/corrupted installs
            if (Test-Path $OutputDir) {
                Remove-Item $OutputDir -Recurse -Force -ErrorAction SilentlyContinue
                Write-Information "Cleaned up output directory due to failure: $OutputDir" -InformationAction Continue
            }
            throw
        }
        finally {
            # Always clean up temp file
            if (Test-Path $tempFile) {
                Remove-Item $tempFile -Force -ErrorAction SilentlyContinue
                Write-Information "Cleaned up temporary file: $tempFile" -InformationAction Continue
            }
        }
    }
}

# Set execute permissions on binaries (Linux/macOS)
function Set-BinariesExecutable {
    param(
        [Parameter(Mandatory = $true)]
        [string]$RunDir,

        [Parameter(Mandatory = $true)]
        [string[]]$Binaries
    )

    if (Test-Path $RunDir -PathType Container) {
        foreach ($bin in $Binaries) {
            $binPath = Join-Path $RunDir $bin

            if (Test-Path $binPath -PathType Leaf) {
                & chmod +x $binPath
                if ($LASTEXITCODE -ne 0) {
                    throw "Failed to set execute permission on '$binPath' with exit code $LASTEXITCODE."
                }
            }
        }
    }
}

# Main module import logic
# Handle platform-specific John the Ripper & pdf2john asset installation
try {
    # Check for GitHub token in environment (for CI/CD scenarios)
    $githubToken = $env:GITHUB_TOKEN
    $johnDir = Join-Path $PSScriptRoot 'john'
    $pdf2johnDir = Join-Path $PSScriptRoot 'pdf2john'

    $assets = @()
    if ($IsWindows) {
        if (-not (Test-Path $johnDir)) {
            $assets += [PSCustomObject]@{
                AssetName = 'john-windows-x64.zip'
                OutputDir = $johnDir
            }
        }
        if (-not (Test-Path $pdf2johnDir)) {
            $assets += [PSCustomObject]@{
                AssetName = 'pdf2john-windows-x64.zip'
                OutputDir = $pdf2johnDir
            }
        }
    }
    elseif ($IsLinux) {
        if (-not (Test-Path $johnDir)) {
            $assets += [PSCustomObject]@{
                AssetName = 'john-linux-x64.tar.gz'
                OutputDir = $johnDir
            }
        }
        if (-not (Test-Path $pdf2johnDir)) {
            $assets += [PSCustomObject]@{
                AssetName = 'pdf2john-linux-x64.tar.gz'
                OutputDir = $pdf2johnDir
            }
        }
    }
    elseif ($IsMacOS) {
        if (-not (Test-Path $johnDir)) {
            $assets += [PSCustomObject]@{
                AssetName = 'john-macos-arm64.tar.gz'
                OutputDir = $johnDir
            }
        }
        if (-not (Test-Path $pdf2johnDir)) {
            $assets += [PSCustomObject]@{
                AssetName = 'pdf2john-macos-arm64.tar.gz'
                OutputDir = $pdf2johnDir
            }
        }
    }

    if ($assets.Count -gt 0) {
        Install-JohnAssets -Assets $assets -GitHubToken $githubToken
    }

    if ($IsLinux -or $IsMacOS) {
        Set-BinariesExecutable -RunDir $johnDir -Binaries @('john', 'zip2john')
        Set-BinariesExecutable -RunDir $pdf2johnDir -Binaries @('pdf2john')
    }
}
catch {
    throw "Module import failed. Failed to download John the Ripper assets: $_"
}