#!/bin/bash
$null = New-Module {
    . "$PSScriptRoot/System.ps1";

    <#
        .SYNOPSIS
        Runs an action involving browser automation.

        .PARAMETER Action
        The action to execute.
    #>
    function Start-BrowserAutomation {
        param(
            [scriptblock] $Action
        )

        if (-not ("OpenQA.Selenium.Firefox.FirefoxDriver" -as [type])) {
            $zipFile = [System.IO.Compression.ZipFile]::OpenRead((Get-Package Selenium.WebDriver).Source);
            $webDriver = ($zipFile.Entries | Where-Object { $_.FullName -like "lib/net6.0/WebDriver.dll" })[0];
            $stream = [System.IO.MemoryStream]::new();
            $reader = [System.IO.StreamReader]($webDriver).Open();
            $reader.BaseStream.CopyTo($stream);
            [byte[]]$bytes = $stream.ToArray();
            $reader.Close();
            $reader.Dispose();
            $stream.Close();
            $stream.Dispose();
            $zipFile.Dispose();
            $null = [System.Reflection.Assembly]::Load($bytes);
        }

        & $Action;
    }

    <#
        .SYNOPSIS
        Downloads a file from the specified url using browser automation.

        .PARAMETER URL
        The url to download the file from.

        .PARAMETER Action
        The action to execute in the browser for initiating the download.

        .PARAMETER OutDir
        The directory to download the file to.
    #>
    function Start-CustomBrowserDownload {
        param(
            [string] $URL,
            [scriptblock] $Action,
            [string] $OutDir
        )

        $downloadAction = $Action;

        Start-BrowserAutomation {
            if (-not $OutDir) {
                $OutDir = ".";
            }

            $dir = New-TemporaryDirectory;
            Write-Host "Downloading ``$URL`` using browser automation…";
            $options = [OpenQA.Selenium.Firefox.FirefoxOptions]::new();
            $options.SetPreference("browser.download.folderList", 2);
            $options.SetPreference("browser.download.dir", "$dir");

            $downloadChecker = {
                $files = Get-ChildItem $dir;

                if ((@($files)).Count -gt 0) {
                    foreach ($file in $files) {
                        try {
                            $stream = [System.IO.File]::Open($file.FullName, [System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None);

                            if ($stream) {
                                $stream.Close();
                            }
                        }
                        catch {
                            return $true;
                        }
                    }

                    return $false;
                } else {
                    return $true;
                }
            };

            $browser = [OpenQA.Selenium.Firefox.FirefoxDriver]::new($options);
            $browser.Navigate().GoToUrl($URL);
            $null = & $downloadAction -Browser $browser;

            while (& $downloadChecker) {
                Write-Host "Waiting for the download to finish…";
                Start-Sleep 1;
            }

            $file = Get-ChildItem $dir;
            $result = Move-Item $file $OutDir -PassThru;
            $browser.Quit();
            Remove-Item -Recurse $dir;
            $result;
        }
    }

    <#
        .SYNOPSIS
        Downloads a file from the specified url using browser automation.

        .PARAMETER URL
        The url to download the file from.

        .PARAMETER ButtonSelector
        The jQuery selector for finding the download button.

        .PARAMETER OutDir
        The directory to download the file to.
    #>
    function Start-BrowserDownload {
        param(
            [string] $URL,
            [string] $ButtonSelector,
            [string] $OutDir = $null
        )

        Start-CustomBrowserDownload @PSBoundParameters -Action {
            param(
                [OpenQA.Selenium.Firefox.FirefoxDriver] $Browser
            )

            $Browser.FindElement([OpenQA.Selenium.By]::CssSelector($ButtonSelector)).Click();
        };
    }
};