# param (
#     [string]$username,
#     [string]$password
# )

Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"

#  .\agentController.ps1 -username "blake" -password "blake"
# config
$ServiceName = "LSTV3_app"
$AppDir= "C:\Users\matthes01\Documents\lst_v3"
$KeepLast = 10 # how many builds do we want to keep.
$Servers = @( 
    [PSCustomObject]@{
        server = "uslim1vms006"
        token  = "uslim1"
        loc    = "D$\LST_V3"
    },
    [PSCustomObject]@{
        server = "ushou1vms006"
        token  = "ushou1"
        loc    = "D$\LST_V3"
    },
    [PSCustomObject]@{
        server = "usday1vms006"
        token  = "usday1"
        loc    = "D$\LST_V3"
    },
    [PSCustomObject]@{
        server = "usweb1vms006"
        token  = "usweb1"
        loc    = "D$\LST_V3"
    },
    [PSCustomObject]@{
        server = "usjci1vms006"
        token  = "usjci1"
        loc    = "D$\LST_V3"
    },
        [PSCustomObject]@{
        server = "usshe1vms006"
        token  = "usshe1"
        loc    = "D$\LST_V3"
    }
    #@{ server = "usbet1vms006"; token = "usbet1";loc = "C$\Users\adm_matthes01\Desktop\lst_backend"; }
    #@{ server = "usbow1vms006"; token = "usbow1"; loc = "C$\Users\adm_matthes01\Desktop\lst_backend" ; }
    #@{ server = "usbow2vms006"; token = "usbow2"; loc = "C$\Users\adm_matthes01\Desktop\lst_backend" ; }
    #@{ server = "usflo1vms006"; token = "usflo1"; loc = "C$\Users\adm_matthes01\Desktop\lst_backend" ; }
    #@{ server = "ushou1vms006"; token = "ushou1"; loc = "C$\Users\adm_matthes01\Desktop\lst_backend" ;}
    #@{ server = "usiow1vms006"; token = "usiow1"; loc = "C$\Users\adm_matthes01\Desktop\lst_backend" ; }
    #@{ server = "usiow1vms006"; token = "usiow2"; loc = "D$\lst\lst_backend_2" ; }
    #@{ server = "usjci1vms006"; token = "usjci1"; loc = "D$\LST\lst_backend" ; }
    #@{ server = "usksc1vms006"; token = "uskc1"; loc = "C$\Users\adm_matthes01\Desktop\lst_backend" ;}
    #@{ server = "usmcd1vms006"; token = "usmcd1"; loc = "C$\Users\adm_matthes01\Desktop\lst_backend" ; }
    #@{ server = "usshe1vms006"; token = "usshe1"; loc = "C$\Users\adm_matthes01\Desktop\lst_backend" ;}
    #@{ server = "usslc1vms006"; token = "usslc1"; loc = "C$\Users\adm_matthes01\Desktop\lst_backend" ;}
    #@{ server = "usstp1vms006"; token = "usstp1"; loc = "E$\LST\lst_backend" ; }
    #@{ server = "usmar1vms006"; token = "test1"; loc = "E$\LST\lst_backend"; }
    
)   
#--------------------------------------------------
# below this shouldn't really need to be messed with
#--------------------------------------------------

# create the username and password to sign into the server and do the udpate stuff
$credFile = Join-Path $AppDir ".scriptCreds"
$credData = @{}

Get-Content $credFile | ForEach-Object {
    if ($_ -match "=") {
        $key, $value = $_ -split "=", 2
        $credData[$key.Trim()] = $value.Trim()
    }
}
$username = $credData["user"]
$password = $credData["password"]

$securePass = ConvertTo-SecureString $password -AsPlainText -Force
$credentials = New-Object System.Management.Automation.PSCredential($username, $securePass)

function Show-Menu {
    Clear-Host
    Write-Host "==============================="
    Write-Host "        Deployment Menu        "
    Write-Host "==============================="
    Write-Host "1. Build app"
    Write-Host "2. Deploy New Release"
    Write-Host "3. Deploy Test Servers"
    Write-Host "4. Upgrade Node"
    Write-Host "5. Update Postgres"
    Write-Host "6. Exit"
    Write-Host ""
}

function Select-Server {
    param([object[]]$List)
    for ($i = 0; $i -lt $List.Count; $i++) {
        Write-Host ("{0}. {1}" -f ($i+1), $List[$i].server)
    }
    Write-Host "$($List.Count + 1) All"
    Write-Host "$($List.Count + 2) exit"

    $sel = Read-Host "Select server by number"

    if ($sel -eq $List.Count + 1) {
        return "all"
    }

    if ($sel -eq $List.Count + 2) {
        return $null
    }
    if ($sel -match '^\d+$' -and [int]$sel -ge 1 -and [int]$sel -le $List.Count) {
        return $List[[int]$sel - 1]
    } else {
        Write-Host "Invalid selection"
        Read-Host -Prompt "Press Enter to continue..."
        Select-Server -List $Servers
        return $null
    }
}

function Build-App {
    try {
        Push-Location $AppDir
        Write-Host "Running: npm run build in $AppDir"
        npm run build
        Start-Sleep -Seconds 3
        Write-Host "Build completed successfully."   
    }
    catch {
        Write-Host "Build failed: $_"
        throw
    }
    finally {
        Pop-Location
    }
}

function Zip-App {
    $BuildNumber = "1"
    $buildFile = Join-Path $AppDir ".buildNumber"

    # validate we have a build file and create it if not there
    if (-Not (Test-Path $buildfile)) {
        Write-Error "File not found: $buildfile, creating it."
        New-Item -Path $buildfile -ItemType File | Out-Null
        $BuildNumber = "1"
        Set-Content -Path $BuildFile -Value $BuildNumber
    }
    
         #bump the build number
    if (Test-Path $BuildFile) {
        $content = Get-Content $BuildFile | Select-Object -First 1
        $num = $content.Trim() -as [int]  # safe cast

        if ($num) {
            $BuildNumber = $num + 1
        } else {
            $BuildNumber = 1
        }
    }
    $BuildNumber = $BuildNumber.ToString()
    Set-Content -Path $BuildFile -Value $BuildNumber
    Write-Output "New Build Number: $BuildNumber"

    # get the include data
    $Includes = Join-Path $AppDir ".includes"
    if (-Not (Test-Path $Includes)) {
        Write-Error "File not found: $Includes, exiting as we dont want to zip up the entire app."
        exit 1
    }

    $BuildFolder = Join-Path $AppDir "builds"

    if (-Not (Test-Path $BuildFolder)) {
        write-host "Build folder missing creating it"
        New-Item -Path $BuildFolder -ItemType Directory | Out-Null
    }

    # do the zip stuff
    $itemsToZip = @()
    Get-Content $Includes | ForEach-Object {
        $relPath = $_.Trim()
        if ($relPath -ne "") {
            $fullPath = Join-Path $AppDir $relPath
            if (Test-Path $fullPath) {
                $itemsToZip += [PSCustomObject]@{
                    FullPath = $fullPath
                    RelativePath = $relPath
                }
            } else {
                Write-Warning "Path not found: $fullPath"
            }
        }
    }

    if (-Not $itemsToZip) {
        Write-Warning "No valid files or folders to zip."
        return
    }

    $zipFileName = Join-Path $BuildFolder "LSTV3-$BuildNumber.zip"

    if (Test-Path $zipFileName) {
        Remove-Item $zipFileName
    }

    $tempFolder = Join-Path $AppDir "temp\zip-temp-$BuildNumber"
    if (Test-Path $tempFolder) { Remove-Item $tempFolder -Recurse -Force }
    New-Item -Path $tempFolder -ItemType Directory | Out-Null

    foreach ($item in $itemsToZip) {
        $dest = Join-Path $tempFolder $item.RelativePath
        $destDir = Split-Path $dest
        if (-Not (Test-Path $destDir)) { New-Item -Path $destDir -ItemType Directory | Out-Null }

        if ((Get-Item $item.FullPath).PSIsContainer) {
            Copy-Item -Path $item.FullPath -Destination $dest -Recurse
        } else {
            Copy-Item -Path $item.FullPath -Destination $dest
        }
    }

    Compress-Archive -Path (Join-Path $tempFolder "*") -DestinationPath $zipFileName

    # Cleanup temp folder
    Remove-Item $tempFolder -Recurse -Force

    Write-Output "Created zip: $zipFileName"

    #clean up builds to not get overwhelmed

    if (Test-Path $BuildFolder) {
        # Get all zip files in build folder matching pattern
        $zips = @(Get-ChildItem -Path $BuildFolder -Filter "LSTV3-*.zip" |
          Sort-Object LastWriteTime -Descending)

        $toRemove = $zips | Select-Object -Skip $KeepLast

        foreach ($zip in $toRemove) {
            Remove-Item $zip.FullName -Force
            Write-Output "Removed old build: $($zip.Name)"
        }
    }

}

function Update-Server {
    param (
        [string]$Destination,
        [string]$Server,
        [string]$Token
    )
    $buildFile = Join-Path $AppDir ".buildNumber"
    $BuildNumber = 1
    $BuildFolder = Join-Path $AppDir "builds"

    if (Test-Path $BuildFile) {
        $content = Get-Content $BuildFile | Select-Object -First 1
        $num = $content.Trim() -as [int]  # safe cast

        if ($num) {
            $BuildNumber = $num + 1
        }
        else {
            $BuildNumber = 1
        }
    }

    # Get The current Build we have zipped up
    $BuildNumber = ([int]$BuildNumber - 1).ToString()


    # copy the latest build over
    Write-Host "Forcing the removal of the mapped drive."
    Get-PSDrive -Name "z" -ErrorAction SilentlyContinue | Remove-PSDrive -Force

    try {

        New-PSDrive -Name "z" -PSProvider FileSystem -Root "\\$Server\$Destination" -Credential $credentials 

        # Create the update folder if it doesn't exist
        if (-not (Test-Path -Path "\\$Server\$Destination")) {
            New-Item -ItemType Directory -Path "\\$Server\$Destination" -Force
        }

        # Copying files to the server
        Write-Host "Copying files to $($Server)"
        $zipFile = Join-Path $BuildFolder "LSTV3-$BuildNumber.zip"
        Copy-Item -Path $zipFile -Destination "z:\" -Force
        Write-Host "Files copied to $($Server)"
    }
    catch {
        Write-Host "Error: $_"
    }
    finally {
        # Remove the mapped drive after copying
        if (Get-PSDrive -Name "z" -ErrorAction SilentlyContinue) {
            Write-Host "Removing mapped drive..."
            Remove-PSDrive -Name "z"
        }
    }

    Write-Host "Updating the app to LSTV3-$BuildNumber.zip"
    # do the stop services, unzip, and restart service and pool
    $AppUpdate = {
        param ($Server, $Token, $Destination, $BuildFile)

        #convert everything to the server fun
        $LocalPath = $Destination -replace '\$', ':'
        $BuildFileLoc = "$LocalPath\$BuildFile"

        Write-Host "Updating the app to $($BuildFile)"

        Write-Host "Stopping the services to do the updates, pkgs and db changes."
        $app_name = "LSTV3_app$(if ($Token -eq "usiow2") { "_2" })"

        # TODO: add in the iis reset later

        Write-Host "Stopping $($app_name)"
        Stop-Service -DisplayName $app_name -Force
        Start-Sleep -Seconds 1

        Write-Host "Unzipping the folder..."

        try {
            # Expand the archive
            Expand-Archive -Path $BuildFileLoc -DestinationPath $LocalPath -Force
        }
        catch {
            Write-Host "Error: $_"
            exit 1  # Exit with a non-zero code if there's an error
        }

        # Delete the zip file after extraction
        Write-Host "Deleting the zip file..."
        Remove-Item -Path $BuildFileLoc -Force
        Start-Sleep -Seconds 1

        try {
        # do the install/update
            Push-Location $LocalPath
            Write-Host "Running install/update in: $LocalPath"
            npm install --omit=dev
            Start-Sleep -Seconds 3
            Write-Host "Install/update completed."   
        # do the migrations
            # Push-Location $LocalPath
            Write-Host "Running migrations"
            npm run dev:db:migrate
            Start-Sleep -Seconds 3
            Write-Host "Migrations Completed."   
        }
        catch {
            Write-Host "Migration: $_"
        }
        finally {
            Pop-Location
        }


        Write-Host "Starting $($app_name)"
        Start-Service -DisplayName $app_name -ErrorAction Stop
        Start-Sleep -Seconds 1

    }

    Invoke-Command -ComputerName $Server -ScriptBlock $AppUpdate -ArgumentList $Server, $Token, $Destination, "LSTV3-$BuildNumber.zip" -Credential $credentials
}

do {
    Show-Menu
    $choice = Read-Host "Select an option"

    switch ($choice) {
        "1" {
            Build-App
            Zip-App
            Read-Host -Prompt "Press Enter to continue..."
        }
        "2" {
            $server = Select-Server -List $Servers

            if($server -eq "all") {
                Write-Host "Updating all servers"
                for ($i = 0; $i -lt $Servers.Count; $i++) {
                    Write-Host "Updating $($Servers[$i].server)"
                    Update-Server -Server $Servers[$i].server -Destination $Servers[$i].loc -Token $Servers[$i].token
                    Start-Sleep -Seconds 1
                }
                Read-Host -Prompt "Press Enter to continue..."
            }

            if ($server -ne "all") {
                Write-Host "You selected $($server.server)"
                # do the update to the server.
                # copy to the server
                Update-Server -Server $server.server -Destination $server.loc -Token $server.token
                # stop service
                # extract zip
                # run update check
                # run migration
                # start service backup

                Read-Host -Prompt "Press Enter to continue..."
            }
        }
        "3" {
            $TestServers = @( 
                [PSCustomObject]@{
                    server = "usmcd1vms036"
                    token  = "test1"
                    loc    = "E$\LST_V3"
                },
                [PSCustomObject]@{
                    server = "usiow1vms036"
                    token  = "test2"
                    loc    = "E$\LST_V3"
                }
                )
            $testServer = Select-Server -List $TestServers

            if($testServer -eq "all") {
                Write-Host "Updating all servers"
                for ($i = 0; $i -lt $TestServers.Count; $i++) {
                    Write-Host "Updating $($TestServers[$i].server)"
                    Update-Server -Server $TestServers[$i].server -Destination $TestServers[$i].loc -Token $TestServers[$i].token
                    Start-Sleep -Seconds 1
                }
                Read-Host -Prompt "Press Enter to continue..."
            }

            if ($testServer -ne "all") {
                Write-Host "You selected $($testServer.server)"
                # do the update to the server.
                # copy to the server
                Update-Server -Server $testServer.server -Destination $testServer.loc -Token $testServer.token
                # stop service
                # extract zip
                # run update check
                # run migration
                # start service backup

                Read-Host -Prompt "Press Enter to continue..."
            }
        }
        "4" {
            Write-Host "Choose Server to upgrade node on"
            $server = Select-Server -List $Servers

            if($server -eq "all") {
                Write-Host "Updating all servers"
                for ($i = 0; $i -lt $Servers.Count; $i++) {
                    Write-Host "Updating $($Servers[$i].server)"
                    # Update-Server -Server $Servers[$i].server -Destination $Servers[$i].loc -Token $Servers[$i].token
                    Start-Sleep -Seconds 1
                }
                Read-Host -Prompt "Press Enter to continue..."
            }

            if ($server -ne "all") {
                Write-Host "You selected $($server.server)"
                # Update-Server -Server $server.server -Destination $server.loc -Token $server.token
                # validate we have a node file to install in the folder
                 # stop service
                # do update script on the server
                # delete the .exe file

                Read-Host -Prompt "Press Enter to continue..."
            }
        }
        "5" {
            Write-Host "Choose Server to upgrade postgres on"
            $server = Select-Server -List $Servers

            if($server -eq "all") {
                Write-Host "Updating all servers"
                for ($i = 0; $i -lt $Servers.Count; $i++) {
                    Write-Host "Updating $($Servers[$i].server)"
                    # Update-Server -Server $Servers[$i].server -Destination $Servers[$i].loc -Token $Servers[$i].token
                    Start-Sleep -Seconds 1
                }
                Read-Host -Prompt "Press Enter to continue..."
            }

            if ($server -ne "all") {
                Write-Host "You selected $($server.server)"
                # Update-Server -Server $server.server -Destination $server.loc -Token $server.token
                # validate we have a postgres file to install in the folder
                # stop service
                # do update script on the server
                # delete the .exe file

                Read-Host -Prompt "Press Enter to continue..."
            }
            
        }
        "6" {
            Write-Host "Exiting..."
            exit
        }
        default {
            Write-Host "Invalid selection"
            Start-Sleep -Seconds 2
        }
    }

} while ($choice -ne "3")