[CmdletBinding(SupportsShouldProcess)]param(
    [switch]$sudo,
    [switch]$RemoveOnly,
    [switch]$ShowSkipped,
    [switch]$Force,
    [switch]$NoGitClone,
    [string]$GitURL = 'https://code.lksz.me/lksz/PowerShell_Scripts',
    [Parameter(DontShow)]
    [string]$My_PSScriptRoot = $MyPSScriptRoot
)

function _Setup {
[CmdletBinding(SupportsShouldProcess)]param(
    [switch]$sudo,
    [switch]$RemoveOnly,
    [switch]$ShowSkipped,
    [switch]$Force,
    [switch]$NoGitClone,
    [string]$GitURL = 'https://code.lksz.me/lksz/PowerShell_Scripts',
    [Parameter(DontShow)]
    [string]$My_PSScriptRoot = $MyPSScriptRoot)


#######################################################################
## ProfileCode function (source) will contain the code to be written
#######################################################################
function ProfileCode_pre_common {
# # If TMUX isn't loaded, start byobu, then exit this script (and current shell)
# if ( -not ( Test-Path env:TMUX ) ) { byobu; exit }
}
#######################################################################
## common Profile Code - receommended not to change this
## Code will be loaded from profile.d sub-directory after this.
#######################################################################
function ProfileCode_post_common {
# This loads the personal profile section from the $MyPSScriptRoot/profile.d directory
Get-Command Reload-MyScripts -ErrorAction SilentlyContinue | ForEach-Object { . $_.Name }
}
#######################################################################
## _Setup Logic starts here
#######################################################################

    . ProfileCode_common
    if( -not $MyPSScriptRoot ) { throw '$MyPSScriptRoot does NOT exist!' }

    if ( -not $NoGitClone -and -not (Test-Path $(Join-Path $MyPSScriptRoot '.git')) ) {
        if ( -not [bool]$(Get-Command git -ErrorAction SilentlyContinue)  ) {
            throw "No git command found, you may either omit run with the -NoGitClone switch or install git and try again."
        }
        if ( $PSCmdlet.ShouldProcess("Pull git repo from $GitURL into $MyPSScriptRoot ?") ) {
            Push-Location $MyPSScriptRoot
            $local:tmpGitDir = New-TemporaryFile
            Remove-Item $tmpGitDir
            New-Item -Type Directory $tmpGitDir.FullName | Out-Null
            Write-Verbose "Cloning git repo [$GitURL] into temporary location ('$($tmpGitDir.FullName)')..."
            $local:GitOutput = "$(& git clone $GitURL $tmpGitDir.FullName --no-checkout)"
            Write-Verbose $GitOutput
            Write-Verbose "Moving git repo from temp location to $MyPSScriptRoot . . ."
            Copy-Item -Recurse (Join-Path $tmpGitDir.FullName .git) ./.git
            Remove-Item $tmpGitDir.FullName
            Write-Verbose "Checking out repo..."
            $GitOutput = "$(& git checkout --force)"
            Write-Verbose $GitOutput
            Pop-Location
            Write-Verbose "Git clone done."
        }
    }

    $local:ProfileSignature = [PSCustomObject]([ordered]@{
            Begin   = '#### SZ Auto Profile Setup - BEGIN ####' 
            End     = '#### SZ Auto Profile Setup -  END  ####'
            Removed = '#### SZ Auto Profile Setup - ----- ####'
            Match   = '#### SZ Auto Profile Setup'
        })

    function New-ProfileSetupStatus {
    param($Name, $Path, $Status, $Exist)
        return [PSCustomObject]([ordered]@{
                Name   = $Name
                Path   = $Path
                Status = $Status
                Exist  = $Exist
            })
    }

    $local:_profiles = $PROFILE | fl * -Force | Out-String -Stream | ForEach-Object {
        $local:p = $($_ -split ' : [/A-Z\\]');
        if ( $p[0] -match 'User' ) {
            $p[0].Trim()
        }
    }
    $local:_profile = $profile.CurrentUserAllHosts;
    $local:randomSeed = "####$(Get-Random)####";

    if ( $RemoveOnly -and $ConfirmPreference -eq 'High' -and (-not $PSBoundParameters -or -not $PSBoundParameters.ContainsKey('Confirm')) ) {
        $script:ConfirmPreference = 'Low'
    }


    if ( $sudo -or $(Test-IsAdmin) ) { $_profile = $profile.AllUsersAllHosts }
    $_profiles | Foreach-Object {
        $local:p = Invoke-Expression "`$profile.$_"
        $local:profileContent = $randomSeed;
        $local:exists = Test-Path $p
        $local:userFile = $($_ -match 'CurrentUser')
        $local:cleanupValue = ''
        if ( $exists ) {
            if ( (Get-Content -Path $p | Out-String ) -match $ProfileSignature.Match ) {
                Write-Verbose "Found code in [$_]'$p', preparing..."
                $local:gate = $true;
                $profileContent = Get-Content $p | ForEach-Object {
                    if ( -not $gate ) {
                        if ( $_ -match $ProfileSignature.End ) {
                            $gate = $true;
                        }
                    }
                    else {
                        if ( $_ -match $ProfileSignature.Match ) {
                            if ( $_ -match $ProfileSignature.Begin ) {
                                $gate = $false;
                            }
                            $cleanupValue = $ProfileSignature.Removed
                            return $randomSeed;
                        }
                        return $_
                    }
                }
            } else {
                $profileContent += "`n`n$(Get-Content $p -Raw)"
            }
        }
        
        if( -not( $profileContent -match $randomSeed ) ) { $profileContent = "$randomSeed`n$profileContent" }

        $local:insertProfileCode = $false;
        if ( ($p -eq $_profile) -and -not $RemoveOnly ) {
            $insertProfileCode = $true
            Write-Verbose "Inserting ProfileCode into $($_)..."
            $cleanupValue = $((@(
                    '',
                    $ProfileSignature.Begin,
                    '',
                    $(Export-FunctionSource -NoHeader ProfileCode_pre_common),
                    '',
                    '',
                    $(Export-FunctionSource -NoHeader ProfileCode_common),
                    '',
                    '',
                    $(Export-FunctionSource -NoHeader ProfileCode_post_common),
                    '',
                    $ProfileSignature.End
                ) -join "`n" ).Trim().Replace(('$' + '_'), ('$$' + '_')))
        }
        
        $profileContent = $($profileContent -replace $randomSeed, $cleanupValue).Trim()

        $local:shouldSudo = $sudo -and -not $userFile
        $local:status = 'Skipped'
        if ( $VerbosePreference -eq 'Continue' ) { $ShowSkipped = $true }
        # Test if new profile Content will be empty
        if ( -not ([string]::IsNullOrWhiteSpace($profileContent)) ) {
            $status = "Need $(if($insertProfileCode){if($exists){'Setup'}else{'Init'}}else{'Cleanup'})"
            if ( $sudo -or $userFile ) {
                Write-Verbose "Writing content to $($_)..."
                $local:tmpOutput = New-TemporaryFile -WhatIf:$false -Confirm:$false
                $profileContent | Out-File $tmpOutput.FullName -WhatIf:$false -Confirm:$false
                if ( $shouldSudo -and $( $PSVersionTable.Platform -eq 'Unix' ) ) {
                    # Make sure the file has the right permissions on a Unix machine
                    Invoke-ExpressionEx -sudo:$shouldSudo chmod 'a+r' $tmpOutput.FullName
                }
                try {
                    $local:errMsg = $(Invoke-ExpressionEx -sudo:$shouldSudo @"
    if( -not (Test-Path $(Split-Path -Parent $p)) ) { New-Item -Type Directory $(Split-Path -Parent $p) -Force };
    Copy-Item $($tmpOutput.FullName) $p -Force:`$$Force -ErrorAction Stop | Out-Null;
    Remove-Item $($tmpOutput.FullName) -Force:`$$Force -ErrorAction Stop
"@ 2>&1)
                    if ( $errMsg ) {
                        Write-Error "$errMsg"
                        $status = $status -replace 'Need ', 'Failed '
                    }
                    else {
                        $status = $status -replace 'Need ', ''
                    }
                }
                catch {
                    Write-Error "Writing $p failed!"
                }
            }
            else {
                $status = "Need $status"
                Write-Error "Cannot write into [$_]'$p'! Please re-run with sudo."
            }
        }
        elseif ( ($Force -or $RemoveOnly) -and $exists ) {
            $status = 'Need Removal'
            if ( $sudo -or $userFile ) {
                Write-Verbose "Removing [$_]'$p'..."
                try {
                    Invoke-ExpressionEx -sudo:$shouldSudo Remove-Item $p
                    if ( -not (Test-Path $p) ) { $status = 'Removed' }
                }
                catch {
                    Write-Error "Removal of $p failed!"
                }
            }
            else {
                Write-Error "Cannot remove [$_]'$p'! Please re-run with sudo."
            }
        }
        if ( $status -ne 'Skipped' -or $ShowSkipped ) {
            if ( $shouldSudo -and $status -ne 'Skipped' ) { $status = "$status via sudo" }
            New-ProfileSetupStatus -Name $_ -Path $p -Status $status -Exist $(Test-Path $p)
        }
    }

    Remove-Item function:ProfileCode* -Confirm:$false
}

#######################################################################
## ProfileCode_common (source) containing mandatory code for $PROFILE
#######################################################################
function ProfileCode_common {
function Get-PowerShellPath {
    Get-Process -PID $PID | ForEach-Object { $_.Path, $_.Parent.Path } | Where-Object { $_ -match 'powershell|pwsh' } | Select-Object -First 1
}

function ConvertTo-Base64 {
param([string]$String)
    return [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($String))
}

function Invoke-ExpressionEx {
[CmdletBinding(SupportsShouldProcess)]param(
    [switch]$sudo,
    [switch]$Force,
    [string]$Function,
    [switch]$MarshalOutput,
    [Parameter(Position = 1, ValueFromRemainingArguments = $true)]
    [string[]]$expr
)

    function do_sudo {
    [CmdletBinding()]param(
        [string]$sudo_cmd
    )

        function do_sudo_win {
        [CmdletBinding()]param(
            [string]$sudo_cmd
        )

            $local:sudo_exec = [string]::Empty
            if ( (Get-Command scoop -ErrorAction SilentlyContinue) ) {
                $sudo_exec = $(scoop which gsudo *>&1)
                if ( $sudo_exec -match 'not found' ) {
                    $sudo_exec = $(scoop which sudo *>&1)
                }
                if ( $sudo_exec -match 'not found' ) { $sudo_exec = [string]::Empty }
            }
            if ( $sudo_exec ) {
                & $sudo_exec $sudo_cmd
            }
            else {
                Write-Error "Didn't find a known sudo command"
            }
        }

        if ( $PSVersionTable.Platform -eq 'Unix' ) {
            /usr/bin/env sudo $(Get-PowerShellPath) "-noprofile" "-EncodedCommand" $(ConvertTo-Base64 $sudo_cmd)
        }
        else {
            $local:currentIdenity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
            if ( $currentIdenity.UserClaims.Value -contains 'S-1-5-32-544' ) {
                do_sudo_win $sudo_cmd
            }
            else {
                Write-Error "User [$($currentIdenity.Name)]does not have permissions to run as admin."
            }
        }

    }

    if ( $sudo -and -not $(Test-IsAdmin) ) {
        $local:tmpOutputFile = $null
        $local:tmpScriptFile = $null

        $local:sudo_cmd = "$($expr -join ' ')".Trim()
        if ( $Function ) {
            $tmpScriptFile = New-TemporaryFile -WhatIf:$false -Confirm:$false
            $tmpScriptFile = Move-Item $tmpScriptFile.FullName "$($tmpScriptFile.FullName).ps1" -PassThru -WhatIf:$false -Confirm:$false

            $local:tmpScriptContent = Export-FunctionSource $Function
            $sudo_cmd = "$Function $sudo_cmd"

            $tmpScriptContent | Out-File $tmpScriptFile.FullName -WhatIf:$false
            $expr = @( ".", $tmpScriptFile.FullName, ";", "`n$Function" ) + $expr
        }

        $expr = @(
            "`n`$WhatIfPreference=`$$WhatIfPreference;",
            "`n`$VerbosePreference='$VerbosePreference';",
            "`n`$ErrorActionPreference='$ErrorActionPreference';",
            "`n`$ConfirmPreference='$ConfirmPreference';`n"
        ) + $expr

        if ( $MarshalOutput ) {
            $tmpOutputFile = New-TemporaryFile -WhatIf:$false -Confirm:$false
            $expr += @( "| Export-CliXml -Path '$($tmpOutputFile.FullName)'" )
        }

        $expr += @( ";`n exit `$LastExitCode" )

        ## $tmpScriptContent += "$($expr -join ' ')".Trim()
        Write-Verbose "Performing the following command line via SUDO:`n{$sudo_cmd}"

        $sudo_cmd = "$($expr -join ' ')".Trim()

        do_sudo $sudo_cmd
        ## do_sudo 'Get-Content' $tmpScriptFile.FullName '|' 'Invoke-Expression' '|' 'Export-Clixml' '-Path' $tmpOutputFile.FullName
        #do_sudo $sudo_cmd

        if ( $tmpScriptFile ) { Remove-Item $tmpScriptFile }
        if ( $tmpOutputFile ) { Import-Clixml $tmpOutputFile; Remove-Item $tmpOutputFile }
        return
    }

    Write-Verbose "Perofrming the following expression in-line:`n{$($expr -join ' ')}"
    Invoke-Expression "$expr"
}

function Test-IsAdmin {
[CmdletBinding(ConfirmImpact = 'None')]
[OutputType([bool])]
param()

    switch ($true) {
        $( ($PSVersionTable.PSEdition -eq 'Desktop') -or
            ($PSVersionTable.Platform -eq 'Win32NT')
        ) {
            # Fastest way on Windows
            ([Security.Principal.WindowsPrincipal](
                    [Security.Principal.WindowsIdentity]::GetCurrent()
                )).IsInRole(
                [Security.Principal.WindowsBuiltInRole]'Administrator'
            )
        }
        $( ($PSVersionTable.PSEdition -eq 'Core') -and
            ($PSVersionTable.Platform -eq 'Unix')
        ) {
            # On macOS and Linux we use ID to figure out if we run elevated (0 means superuser rights)
            return $((id -u) -eq 0)
        }
        default {
            # Unable to figure it out!
            Write-Warning -Message 'Unknown'

            return
        }
    }
}

function Export-FunctionSource {
param(
    [switch]$NoHeader,
    [Parameter(Position = 0, ValueFromRemainingArguments = $true)]
    #[ValidateSet([Functions])]
    [string[]]$FunctionName
)

    $local:src = ""
    foreach ( $local:func in $FunctionName ) {
        if ( -not $NoHeader ) { $src += "`nfunction $func {" }
        $src += "`n"
        $src += $((Get-Command -Type Function $func).Definition)
        if ( -not $NoHeader ) { $src += "`n}" }
        $src += "`n"
    }
    return $src.Trim()
}

# Are we running in Unix or Windows?
$global:PathEnvDelimiter = $(if ( $PSVersionTable.Platform -match 'unix' ) { ':' } else { ';' })
function Split-PathEnv {
param([string]$EnvPath)
    $EnvPath -split $PathEnvDelimiter
}

# Establish Module Path
$global:MyPSModulePath = Split-PathEnv $env:PSModulePath |
    Where-Object { $_ -match "^$($(Resolve-Path ~) -replace '\\',"\\")" } |
    Where-Object { Test-Path $_ } |
    Get-Item | Sort-Object -Property LastWriteTime -Descending |
    Select-Object -ExpandProperty FullName -First 1

if ( -not $MyPSModulePath ) {
    $MyPSModulePath = $(Join-Path $(Join-Path $(Resolve-Path ~) 'powershell') 'Modules')
    $env:PSModulePath = "$MyPSModulePath$PathEnvDelimiter$env:PSModulePath"
}
if ( -not (Test-Path $MyPSModulePath) ) {
    New-Item -ItemType Directory -Path $MyPSModulePath -Force | Out-Null
}

Write-Verbose $(Get-Item $MyPSModulePath | Select-Object -ExpandProperty FullName)

# Establish local/personal Script Root and make sure it's in $env:Path
$local:p = Split-PathEnv $env:PATH
$local:My_PSScriptRoot = Join-Path (Split-Path -Parent $MyPSModulePath) Scripts
Write-Verbose "MyPSScriptRoot = $My_PSScriptRoot"
if ( -not (Test-Path $My_PSScriptRoot) ) {
    New-Item -ItemType Directory -Path $My_PSScriptRoot -Force | Out-Null
    New-Item -ItemType Directory -Path $My_PSScriptRoot/profile.d -Force | Out-Null
}
$global:MyPSScriptRoot = $My_PSScriptRoot

$p = @($p[0], $MyPSScriptRoot) + $($p | Select-Object -Skip 1)
$env:PATH = $p -join $PathEnvDelimiter
} 

$SetupFromWeb = [bool]$(@( $SetupFromWeb, $sfw, $LoadCode ) | Where-Object { $_ })

if ( -not $SetupFromWeb ) {
    _setup @PSBoundParameters
    Get-Item function:_setup | Remove-Item
}
Remove-Variable SetupFromWeb -ErrorAction SilentlyContinue