@echo off
setlocal EnableExtensions EnableDelayedExpansion
REM FW_Client version will be %1 passed as a launch argument

set "TEMP_DIR=C:\temp"
set "TASK_NAME=FileWave Client Upgrade"
set "TASK_XML=%TEMP_DIR%\FileWave Client Upgrade.xml"
set "WORKER_SCRIPT=%TEMP_DIR%\upgradeClient.bat"
set "MSI_PATH=%TEMP_DIR%\FileWaveClient.msi"
set "UPGRADE_LOG=%TEMP_DIR%\upgradeClient.log"
set "MSI_LOG=%TEMP_DIR%\FileWaveClient.log"
set "LOCK_DIR=%TEMP_DIR%\fwclient-upgrade.lock"
set "SERVICE_NAME=FileWaveWinClient"
set "TASK_START_ATTEMPTS=12"
set "TASK_START_WAIT_SECONDS=5"
set "HANDOFF_RETRY_ATTEMPTS=5"
set "WORKER_INTERRUPT_WAIT_SECONDS=600"
set "WORKER_INTERRUPT_POLL_SECONDS=5"

if not exist "%TEMP_DIR%" mkdir "%TEMP_DIR%" >NUL 2>&1

call :trimlog "%UPGRADE_LOG%"
call :trimlog "%MSI_LOG%"
call :WriteOut "scheduleRestart.bat started."
if not exist "%MSI_LOG%" echo. > "%MSI_LOG%"

if "%~1"=="" (
	call :WriteOut "No target FileWave Client version supplied. Aborting before touching client."
	exit /b 10
)

if not exist "%MSI_PATH%" (
	call :WriteOut "MSI not found at %MSI_PATH%. Aborting before touching client."
	exit /b 11
)

if not exist "%WORKER_SCRIPT%" (
	call :WriteOut "Worker script not found at %WORKER_SCRIPT%. Aborting before touching client."
	exit /b 12
)

if not exist "%TASK_XML%" (
	call :WriteOut "Scheduled task XML not found at %TASK_XML%. Aborting before touching client."
	exit /b 13
)

REM Remove the old upgrade software if it is still there.
set "Value=nothing"
for /f "delims=;" %%a in ('powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -Command "(Get-Package -Name 'FileWave Custom Client Upgrade (from 10.0.X)' -ErrorAction SilentlyContinue).Name"') do set "Value=%%a"
if /I "%Value%"=="FileWave Custom Client Upgrade (from 10.0.X)" (
	call :WriteOut "Uninstalling the FileWave Client upgrader..."
	start /wait msiexec.exe /l*v "%TEMP_DIR%\FileWaveClientUpgraderUninstall.log" /qn /norestart /x "{E3DC560D-C698-41DF-8B6C-EEA0BEFC44EF}" >NUL 2>&1
	call :WriteOut "Done uninstalling the FileWave Client upgrader, exit code !ERRORLEVEL!."
)

call :WriteOut "Installer version %~1."

REM Is the version right? If FW client is not showing in add/remove, it will return no value, so setting a default.
set "version=none"
for /f "delims=;" %%b in ('powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -Command "(Get-Package -Name 'FileWave Client' -ErrorAction SilentlyContinue).Version"') do set "version=%%b"

if "%version%"=="%~1" (
	call :WriteOut "FileWave version %version% already installed."
	call :WriteOut "Exit 0 - upgrade complete."
	exit /b 0
) else (
	call :WriteOut "FileWave version %version% installed. Upgrading..."
)

REM Overwrite any previous scheduled task and recreate if needed.
schtasks /create /tn "%TASK_NAME%" /xml "%TASK_XML%" /F >> "%UPGRADE_LOG%" 2>&1
if !ERRORLEVEL! NEQ 0 (
	call :WriteOut "Scheduled task was not created. Aborting before touching client."
	exit /b 1
)

schtasks /query /tn "%TASK_NAME%" >> "%UPGRADE_LOG%" 2>&1
if !ERRORLEVEL! NEQ 0 (
	call :WriteOut "Scheduled task could not be queried after creation. Aborting before touching client."
	exit /b 1
)

call :WriteOut "Scheduled task confirmed created."

REM We don't want to loop this process more than a few times, so we'll track it with a version-specific tracking file.
set "LOOP_FILE=%TEMP_DIR%\%~1_loop.txt"
echo 1 >> "%LOOP_FILE%"

set "attempts=0"
for /f "delims=;" %%c in ('powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -Command "if (Test-Path '%LOOP_FILE%') { (Get-Content '%LOOP_FILE%').Length } else { 0 }"') do set "attempts=%%c"
if !attempts! GEQ 4 (
	call :WriteOut "Retried !attempts! times. Manual investigation recommended."
)

call :start_worker_with_retries
if !ERRORLEVEL! NEQ 0 exit /b 1

call :wait_for_worker_interrupt
exit /b !ERRORLEVEL!

:start_worker_with_retries
set /a handoff_count=0

:start_worker_retry_loop
set /a handoff_count+=1

REM The registration trigger should run on create, but it does not always do so. Launch deliberately and verify handoff.
schtasks /run /tn "%TASK_NAME%" /i >> "%UPGRADE_LOG%" 2>&1
set "RUN_RESULT=!ERRORLEVEL!"
call :WriteOut "Scheduled task run requested, attempt !handoff_count! of %HANDOFF_RETRY_ATTEMPTS%, exit code !RUN_RESULT!."

call :wait_for_worker_start
if !ERRORLEVEL! EQU 0 exit /b 0

call :WriteOut "Scheduled task did not appear to start. Leaving %SERVICE_NAME% running and retrying handoff."

if !handoff_count! GEQ %HANDOFF_RETRY_ATTEMPTS% (
	call :WriteOut "Exit 1 - upgrade worker handoff was not confirmed after retries."
	exit /b 1
)

goto start_worker_retry_loop

:wait_for_worker_interrupt
set /a interrupt_waited=0

REM Hang out here so activation does not complete. The async worker should stop the client service and interrupt this script.
call :WriteOut "Waiting up to %WORKER_INTERRUPT_WAIT_SECONDS% seconds for scheduled upgrade worker to interrupt activation."

:wait_for_worker_interrupt_loop
if not exist "%LOCK_DIR%\created.txt" (
	call :WriteOut "Exit 1 - scheduled upgrade worker ended without interrupting activation."
	exit /b 1
)

if !interrupt_waited! GEQ %WORKER_INTERRUPT_WAIT_SECONDS% (
	call :WriteOut "Exit 1 - scheduled upgrade worker did not interrupt activation within %WORKER_INTERRUPT_WAIT_SECONDS% seconds."
	exit /b 1
)

timeout /t %WORKER_INTERRUPT_POLL_SECONDS% /nobreak >NUL
set /a interrupt_waited+=%WORKER_INTERRUPT_POLL_SECONDS%
goto wait_for_worker_interrupt_loop

:wait_for_worker_start
set /a wait_count=0

:wait_for_worker_start_loop
set /a wait_count+=1

if exist "%LOCK_DIR%\created.txt" (
	call :WriteOut "Worker lock detected."
	exit /b 0
)

if !wait_count! GEQ %TASK_START_ATTEMPTS% (
	exit /b 1
)

timeout /t %TASK_START_WAIT_SECONDS% /nobreak >NUL
goto wait_for_worker_start_loop

::----------------------------------------------------------------------------------------
:WriteOut <Message>
Set "Message=%~1"
echo "%DATE% %TIME% == %Message%" >> "%UPGRADE_LOG%"
exit /b
::----------------------------------------------------------------------------------------

:trimlog
set "TRIM_FILE=%~1"
powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -Command "$p='%TRIM_FILE%'; $max=10485760; if (Test-Path $p) { $i=Get-Item $p; if ($i.Length -gt $max) { $b=[System.IO.File]::ReadAllBytes($p); [System.IO.File]::WriteAllBytes($p, $b[($b.Length-$max)..($b.Length-1)]) } }"
exit /b 0
