@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 "PS_HELPER=%TEMP_DIR%\upgradeClientHelper.ps1"
set "POWERSHELL_EXE=%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe"
if exist "%SystemRoot%\Sysnative\WindowsPowerShell\v1.0\powershell.exe" set "POWERSHELL_EXE=%SystemRoot%\Sysnative\WindowsPowerShell\v1.0\powershell.exe"
set "POWERSHELL_ARGS=-NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -File"
set "MSI_PATH=%TEMP_DIR%\FileWaveClient.msi"
set "UPGRADE_LOG=%TEMP_DIR%\upgradeClient.log"
set "FW_UPGRADE_LOG=%UPGRADE_LOG%"
set "FW_UPGRADE_HELPER_LOG=%TEMP_DIR%\upgradeClientHelper.log"
set "MSI_LOG=%TEMP_DIR%\FileWaveClient.log"
set "LOCK_DIR=%TEMP_DIR%\fwclient-upgrade.lock"
set "RESULT_FILE=%TEMP_DIR%\fwclient-upgrade-result.txt"
set "TARGET_VERSION_FILE=%TEMP_DIR%\fwclient-upgrade-target-version.txt"
set "SERVICE_NAME=FileWaveWinClient"
set "CLIENT_BIN_64=C:\Program Files\FileWave\client\fwcld.exe"
set "CLIENT_BIN_LEGACY_64=C:\Program Files\FileWave\fwcld.exe"
set "CLIENT_BIN_32=C:\Program Files (x86)\FileWave\fwcld.exe"
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

if not exist "%PS_HELPER%" (
	echo "%DATE% %TIME% == PowerShell helper not found at %PS_HELPER%. Exiting." >> "%UPGRADE_LOG%"
	exit /b 14
)

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%" %POWERSHELL_ARGS% "%PS_HELPER%" GetPackageName "FileWave Custom Client Upgrade (from 10.0.X)" 2^>^> "%UPGRADE_LOG%"') 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."
>"%TARGET_VERSION_FILE%" echo %~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%" %POWERSHELL_ARGS% "%PS_HELPER%" GetPackageVersion "FileWave Client" 2^>^> "%UPGRADE_LOG%"') do set "version=%%b"
if "%version%"=="none" call :read_fwcld_version

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."

if exist "%LOCK_DIR%\created.txt" (
	call :WriteOut "Existing worker lock detected before handoff. Waiting for current upgrade worker."
	call :wait_for_worker_interrupt
	exit /b !ERRORLEVEL!
)

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%" %POWERSHELL_ARGS% "%PS_HELPER%" CountLines "%LOOP_FILE%" 2^>^> "%UPGRADE_LOG%"') 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

del /f /q "%RESULT_FILE%" >NUL 2>&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 :exit_with_worker_result
	exit /b !ERRORLEVEL!
)

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

call :sleep %WORKER_INTERRUPT_POLL_SECONDS%
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
)

call :sleep %TASK_START_WAIT_SECONDS%
goto wait_for_worker_start_loop

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

:read_fwcld_version
set "FWCLD_VERSION="
call :read_fwcld_version_from "%CLIENT_BIN_64%"
if not defined FWCLD_VERSION call :read_fwcld_version_from "%CLIENT_BIN_LEGACY_64%"
if not defined FWCLD_VERSION call :read_fwcld_version_from "%CLIENT_BIN_32%"
if defined FWCLD_VERSION set "version=!FWCLD_VERSION!"
exit /b 0

:read_fwcld_version_from
set "FWCLD_PATH=%~1"
if not exist "%FWCLD_PATH%" exit /b 1
call :WriteOut "Reading installed FileWave Client version from %FWCLD_PATH% -V."
for /f "usebackq delims=" %%o in (`"%FWCLD_PATH%" -V 2^>^&1`) do (
	call :WriteOut "fwcld.exe -V output: %%o"
	for /f "tokens=2 delims= " %%v in ("%%o") do (
		if not defined FWCLD_VERSION set "FWCLD_VERSION=%%v"
	)
)
if defined FWCLD_VERSION call :WriteOut "Installed FileWave Client version from fwcld.exe is %FWCLD_VERSION%."
exit /b 0

:trimlog
set "TRIM_FILE=%~1"
"%POWERSHELL_EXE%" %POWERSHELL_ARGS% "%PS_HELPER%" TrimLog "%TRIM_FILE%" "10485760" 2>> "%UPGRADE_LOG%"
exit /b 0

:sleep
set /a pings=%~1 + 1
"%SystemRoot%\System32\ping.exe" 127.0.0.1 -n !pings! >NUL
exit /b 0

:exit_with_worker_result
set "WORKER_RESULT="
if exist "%RESULT_FILE%" (
	for /f "usebackq delims=" %%r in ("%RESULT_FILE%") do set "WORKER_RESULT=%%r"
)

if not defined WORKER_RESULT (
	call :WriteOut "Exit 1 - scheduled upgrade worker ended without writing a result."
	exit /b 1
)

call :WriteOut "Exit !WORKER_RESULT! - scheduled upgrade worker completed."
exit /b !WORKER_RESULT!
