#!/usr/bin/env pwsh

$null = New-Module mcfly {
    # We need PSReadLine for a number of capabilities
    if ($null -eq (Get-Module -Name PSReadLine)) {
        Write-Host "Installing PSReadLine as McFly dependency"
        Install-Module PSReadLine
    }

    # Get history file and make a dummy file for psreadline (hopefully after it has loaded the real history file to its in memory history)
    $env:HISTFILE = $null -eq $env:HISTFILE -or "" -eq $env:HISTFILE ? (Get-PSReadLineOption).HistorySavePath : $env:HISTFILE;
    $psreadline_dummy = New-TemporaryFile
    Set-PSReadLineOption -HistorySavePath $psreadline_dummy.FullName


    $fileExists = Test-Path -path $env:HISTFILE
    if (-not $fileExists) {
        Write-Host "McFly: ${env:HISTFILE} does not exist or is not readable. Please fix this or set HISTFILE to something else before using McFly.";
        return 1;
    }

    # MCFLY_SESSION_ID is used by McFly internally to keep track of the commands from a particular terminal session.
    $MCFLY_SESSION_ID = new-guid
    $env:MCFLY_SESSION_ID = $MCFLY_SESSION_ID

    $env:MCFLY_HISTORY = New-TemporaryFile
    Get-Content $env:HISTFILE | Select-Object -Last 100 | Set-Content $env:MCFLY_HISTORY

    <#
    .SYNOPSIS
    Cmdlet to run McFly

    .PARAMETER CommandToComplete
    The command to complete

    .EXAMPLE
    Invoke-McFly -CommandToComplete "cargo bu"
    #>
    function Invoke-McFly {
        Param([string]$CommandToComplete)
        $lastExitTmp = $LASTEXITCODE
        $tempFile = New-TemporaryFile
        Start-Process -FilePath '::MCFLY::' -ArgumentList "search", "$CommandToComplete", -o, "$tempFile" -NoNewWindow -Wait
        foreach($line in Get-Content $tempFile) {
            $key, $value = $line -split ' ', 2
            if ("mode" -eq $key) {
                $mode = $value
            }
            if ("commandline" -eq $key) {
                $commandline = $value
            }
        }
        if(-not ($null -eq $commandline)) {
            [Microsoft.PowerShell.PSConsoleReadLine]::DeleteLine()
            [Microsoft.PowerShell.PSConsoleReadline]::Insert($commandline)
            if("run" -eq $mode) {
                [Microsoft.PowerShell.PSConsoleReadline]::AcceptLine()
            }
        }
        Remove-Item $tempFile
        $LASTEXITCODE = $lastExitTmp
    }

    <#
    .SYNOPSIS
    Add a command to McFly's history.

    .PARAMETER Command
    The string of the command to add to McFly's history

    .PARAMETER ExitCode
    The exit code of the command to add

    .EXAMPLE
    Add-CommandToMcFly -Command "cargo build"
    #>
    function Add-CommandToMcFly {
        Param (
            [string] $Command,
            [int] $ExitCode
        )
        $ExitCode = $ExitCode ?? 0;
        $Command | Out-File -FilePath $env:MCFLY_HISTORY -Append
        Start-Process -FilePath '::MCFLY::' -ArgumentList add, --exit, $ExitCode, --append-to-histfile, $env:HISTFILE -NoNewWindow | Write-Host
    }

    # We need to make sure we call out AddToHistoryHandler right after each command is called
    Set-PSReadLineOption -HistorySaveStyle SaveIncrementally

    Set-PSReadLineOption -PredictionSource HistoryAndPlugin

    Set-PSReadLineOption -AddToHistoryHandler {
        Param([string]$Command)
        $lastExitTmp = $LASTEXITCODE
        $Command = $Command.Trim();
        # PSReadLine executes this before the command even runs, so we don't know its exit code - assume 0
        Add-CommandToMcFly -Command $Command -ExitCode 0
        $LASTEXITCODE = $lastExitTmp
        # Tell PSReadLine to save the command to their in-memory history (and also the dummy file)
        return $true
    }

    Set-PSReadLineKeyHandler -Chord "Ctrl+r" -ScriptBlock {
        $line = $null
        $cursor = $null
        [Microsoft.PowerShell.PSConsoleReadline]::GetBufferState([ref]$line, [ref]$cursor)
        "#mcfly: $line" | Out-File -FilePath $env:MCFLY_HISTORY -Append
        Invoke-McFly -CommandToComplete $line
    }

    Export-ModuleMember -Function @(
        "Invoke-McFly"
        "Add-CommandToMcFly"
    )
}
