@echo off
SETLOCAL EnableDelayedExpansion

REM ============================================================
REM  goteamviewer.bat
REM
REM  *** THIS SCRIPT MUST BE RUN AS ADMINISTRATOR ***   
REM
REM  Combined TeamViewer reassignment script - handles BOTH:
REM    - LOCAL installs  (run directly on the target machine,
REM                        e.g. a remote site with no network
REM                        path back to it)
REM    - REMOTE installs (run from an admin machine, targeting
REM                        another machine over the network via
REM                        PsExec)
REM  The admin is asked up front which mode applies, then the
REM  script follows the matching path.
REM
REM  The admin is also asked up front whether this run is an INSTALL
REM  (reassign/update, default) or a REMOVAL (strip TeamViewer off the
REM  machine completely, no reinstall) - Removal mode reuses the same
REM  Local/Remote plumbing but skips straight to :BUILD_TV_CLEANUP.
REM
REM  Fresh-install MSI handling differs by mode:
REM    - LOCAL : if TeamViewer_Host.msi is not next to this script it is
REM              downloaded from MSI_DOWNLOAD_URL onto THIS machine.
REM    - REMOTE: the helper downloads the MSI from MSI_DOWNLOAD_URL on the
REM              TARGET machine itself (nothing is copied over the network).
REM
REM  Requires (REMOTE mode only): PsExec.exe (Sysinternals) in
REM  the same folder or in PATH. If missing, the script offers to
REM  download it from live.sysinternals.com straight into the same
REM  folder as this script, and removes it again during cleanup if
REM  the operator opted into "Clean Up Downloaded Files?" AND this
REM  run is the one that downloaded it (a pre-existing copy placed
REM  here manually, or one already on PATH, is left alone).
REM
REM  Fresh Install now performs a comprehensive removal of any existing
REM  TeamViewer install (services, Program Files, ProgramData, HKLM +
REM  per-user registry, scheduled tasks, shortcuts) rather than a bare
REM  uninstall - the cleanup script is generated on the fly (see
REM  :BUILD_TV_CLEANUP below), so no extra companion file is needed.
REM
REM  Branding (CUSTOMCONFIGID) is now ALSO reapplied explicitly after every
REM  INSTALL via "TeamViewer.exe customize --id <id>" (TeamViewer KB109645),
REM  because the CUSTOMCONFIGID passed to msiexec at install time can
REM  silently fail to apply (no error, device just ends up with default
REM  Host branding) - this happens independently of whether the
REM  console-side assignment/grouping succeeded. This explicit customize
REM  step fixes that without needing a Fresh Install.
REM
REM  Fresh Install wipes the device's local TeamViewer ID, so on a machine
REM  that was already provisioned it will register as a brand-new device in
REM  the console, leaving the old device entry behind as a stale duplicate
REM  (this is a known TeamViewer platform behavior, not fixable purely from
REM  this script). The script now warns before doing this and logs the
REM  pre-existing TeamViewer ID so the duplicate can be found and removed
REM  from the console afterward.
REM ============================================================

REM ---- EDIT THIS ----
SET "CUSTOMCONFIGID=6apj46w"
SET "ASSIGNMENT_ID=0001CoABChApmRvQpeER8JtYvVzWhKXfEigIACAAAgAJAB7V6y0ANWO1TcZtwMUsYHsSJTDoFLhKuuomknPLMlVfGkAhPAC_7PPBJfYcrzUWcFyd8d70d2GVIqvyP-NDfgxZssywog1vRZxyp7kzZBKkYRblY1CH0GYN4KJ_EU7bd6vQIAEQs92omgk="
SET "MSI_DOWNLOAD_URL=https://rwthelp.com/_media/wiki:teamviewer:teamviewer_host.msi"
REM --------------------

GOTO :AFTERDEF

REM ============================================================
REM  :LOG subroutine - usage: call :LOG LEVEL "message text"
REM  LEVEL is one of INFO, SUCCESS, WARN, ERROR
REM ============================================================
:LOG
SET "LOGLEVEL=%~1"
SET "LOGMSG=%~2"
IF DEFINED LOGFILE echo %DATE% %TIME% [%LOGLEVEL%] %LOGMSG% >> "%LOGFILE%"
IF /I "%LOGLEVEL%"=="ERROR" (
    echo %C_ERROR%[ERROR] !LOGMSG!%C_RESET%
) ELSE IF /I "%LOGLEVEL%"=="WARN" (
    echo %C_WARN%[WARN]  !LOGMSG!%C_RESET%
) ELSE IF /I "%LOGLEVEL%"=="SUCCESS" (
    echo %C_OK%[OK]    !LOGMSG!%C_RESET%
) ELSE (
    echo %C_INFO%[INFO]  !LOGMSG!%C_RESET%
)
GOTO :EOF

REM ============================================================
REM  :CLEANUP_CREDS - REMOTE mode only. Removes the cached
REM  credential and any share session for the target. Called on
REM  every exit path so nothing lingers if the script errors out
REM  partway through.
REM ============================================================
:CLEANUP_CREDS
net use \\%COMPUTERNAME_TARGET%\C$ /delete /y >nul 2>&1
cmdkey /delete:%COMPUTERNAME_TARGET% >nul 2>&1
GOTO :EOF

REM ============================================================
REM  :GET_MSI - usage: call :GET_MSI "destination path"
REM  Downloads TeamViewer_Host.msi from MSI_DOWNLOAD_URL if it is
REM  not already present at the destination. No-op if it exists.
REM ============================================================
:GET_MSI
SET "MSI_DEST=%~1"
IF EXIST "%MSI_DEST%" GOTO :EOF
call :LOG INFO "TeamViewer_Host.msi not found locally - downloading from %MSI_DOWNLOAD_URL% ..."
powershell -NoProfile -Command "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; Invoke-WebRequest -Uri '%MSI_DOWNLOAD_URL%' -OutFile '%MSI_DEST%'"
IF EXIST "%MSI_DEST%" (
    SET "MSI_DOWNLOADED=1"
    call :LOG SUCCESS "Downloaded TeamViewer_Host.msi to %MSI_DEST%"
) ELSE (
    call :LOG ERROR "Download failed - could not obtain TeamViewer_Host.msi from %MSI_DOWNLOAD_URL%."
)
GOTO :EOF

REM ============================================================
REM  :ASK_YESNO - usage: call :ASK_YESNO ResultVar "Title" "Question" "Detail"
REM  Detail may be passed as "" if there is no second line needed.
REM  Sets ResultVar to YES or NO via a terminal Y/N prompt (default YES).
REM ============================================================
:ASK_YESNO
SET "AY_RESULTVAR=%~1"
SET "AY_TITLE=%~2"
SET "AY_QUESTION=%~3"
SET "AY_DETAIL=%~4"
echo.
IF NOT "!AY_TITLE!"=="" echo %C_H_FRESH%!AY_TITLE!%C_RESET%
IF NOT "!AY_QUESTION!"=="" echo !AY_QUESTION!
IF NOT "!AY_DETAIL!"=="" echo !AY_DETAIL!
echo Press %C_GREEN%Y%C_RESET% = Yes    %C_RED%N%C_RESET% = No   (default %C_GREEN%Y%C_RESET%)
SET "AY_ANSWER="
SET /P "AY_ANSWER=Y/N [Y]: "
IF /I "!AY_ANSWER!"=="N" (SET "%AY_RESULTVAR%=NO") ELSE (SET "%AY_RESULTVAR%=YES")
GOTO :EOF

REM ============================================================
REM  :ASK_TEXT - usage: call :ASK_TEXT ResultVar "Title" "Prompt" "Default"
REM  Default may be passed as "" for no default. Sets ResultVar to the
REM  entered text (blank input uses the default when one is provided).
REM ============================================================
:ASK_TEXT
SET "AT_RESULTVAR=%~1"
SET "AT_TITLE=%~2"
SET "AT_PROMPT=%~3"
SET "AT_DEFAULT=%~4"
SET "AT_ANSWER="
echo.
IF "!AT_DEFAULT!"=="" (
    SET /P "AT_ANSWER=!AT_PROMPT!: "
) ELSE (
    SET /P "AT_ANSWER=!AT_PROMPT! [!AT_DEFAULT!]: "
    IF "!AT_ANSWER!"=="" SET "AT_ANSWER=!AT_DEFAULT!"
)
SET "%AT_RESULTVAR%=!AT_ANSWER!"
GOTO :EOF

REM ============================================================
REM  :ASK_PASSWORD - usage: call :ASK_PASSWORD ResultVar "Prompt text"
REM  Sets ResultVar to the entered password. The input is masked right
REM  in this console window via PowerShell's Read-Host -AsSecureString.
REM ============================================================
:ASK_PASSWORD
SET "AP_RESULTVAR=%~1"
SET "AP_PROMPT=%~2"
FOR /F "usebackq delims=" %%A IN (`powershell -NoProfile -Command "$p = Read-Host -Prompt '!AP_PROMPT!' -AsSecureString; $b = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($p); [Runtime.InteropServices.Marshal]::PtrToStringBSTR($b)"`) DO SET "AP_ANSWER=%%A"
SET "%AP_RESULTVAR%=!AP_ANSWER!"
GOTO :EOF

REM ============================================================
REM  :TV_ASSIGNMENT_CHECK - usage: call :TV_ASSIGNMENT_CHECK <exitcode>
REM  Per TeamViewerMSI_ERROR_CODES.md, only 0 and 409 mean success
REM  (409 = device already managed with this assignment ID). Sets
REM  ASSIGNMENT_OK (1/0) and ASSIGNMENT_MEANING, and logs the result.
REM ============================================================
:TV_ASSIGNMENT_CHECK
SET "TV_RC=%~1"
SET "ASSIGNMENT_OK=0"
SET "ASSIGNMENT_MEANING=Unknown exit code - see TeamViewerMSI_ERROR_CODES.md"
IF "%TV_RC%"=="0" SET "ASSIGNMENT_OK=1"
IF "%TV_RC%"=="0" SET "ASSIGNMENT_MEANING=Assignment completed successfully"
IF "%TV_RC%"=="409" SET "ASSIGNMENT_OK=1"
IF "%TV_RC%"=="409" SET "ASSIGNMENT_MEANING=Device already managed - already assigned with this assignment ID"
IF "%TV_RC%"=="1" SET "ASSIGNMENT_MEANING=Invalid command line arguments"
IF "%TV_RC%"=="2" SET "ASSIGNMENT_MEANING=Signature verification error"
IF "%TV_RC%"=="3" SET "ASSIGNMENT_MEANING=Product not installed - install TeamViewer before assignment"
IF "%TV_RC%"=="4" SET "ASSIGNMENT_MEANING=Service config access failed - ComAPI - try again later"
IF "%TV_RC%"=="400" SET "ASSIGNMENT_MEANING=Invalid argument - verify the assignment ID"
IF "%TV_RC%"=="401" SET "ASSIGNMENT_MEANING=Service not running - verify the TeamViewer service"
IF "%TV_RC%"=="402" SET "ASSIGNMENT_MEANING=Service incompatible version - reinstall TeamViewer"
IF "%TV_RC%"=="403" SET "ASSIGNMENT_MEANING=Not online - check internet connectivity, consider --retries"
IF "%TV_RC%"=="404" SET "ASSIGNMENT_MEANING=Already running - another assignment process in progress"
IF "%TV_RC%"=="405" SET "ASSIGNMENT_MEANING=Timeout - retry, consider increasing --timeout"
IF "%TV_RC%"=="406" SET "ASSIGNMENT_MEANING=Failed for unknown reasons - save logs, contact TeamViewer support"
IF "%TV_RC%"=="407" SET "ASSIGNMENT_MEANING=Access denied - ensure local administrator rights"
IF "%TV_RC%"=="408" SET "ASSIGNMENT_MEANING=Denied by policy - disable policy in Management Console"
IF "%ASSIGNMENT_OK%"=="1" call :LOG SUCCESS "Assignment exit code %TV_RC%: %ASSIGNMENT_MEANING%"
IF NOT "%ASSIGNMENT_OK%"=="1" call :LOG ERROR "Assignment exit code %TV_RC%: %ASSIGNMENT_MEANING%"
GOTO :EOF

REM ============================================================
REM  :ASK_CLEANUP - usage: call :ASK_CLEANUP "location description"
REM  Asks whether to delete downloaded/temporary install files.
REM  Default is NO. Sets CLEAN_YN to YES or NO. ANSI-colored Y/N.
REM ============================================================
:ASK_CLEANUP
echo.
echo %C_H_CLEAN%Clean Up Downloaded Files?%C_RESET%
echo Delete MSI and temp files from %~1 when done?
echo Press %C_GREEN%Y%C_RESET% = Yes    %C_RED%N%C_RESET% = No   (default %C_RED%N%C_RESET%)
SET "CLEAN_YN=NO"
SET "CLEAN_ANSWER="
SET /P "CLEAN_ANSWER=Y/N [N]: "
IF /I "!CLEAN_ANSWER!"=="Y" SET "CLEAN_YN=YES"
GOTO :EOF

REM ============================================================
REM  :BUILD_TV_CLEANUP - usage: call :BUILD_TV_CLEANUP "destination path"
REM  Writes out a PowerShell script that comprehensively removes any
REM  existing TeamViewer install: services, processes, native uninstall,
REM  leftover MSI product entries (via msiexec), Program Files,
REM  ProgramData, HKLM registry, per-user registry for every profile
REM  (including profiles not currently logged in), scheduled tasks, and
REM  shortcuts. This runs before a Fresh Install so a leftover prior
REM  install can't leave the new install showing generic Host branding
REM  instead of the CUSTOMCONFIGID skin.
REM ============================================================
:BUILD_TV_CLEANUP
SET "TVC=%~1"
> "%TVC%" echo $ErrorActionPreference = 'SilentlyContinue'
>> "%TVC%" echo Write-Output 'STATUS: Stopping TeamViewer services...'
>> "%TVC%" echo Get-Service -Name 'TeamViewer*' ^| Stop-Service -Force
>> "%TVC%" echo Write-Output 'STATUS: Killing TeamViewer processes...'
>> "%TVC%" echo foreach ($p in 'TeamViewer','TeamViewer_Service','TeamViewer_Desktop','tv_w32','tv_x64','TeamViewerQS','TeamViewer_Host') {
>> "%TVC%" echo     Stop-Process -Name $p -Force
>> "%TVC%" echo }
>> "%TVC%" echo Write-Output 'STATUS: Running native TeamViewer uninstallers...'
>> "%TVC%" echo foreach ($dir in 'C:\Program Files\TeamViewer', 'C:\Program Files (x86)\TeamViewer') {
>> "%TVC%" echo     $u = Join-Path $dir 'uninstall.exe'
>> "%TVC%" echo     if (Test-Path $u) { Start-Process -FilePath $u -ArgumentList '/S' -Wait }
>> "%TVC%" echo }
>> "%TVC%" echo Start-Sleep -Seconds 8
>> "%TVC%" echo Write-Output 'STATUS: Removing remaining TeamViewer MSI product entries via msiexec...'
>> "%TVC%" echo $uninstallRoots = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*', 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
>> "%TVC%" echo Get-ItemProperty -Path $uninstallRoots ^| Where-Object { $_.DisplayName -like 'TeamViewer*' } ^| ForEach-Object {
>> "%TVC%" echo     if ($_.PSChildName -match '^^\{[0-9A-Fa-f-]{36}\}$') {
>> "%TVC%" echo         Write-Output "STATUS: msiexec /x $($_.PSChildName) ($($_.DisplayName))"
>> "%TVC%" echo         Start-Process -FilePath 'msiexec.exe' -ArgumentList @('/x', $_.PSChildName, '/qn', '/norestart') -Wait
>> "%TVC%" echo     }
>> "%TVC%" echo }
>> "%TVC%" echo Write-Output 'STATUS: Stopping and deleting any remaining TeamViewer services...'
>> "%TVC%" echo Get-CimInstance Win32_Service ^| Where-Object { $_.Name -like 'TeamViewer*' } ^| ForEach-Object {
>> "%TVC%" echo     sc.exe stop $_.Name ^| Out-Null
>> "%TVC%" echo     sc.exe delete $_.Name ^| Out-Null
>> "%TVC%" echo }
>> "%TVC%" echo Write-Output 'STATUS: Removing TeamViewer program and data folders...'
>> "%TVC%" echo foreach ($dir in 'C:\Program Files\TeamViewer', 'C:\Program Files (x86)\TeamViewer', 'C:\ProgramData\TeamViewer') {
>> "%TVC%" echo     if (Test-Path $dir) { Remove-Item -Path $dir -Recurse -Force }
>> "%TVC%" echo }
>> "%TVC%" echo Write-Output 'STATUS: Removing machine-wide TeamViewer registry keys...'
>> "%TVC%" echo foreach ($key in 'HKLM:\SOFTWARE\TeamViewer', 'HKLM:\SOFTWARE\WOW6432Node\TeamViewer') {
>> "%TVC%" echo     if (Test-Path $key) { Remove-Item -Path $key -Recurse -Force }
>> "%TVC%" echo }
>> "%TVC%" echo Write-Output 'STATUS: Removing per-user TeamViewer registry keys (all profiles)...'
>> "%TVC%" echo Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\*' ^| ForEach-Object {
>> "%TVC%" echo     $sid = $_.PSChildName
>> "%TVC%" echo     $profilePath = $_.ProfileImagePath
>> "%TVC%" echo     if ($sid -notmatch '^^S-1-5-21-') { return }
>> "%TVC%" echo     if (-not $profilePath -or -not (Test-Path $profilePath)) { return }
>> "%TVC%" echo     if (Test-Path "Registry::HKEY_USERS\$sid") {
>> "%TVC%" echo         $loadedKey = "Registry::HKEY_USERS\$sid\SOFTWARE\TeamViewer"
>> "%TVC%" echo         if (Test-Path $loadedKey) { Remove-Item -Path $loadedKey -Recurse -Force }
>> "%TVC%" echo         return
>> "%TVC%" echo     }
>> "%TVC%" echo     $ntUser = Join-Path $profilePath 'NTUSER.DAT'
>> "%TVC%" echo     if (-not (Test-Path $ntUser)) { return }
>> "%TVC%" echo     reg.exe load 'HKU\TVCleanupTemp' "$ntUser" ^| Out-Null
>> "%TVC%" echo     $tempKey = 'Registry::HKEY_USERS\TVCleanupTemp\SOFTWARE\TeamViewer'
>> "%TVC%" echo     if (Test-Path $tempKey) { Remove-Item -Path $tempKey -Recurse -Force }
>> "%TVC%" echo     [gc]::Collect()
>> "%TVC%" echo     reg.exe unload 'HKU\TVCleanupTemp' ^| Out-Null
>> "%TVC%" echo }
>> "%TVC%" echo Write-Output 'STATUS: Removing TeamViewer scheduled tasks...'
>> "%TVC%" echo Get-ScheduledTask -TaskName 'TeamViewer*' ^| Unregister-ScheduledTask -Confirm:$false
>> "%TVC%" echo Write-Output 'STATUS: Removing TeamViewer shortcuts...'
>> "%TVC%" echo foreach ($pattern in 'C:\Users\*\Desktop\TeamViewer*.lnk', 'C:\Users\*\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\TeamViewer*.lnk', 'C:\ProgramData\Microsoft\Windows\Start Menu\Programs\TeamViewer*.lnk') {
>> "%TVC%" echo     Get-ChildItem -Path $pattern ^| Remove-Item -Force
>> "%TVC%" echo }
>> "%TVC%" echo Write-Output 'STATUS: TeamViewer removal complete.'
GOTO :EOF

:AFTERDEF

REM ---------- Set up native ANSI color codes (VT escape sequences; Windows 10+) ----------
for /f %%a in ('echo prompt $E ^| cmd') do set "ESC=%%a"
set "C_RESET=%ESC%[0m"
set "C_INFO=%ESC%[36m"
set "C_WARN=%ESC%[33m"
set "C_ERROR=%ESC%[91m"
set "C_OK=%ESC%[32m"
set "C_GREEN=%ESC%[32m"
set "C_YELLOW=%ESC%[33m"
set "C_RED=%ESC%[91m"
set "C_CYAN=%ESC%[36m"
set "C_BORDER=%ESC%[90m"
REM  Distinct header colors for the Local/Remote, Fresh, and Clean Up questions
set "C_H_MODE=%ESC%[95m"
set "C_H_FRESH=%ESC%[94m"
set "C_H_CLEAN=%ESC%[38;5;205m"
REM  Device Alias prompt colors: aqua label, light-green default name
set "C_AQUA=%ESC%[38;5;51m"
set "C_LGREEN=%ESC%[38;5;120m"

echo %C_BORDER%============================================================%C_RESET%
echo  %C_CYAN%TeamViewer%C_RESET% %C_GREEN%Installer%C_RESET% / %C_YELLOW%Updater%C_RESET% / %C_RED%Removal%C_RESET%
echo %C_BORDER%============================================================%C_RESET%
echo %C_WARN%NOTE: This script must be run as Administrator.%C_RESET%

REM ---------- Ask which action: INSTALL or REMOVAL ----------
echo.
echo %C_H_MODE%Install or Removal?%C_RESET%
echo Press %C_GREEN%I%C_RESET% = %C_GREEN%INSTALL%C_RESET% (install/reassign TeamViewer)    %C_RED%R%C_RESET% = %C_RED%REMOVAL%C_RESET% (completely remove TeamViewer, no reinstall)   (default %C_GREEN%I%C_RESET%)
SET "ACTION_ANSWER="
SET /P "ACTION_ANSWER=I/R [I]: "
IF /I "!ACTION_ANSWER!"=="R" (SET "ACTION_MODE=REMOVAL") ELSE (SET "ACTION_MODE=INSTALL")

REM ---------- Ask which mode: LOCAL or REMOTE ----------
echo.
echo %C_H_MODE%Local or Remote %ACTION_MODE%?%C_RESET%
echo Press %C_GREEN%L%C_RESET% = %C_GREEN%LOCAL%C_RESET% (this machine)    %C_YELLOW%R%C_RESET% = %C_YELLOW%REMOTE%C_RESET% (another machine via PsExec)   (default %C_YELLOW%R%C_RESET%)
SET "MODE_ANSWER="
SET /P "MODE_ANSWER=L/R [R]: "
IF /I "!MODE_ANSWER!"=="L" (SET "INSTALL_MODE=LOCAL") ELSE (SET "INSTALL_MODE=REMOTE")

IF /I "%INSTALL_MODE%"=="LOCAL" GOTO :LOCAL_FLOW
GOTO :REMOTE_FLOW

REM ============================================================
REM  LOCAL FLOW - runs all steps directly on this machine.
REM  No PsExec, no network shares, no credentials required.
REM ============================================================
:LOCAL_FLOW

REM ---------- Pre-flight: confirm running elevated ----------
net session >nul 2>&1
IF ERRORLEVEL 1 (
    echo %C_ERROR%[ERROR] This script must be run as Administrator. Right-click it and choose Run as administrator.%C_RESET%
    goto :END
)

REM ---------- Detect an existing TeamViewer install so we can warn about duplicate-device risk ----------
SET "PRIOR_TVID="
sc query TeamViewer >nul 2>&1
IF NOT ERRORLEVEL 1 (
    FOR /F "usebackq tokens=3" %%V IN (`reg query "HKLM\SOFTWARE\TeamViewer" /v ClientID 2^>nul ^| find "ClientID"`) DO SET "PRIOR_TVID=%%V"
    IF NOT DEFINED PRIOR_TVID FOR /F "usebackq tokens=3" %%V IN (`reg query "HKLM\SOFTWARE\Wow6432Node\TeamViewer" /v ClientID 2^>nul ^| find "ClientID"`) DO SET "PRIOR_TVID=%%V"
)

REM ---------- Ask whether a fresh install is wanted (INSTALL mode only; Removal mode always cleans) ----------
SET "DO_FRESH_INSTALL=0"
IF /I "%ACTION_MODE%"=="INSTALL" (
    IF DEFINED PRIOR_TVID (
        echo.
        echo %C_WARN%[WARN]  TeamViewer is already installed here ^(existing TeamViewer ID: !PRIOR_TVID!^).%C_RESET%
        echo %C_WARN%[WARN]  Fresh Install wipes that ID and registers a NEW device in the console -%C_RESET%
        echo %C_WARN%[WARN]  the ID above will be left behind as a stale/duplicate entry unless you%C_RESET%
        echo %C_WARN%[WARN]  remove it manually afterward. The branding fix now runs on every install,%C_RESET%
        echo %C_WARN%[WARN]  so Fresh Install is usually not needed just to correct a wrong logo.%C_RESET%
    )
    call :ASK_YESNO FRESH_YN "Fresh Install?" "" "This will uninstall any existing TeamViewer first."
    IF /I "!FRESH_YN!"=="YES" SET "DO_FRESH_INSTALL=1"
) ELSE (
    SET "DO_FRESH_INSTALL=1"
)

REM ---------- Ask for the device alias (leave blank to use this computer's name) - INSTALL mode only ----------
IF /I "%ACTION_MODE%"=="INSTALL" (
    call :ASK_TEXT DEVICE_ALIAS "TeamViewer Reassignment" "%C_AQUA%Device Alias%C_RESET% (default is %C_LGREEN%%COMPUTERNAME%%C_RESET%)" ""
    IF NOT DEFINED DEVICE_ALIAS SET "DEVICE_ALIAS=%COMPUTERNAME%"
)

REM ---------- Ask up front whether to clean downloaded files when finished ----------
call :ASK_CLEANUP "this machine (%COMPUTERNAME%)"

FOR /F "usebackq delims=" %%T IN (`powershell -NoProfile -Command "Get-Date -Format 'yyyyMMdd_HHmmss'"`) DO SET "TIMESTAMP=%%T"
SET "LOGFILE=%~dp0TeamViewer_%ACTION_MODE%_%COMPUTERNAME%_%TIMESTAMP%.log"

echo ===== Starting local TeamViewer %ACTION_MODE% on %COMPUTERNAME% ===== > "%LOGFILE%"
call :LOG INFO "Script started locally on %COMPUTERNAME%, mode: %ACTION_MODE%"
IF DEFINED PRIOR_TVID call :LOG WARN "Pre-existing TeamViewer ID before this run: %PRIOR_TVID% (check the console for a duplicate device if Fresh Install was used)"

IF /I "%ACTION_MODE%"=="INSTALL" IF "%DO_FRESH_INSTALL%"=="1" (
    call :GET_MSI "%~dp0TeamViewer_Host.msi"
    IF NOT EXIST "%~dp0TeamViewer_Host.msi" (
        call :LOG ERROR "TeamViewer_Host.msi not available (not found next to script and download failed). Aborting."
        goto :END
    )
)

IF "%DO_FRESH_INSTALL%"=="1" GOTO L_DOFRESHINSTALL
GOTO L_LOCATEEXE

:L_DOFRESHINSTALL
call :LOG INFO "Removing any existing TeamViewer installation (services, files, registry, per-user config)..."
SET "TVCLEANUP_PS1=%TEMP%\TV_Cleanup_%RANDOM%.ps1"
call :BUILD_TV_CLEANUP "%TVCLEANUP_PS1%"
SET "TVCLEAN_OUT=%TEMP%\tv_cleanup_output_%RANDOM%.log"
powershell -NoProfile -ExecutionPolicy Bypass -File "%TVCLEANUP_PS1%" > "%TVCLEAN_OUT%" 2>&1
FOR /F "usebackq delims=" %%L IN ("%TVCLEAN_OUT%") DO (
    SET "LINE=%%L"
    IF /I "!LINE:~0,7!"=="STATUS:" call :LOG INFO "!LINE:~7!"
    IF /I "!LINE:~0,6!"=="ERROR:" call :LOG ERROR "!LINE:~6!"
    IF /I "!LINE:~0,8!"=="WARNING:" call :LOG WARN "!LINE:~8!"
)
del "%TVCLEAN_OUT%" >nul 2>&1
del "%TVCLEANUP_PS1%" >nul 2>&1

IF /I "%ACTION_MODE%"=="REMOVAL" (
    call :LOG SUCCESS "===== TeamViewer removal completed on %COMPUTERNAME% ====="
    GOTO L_CLEANUP_AND_DONE
)

call :LOG INFO "Installing TeamViewer Host from MSI..."
msiexec.exe /i "%~dp0TeamViewer_Host.msi" /qn CUSTOMCONFIGID=%CUSTOMCONFIGID% INSTALLDRIVERS=0 INSTALLPRINTER=0 INSTALLVPN=0 /l*v "%~dp0tv_install.log"
ping -n 6 127.0.0.1 >nul

:L_LOCATEEXE
SET "TVEXE="
IF EXIST "C:\Program Files\TeamViewer\TeamViewer.exe" SET "TVEXE=C:\Program Files\TeamViewer\TeamViewer.exe"
IF NOT DEFINED TVEXE IF EXIST "C:\Program Files (x86)\TeamViewer\TeamViewer.exe" SET "TVEXE=C:\Program Files (x86)\TeamViewer\TeamViewer.exe"
IF DEFINED TVEXE GOTO L_FOUNDEXE
call :LOG ERROR "TeamViewer.exe not found."
goto :END

:L_FOUNDEXE
call :LOG INFO "Found TeamViewer.exe at %TVEXE%"
sc query TeamViewer | find "RUNNING" >nul
IF NOT ERRORLEVEL 1 GOTO L_SVCRUNNING
call :LOG INFO "Starting TeamViewer service..."
net start TeamViewer >nul 2>&1

:L_SVCRUNNING
SET WAITCOUNT=0
:L_WAITLOOP
sc query TeamViewer | find "RUNNING" >nul
IF NOT ERRORLEVEL 1 GOTO L_AFTERWAIT
SET /A WAITCOUNT=WAITCOUNT+1
IF %WAITCOUNT% GEQ 15 GOTO L_AFTERWAIT
ping -n 3 127.0.0.1 >nul
GOTO L_WAITLOOP

:L_AFTERWAIT
call :LOG INFO "Service check complete."

call :LOG INFO "Applying branding customization (module %CUSTOMCONFIGID%) via TeamViewer KB109645 method..."
SET "CUSTOMIZE_OUT=%TEMP%\tv_customize_%RANDOM%.log"
"%TVEXE%" customize --id %CUSTOMCONFIGID% > "%CUSTOMIZE_OUT%" 2>&1
SET "CUSTOMIZE_RC=%ERRORLEVEL%"
FOR /F "usebackq delims=" %%L IN ("%CUSTOMIZE_OUT%") DO (
    IF NOT "%%L"=="" call :LOG INFO "customize: %%L"
)
del "%CUSTOMIZE_OUT%" >nul 2>&1
IF "%CUSTOMIZE_RC%"=="0" (
    call :LOG SUCCESS "Branding customization applied."
) ELSE (
    call :LOG WARN "Branding customization returned exit code %CUSTOMIZE_RC% - logo/text may not be correct. See TeamViewer KB109645."
)

tasklist /FI "IMAGENAME eq TeamViewer.exe" | find /I "TeamViewer.exe" >nul
IF ERRORLEVEL 1 GOTO L_NOGUI
call :LOG INFO "Closing running TeamViewer GUI..."
taskkill /F /IM TeamViewer.exe /T >nul 2>&1
SET GUI_WAS_RUNNING=1
ping -n 3 127.0.0.1 >nul
GOTO L_GUICHECKDONE

:L_NOGUI
call :LOG INFO "No GUI running."
SET GUI_WAS_RUNNING=0

:L_GUICHECKDONE
call :LOG INFO "Running assignment with device alias: %DEVICE_ALIAS%"
"%TVEXE%" assignment --id %ASSIGNMENT_ID% --reassign --device-alias="%DEVICE_ALIAS%" --offline --retries=2 --timeout=5
SET "RUN_RC=%ERRORLEVEL%"
call :TV_ASSIGNMENT_CHECK %RUN_RC%

IF NOT "%GUI_WAS_RUNNING%"=="1" GOTO L_SKIPRELAUNCH
call :LOG INFO "Relaunching GUI..."
start "" "%TVEXE%"
:L_SKIPRELAUNCH

sc query TeamViewer | find "RUNNING" >nul
IF ERRORLEVEL 1 (
    call :LOG WARN "Service not running after operation!"
) ELSE (
    call :LOG SUCCESS "Service confirmed running after operation."
)

IF "%ASSIGNMENT_OK%"=="1" (
    call :LOG SUCCESS "===== Script completed successfully on %COMPUTERNAME% ====="
) ELSE (
    call :LOG ERROR "===== Script completed with errors on %COMPUTERNAME% (exit code %RUN_RC%) ====="
)

:L_CLEANUP_AND_DONE
REM ---------- Clean downloaded files off this machine (per the earlier prompt) ----------
IF /I "%CLEAN_YN%"=="YES" (
    call :LOG INFO "Cleaning downloaded files from this machine..."
    IF DEFINED MSI_DOWNLOADED del "%~dp0TeamViewer_Host.msi" >nul 2>&1
    del "%~dp0tv_install.log" >nul 2>&1
    call :LOG SUCCESS "Downloaded files removed."
)

GOTO :DONE

REM ============================================================
REM  REMOTE FLOW - runs from an admin machine, targeting another
REM  machine over the network via PsExec (same approach as the
REM  original reassign.bat).
REM ============================================================
:REMOTE_FLOW

REM ---------- Pre-flight: ensure PsExec is available (PATH, or next to this script) ----------
SET "PSEXEC_CMD=psexec"
SET "PSEXEC_DOWNLOADED=0"
where psexec >nul 2>&1
IF ERRORLEVEL 1 (
    IF EXIST "%~dp0PsExec.exe" (
        SET "PSEXEC_CMD=%~dp0PsExec.exe"
    ) ELSE (
        echo.
        echo %C_H_MODE%PsExec Not Found%C_RESET%
        echo PsExec.exe ^(Sysinternals^) is required for Remote mode and was not found in PATH or next to this script.
        echo Press %C_GREEN%Y%C_RESET% = Yes    %C_RED%N%C_RESET% = No   ^(default %C_GREEN%Y%C_RESET%^)
        SET "PSEXEC_ANSWER="
        SET /P "PSEXEC_ANSWER=Download it now to %~dp0PsExec.exe ? Y/N [Y]: "
        IF /I "!PSEXEC_ANSWER!"=="N" (
            call :LOG ERROR "PsExec.exe not available and download declined. Aborting."
            goto :END
        )
        call :LOG INFO "Downloading PsExec.exe from https://live.sysinternals.com/psexec.exe ..."
        powershell -NoProfile -Command "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; Invoke-WebRequest -Uri 'https://live.sysinternals.com/psexec.exe' -OutFile '%~dp0PsExec.exe'"
        IF NOT EXIST "%~dp0PsExec.exe" (
            call :LOG ERROR "Download failed - could not obtain PsExec.exe. Aborting."
            goto :END
        )
        call :LOG SUCCESS "Downloaded PsExec.exe to %~dp0PsExec.exe"
        SET "PSEXEC_CMD=%~dp0PsExec.exe"
        SET "PSEXEC_DOWNLOADED=1"
    )
)

REM ---------- Ask for target computer name ----------
call :ASK_TEXT COMPUTERNAME_TARGET "TeamViewer Reassignment" "Enter the target computer name or IP" ""

IF NOT DEFINED COMPUTERNAME_TARGET (
    call :LOG WARN "No computer name entered. Aborting."
    goto :END
)

REM ---------- Resolve target to an IPv4 address and confirm it is an internal/private IP ----------
SET "IP_STATUS="
SET "RESOLVED_IP="
FOR /F "usebackq tokens=1,2" %%A IN (`powershell -NoProfile -Command "$ErrorActionPreference='Stop'; try { $all=[System.Net.Dns]::GetHostAddresses('%COMPUTERNAME_TARGET%'); $ip=$null; foreach ($x in $all) { if ($x.AddressFamily -eq 'InterNetwork') { $ip=$x.IPAddressToString; break } }; if (-not $ip) { Write-Output 'UNRESOLVED' } else { $o=$ip.Split('.'); $a=[int]$o[0]; $b=[int]$o[1]; $internal = ($a -eq 10) -or ($a -eq 127) -or ($a -eq 169 -and $b -eq 254) -or ($a -eq 192 -and $b -eq 168) -or ($a -eq 172 -and $b -ge 16 -and $b -le 31); if ($internal) { Write-Output ('INTERNAL ' + $ip) } else { Write-Output ('EXTERNAL ' + $ip) } } } catch { Write-Output 'UNRESOLVED' }"`) DO (
    SET "IP_STATUS=%%A"
    SET "RESOLVED_IP=%%B"
)

IF /I "!IP_STATUS!"=="INTERNAL" (
    call :LOG SUCCESS "Target %COMPUTERNAME_TARGET% resolved to internal IP !RESOLVED_IP!"
) ELSE IF /I "!IP_STATUS!"=="EXTERNAL" (
    call :LOG ERROR "Target %COMPUTERNAME_TARGET% resolved to non-internal IP !RESOLVED_IP! - refusing to continue. Aborting."
    goto :END
) ELSE (
    call :LOG ERROR "Could not resolve %COMPUTERNAME_TARGET% to an IPv4 address. Aborting."
    goto :END
)

REM ---------- Ask whether a fresh install is wanted (INSTALL mode only; Removal mode always cleans) ----------
SET "DO_FRESH_INSTALL=0"
SET "HELPER_ALIAS=%%COMPUTERNAME%%"
IF /I "%ACTION_MODE%"=="INSTALL" (
    call :ASK_YESNO FRESH_YN "Fresh Install?" "" "This will uninstall any existing TeamViewer first."
    IF /I "!FRESH_YN!"=="YES" SET "DO_FRESH_INSTALL=1"

    REM ---- Ask for the device alias (leave blank to use the target's computer name) ----
    call :ASK_TEXT DEVICE_ALIAS "TeamViewer Reassignment" "%C_AQUA%Device Alias%C_RESET% (default is %C_LGREEN%%COMPUTERNAME_TARGET%%C_RESET%)" ""
    IF DEFINED DEVICE_ALIAS SET "HELPER_ALIAS=!DEVICE_ALIAS!"
) ELSE (
    SET "DO_FRESH_INSTALL=1"
)

REM ---------- Ask up front whether to clean downloaded files when finished ----------
call :ASK_CLEANUP "the remote machine %COMPUTERNAME_TARGET%"

REM  (No admin-side MSI download/copy for REMOTE - the helper downloads the
REM   MSI directly from MSI_DOWNLOAD_URL on the target machine itself.)

REM ---------- Ask for username (type it on the line underneath the prompt) ----------
echo.
echo Enter admin username for %COMPUTERNAME_TARGET%
echo   (local account, or DOMAIN\user - leave blank for .\Administrator)
SET "REMOTE_USER="
SET /P "REMOTE_USER=Username: "
IF NOT DEFINED REMOTE_USER SET "REMOTE_USER=.\Administrator"

REM ---------- Ask for password (masked in the terminal) ----------
call :ASK_PASSWORD REMOTE_PASS "Enter password for %REMOTE_USER% on %COMPUTERNAME_TARGET%"

IF NOT DEFINED REMOTE_PASS (
    call :LOG WARN "No password entered. Aborting."
    goto :END
)

FOR /F "usebackq delims=" %%T IN (`powershell -NoProfile -Command "Get-Date -Format 'yyyyMMdd_HHmmss'"`) DO SET "TIMESTAMP=%%T"
SET LOGFILE=%~dp0TeamViewer_%ACTION_MODE%_%COMPUTERNAME_TARGET%_%TIMESTAMP%.log
SET "HELPER_LOCAL=%TEMP%\reassign_helper.bat"
SET "TVCLEANUP_LOCAL=%TEMP%\TV_Cleanup.ps1"
SET "PSEXEC_OUT=%TEMP%\psexec_output.log"

echo ===== Starting TeamViewer %ACTION_MODE% on %COMPUTERNAME_TARGET% ===== > "%LOGFILE%"
call :LOG INFO "Script started - target: %COMPUTERNAME_TARGET%, mode: %ACTION_MODE%"

REM ---------- 1a. If fresh install, generate the comprehensive cleanup script locally first ----------
IF "%DO_FRESH_INSTALL%"=="1" call :BUILD_TV_CLEANUP "%TVCLEANUP_LOCAL%"

REM ---------- 1b. Build the remote helper .bat locally, line by line ----------
IF /I "%ACTION_MODE%"=="REMOVAL" GOTO R_BUILD_REMOVAL_HELPER
GOTO R_BUILD_INSTALL_HELPER

:R_BUILD_REMOVAL_HELPER
> "%HELPER_LOCAL%" echo @echo off
>> "%HELPER_LOCAL%" echo echo STATUS: Removing any existing TeamViewer installation (services, files, registry, per-user config)...
>> "%HELPER_LOCAL%" echo powershell -NoProfile -ExecutionPolicy Bypass -File "C:\Temp\TV_Cleanup.ps1"
>> "%HELPER_LOCAL%" echo EXIT /B 0
GOTO R_HELPER_BUILT

:R_BUILD_INSTALL_HELPER
> "%HELPER_LOCAL%" echo @echo off
>> "%HELPER_LOCAL%" echo SET DO_FRESH_INSTALL=%DO_FRESH_INSTALL%
>> "%HELPER_LOCAL%" echo IF "%%DO_FRESH_INSTALL%%"=="1" GOTO DOFRESHINSTALL
>> "%HELPER_LOCAL%" echo GOTO LOCATEEXE
>> "%HELPER_LOCAL%" echo :DOFRESHINSTALL
>> "%HELPER_LOCAL%" echo IF EXIST "C:\Temp\TeamViewer_Host.msi" echo STATUS: TeamViewer_Host.msi already present - skipping download.
>> "%HELPER_LOCAL%" echo IF NOT EXIST "C:\Temp\TeamViewer_Host.msi" echo STATUS: Downloading TeamViewer_Host.msi ~50MB from %MSI_DOWNLOAD_URL% ...
>> "%HELPER_LOCAL%" echo IF NOT EXIST "C:\Temp\TeamViewer_Host.msi" powershell -NoProfile -Command "[Net.ServicePointManager]::SecurityProtocol=[Net.SecurityProtocolType]::Tls12; Invoke-WebRequest -Uri '%MSI_DOWNLOAD_URL%' -OutFile 'C:\Temp\TeamViewer_Host.msi'"
>> "%HELPER_LOCAL%" echo IF NOT EXIST "C:\Temp\TeamViewer_Host.msi" echo ERROR: Download failed - could not obtain TeamViewer_Host.msi
>> "%HELPER_LOCAL%" echo IF NOT EXIST "C:\Temp\TeamViewer_Host.msi" exit /b 1
>> "%HELPER_LOCAL%" echo echo STATUS: Removing any existing TeamViewer installation (services, files, registry, per-user config)...
>> "%HELPER_LOCAL%" echo powershell -NoProfile -ExecutionPolicy Bypass -File "C:\Temp\TV_Cleanup.ps1"
>> "%HELPER_LOCAL%" echo echo STATUS: Installing TeamViewer Host from MSI...
>> "%HELPER_LOCAL%" echo msiexec.exe /i "C:\Temp\TeamViewer_Host.msi" /qn CUSTOMCONFIGID=%CUSTOMCONFIGID% INSTALLDRIVERS=0 INSTALLPRINTER=0 INSTALLVPN=0 /l*v "C:\Temp\tv_install.log"
>> "%HELPER_LOCAL%" echo ping -n 6 127.0.0.1 ^>nul
>> "%HELPER_LOCAL%" echo :LOCATEEXE
>> "%HELPER_LOCAL%" echo SET "TVEXE="
>> "%HELPER_LOCAL%" echo IF EXIST "C:\Program Files\TeamViewer\TeamViewer.exe" SET "TVEXE=C:\Program Files\TeamViewer\TeamViewer.exe"
>> "%HELPER_LOCAL%" echo IF NOT DEFINED TVEXE IF EXIST "C:\Program Files (x86)\TeamViewer\TeamViewer.exe" SET "TVEXE=C:\Program Files (x86)\TeamViewer\TeamViewer.exe"
>> "%HELPER_LOCAL%" echo IF DEFINED TVEXE GOTO FOUNDEXE
>> "%HELPER_LOCAL%" echo echo ERROR: TeamViewer.exe not found.
>> "%HELPER_LOCAL%" echo exit /b 1
>> "%HELPER_LOCAL%" echo :FOUNDEXE
>> "%HELPER_LOCAL%" echo echo STATUS: Found TeamViewer.exe at %%TVEXE%%
>> "%HELPER_LOCAL%" echo sc query TeamViewer ^| find "RUNNING" ^>nul
>> "%HELPER_LOCAL%" echo IF NOT ERRORLEVEL 1 GOTO SVCRUNNING
>> "%HELPER_LOCAL%" echo echo STATUS: Starting TeamViewer service...
>> "%HELPER_LOCAL%" echo net start TeamViewer ^>nul 2^>^&1
>> "%HELPER_LOCAL%" echo :SVCRUNNING
>> "%HELPER_LOCAL%" echo SET WAITCOUNT=0
>> "%HELPER_LOCAL%" echo :WAITLOOP
>> "%HELPER_LOCAL%" echo sc query TeamViewer ^| find "RUNNING" ^>nul
>> "%HELPER_LOCAL%" echo IF NOT ERRORLEVEL 1 GOTO AFTERWAIT
>> "%HELPER_LOCAL%" echo SET /A WAITCOUNT=WAITCOUNT+1
>> "%HELPER_LOCAL%" echo IF %%WAITCOUNT%% GEQ 15 GOTO AFTERWAIT
>> "%HELPER_LOCAL%" echo ping -n 3 127.0.0.1 ^>nul
>> "%HELPER_LOCAL%" echo GOTO WAITLOOP
>> "%HELPER_LOCAL%" echo :AFTERWAIT
>> "%HELPER_LOCAL%" echo echo STATUS: Service check complete.
>> "%HELPER_LOCAL%" echo echo STATUS: Applying branding customization (module %CUSTOMCONFIGID%) via TeamViewer KB109645 method...
>> "%HELPER_LOCAL%" echo "%%TVEXE%%" customize --id %CUSTOMCONFIGID%
>> "%HELPER_LOCAL%" echo SET CUSTOMIZE_RC=%%ERRORLEVEL%%
>> "%HELPER_LOCAL%" echo IF "%%CUSTOMIZE_RC%%"=="0" echo STATUS: Branding customization applied.
>> "%HELPER_LOCAL%" echo IF NOT "%%CUSTOMIZE_RC%%"=="0" echo WARNING: Branding customization returned exit code %%CUSTOMIZE_RC%% - logo/text may not be correct.
>> "%HELPER_LOCAL%" echo tasklist /FI "IMAGENAME eq TeamViewer.exe" ^| find /I "TeamViewer.exe" ^>nul
>> "%HELPER_LOCAL%" echo IF ERRORLEVEL 1 GOTO NOGUI
>> "%HELPER_LOCAL%" echo echo STATUS: Closing running TeamViewer GUI...
>> "%HELPER_LOCAL%" echo taskkill /F /IM TeamViewer.exe /T ^>nul 2^>^&1
>> "%HELPER_LOCAL%" echo SET GUI_WAS_RUNNING=1
>> "%HELPER_LOCAL%" echo ping -n 3 127.0.0.1 ^>nul
>> "%HELPER_LOCAL%" echo GOTO GUICHECKDONE
>> "%HELPER_LOCAL%" echo :NOGUI
>> "%HELPER_LOCAL%" echo echo STATUS: No GUI running.
>> "%HELPER_LOCAL%" echo SET GUI_WAS_RUNNING=0
>> "%HELPER_LOCAL%" echo :GUICHECKDONE
>> "%HELPER_LOCAL%" echo echo STATUS: Running assignment with device alias: !HELPER_ALIAS!
>> "%HELPER_LOCAL%" echo "%%TVEXE%%" assignment --id %ASSIGNMENT_ID% --reassign --device-alias="!HELPER_ALIAS!" --offline --retries=2 --timeout=5
>> "%HELPER_LOCAL%" echo SET ASSIGNMENT_RC=%%ERRORLEVEL%%
>> "%HELPER_LOCAL%" echo SET ASSIGNMENT_OK=0
>> "%HELPER_LOCAL%" echo SET ASSIGNMENT_MEANING=Unknown exit code - see TeamViewerMSI_ERROR_CODES.md
>> "%HELPER_LOCAL%" echo IF "%%ASSIGNMENT_RC%%"=="0" SET ASSIGNMENT_OK=1
>> "%HELPER_LOCAL%" echo IF "%%ASSIGNMENT_RC%%"=="0" SET ASSIGNMENT_MEANING=Assignment completed successfully
>> "%HELPER_LOCAL%" echo IF "%%ASSIGNMENT_RC%%"=="409" SET ASSIGNMENT_OK=1
>> "%HELPER_LOCAL%" echo IF "%%ASSIGNMENT_RC%%"=="409" SET ASSIGNMENT_MEANING=Device already managed - already assigned with this assignment ID
>> "%HELPER_LOCAL%" echo IF "%%ASSIGNMENT_RC%%"=="1" SET ASSIGNMENT_MEANING=Invalid command line arguments
>> "%HELPER_LOCAL%" echo IF "%%ASSIGNMENT_RC%%"=="2" SET ASSIGNMENT_MEANING=Signature verification error
>> "%HELPER_LOCAL%" echo IF "%%ASSIGNMENT_RC%%"=="3" SET ASSIGNMENT_MEANING=Product not installed
>> "%HELPER_LOCAL%" echo IF "%%ASSIGNMENT_RC%%"=="4" SET ASSIGNMENT_MEANING=Service config access failed - ComAPI - try again later
>> "%HELPER_LOCAL%" echo IF "%%ASSIGNMENT_RC%%"=="400" SET ASSIGNMENT_MEANING=Invalid argument - verify the assignment ID
>> "%HELPER_LOCAL%" echo IF "%%ASSIGNMENT_RC%%"=="401" SET ASSIGNMENT_MEANING=Service not running - verify the TeamViewer service
>> "%HELPER_LOCAL%" echo IF "%%ASSIGNMENT_RC%%"=="402" SET ASSIGNMENT_MEANING=Service incompatible version - reinstall TeamViewer
>> "%HELPER_LOCAL%" echo IF "%%ASSIGNMENT_RC%%"=="403" SET ASSIGNMENT_MEANING=Not online - check internet connectivity
>> "%HELPER_LOCAL%" echo IF "%%ASSIGNMENT_RC%%"=="404" SET ASSIGNMENT_MEANING=Already running - another assignment in progress
>> "%HELPER_LOCAL%" echo IF "%%ASSIGNMENT_RC%%"=="405" SET ASSIGNMENT_MEANING=Timeout - retry or increase --timeout
>> "%HELPER_LOCAL%" echo IF "%%ASSIGNMENT_RC%%"=="406" SET ASSIGNMENT_MEANING=Failed for unknown reasons - contact TeamViewer support
>> "%HELPER_LOCAL%" echo IF "%%ASSIGNMENT_RC%%"=="407" SET ASSIGNMENT_MEANING=Access denied - ensure local administrator rights
>> "%HELPER_LOCAL%" echo IF "%%ASSIGNMENT_RC%%"=="408" SET ASSIGNMENT_MEANING=Denied by policy - disable policy in Management Console
>> "%HELPER_LOCAL%" echo echo STATUS: Assignment exit code %%ASSIGNMENT_RC%%: %%ASSIGNMENT_MEANING%%
>> "%HELPER_LOCAL%" echo IF NOT "%%GUI_WAS_RUNNING%%"=="1" GOTO SKIPRELAUNCH
>> "%HELPER_LOCAL%" echo echo STATUS: Relaunching GUI...
>> "%HELPER_LOCAL%" echo start "" "%%TVEXE%%"
>> "%HELPER_LOCAL%" echo :SKIPRELAUNCH
>> "%HELPER_LOCAL%" echo sc query TeamViewer ^| find "RUNNING" ^>nul
>> "%HELPER_LOCAL%" echo IF ERRORLEVEL 1 echo WARNING: service not running after operation!
>> "%HELPER_LOCAL%" echo IF NOT ERRORLEVEL 1 echo STATUS: Service confirmed running after operation.
>> "%HELPER_LOCAL%" echo IF "%%ASSIGNMENT_OK%%"=="1" EXIT /B 0
>> "%HELPER_LOCAL%" echo EXIT /B 1

:R_HELPER_BUILT

REM ---------- 2. Cache the credential via cmdkey, then authenticate ----------
REM cmdkey stores the credential for this specific target in Windows'
REM Credential Manager. Once cached, "net use" and "psexec" both connect
REM WITHOUT ever putting the password on their own command line - Windows
REM transparently supplies the cached credential for that host. This keeps
REM the password out of process command-line listings (Task Manager,
REM Process Explorer, EDR logging) as well as out of this log file.
call :LOG INFO "Clearing any existing sessions/cached creds for %COMPUTERNAME_TARGET%..."
net use \\%COMPUTERNAME_TARGET%\C$ /delete /y >nul 2>&1
net use \\%COMPUTERNAME_TARGET%\IPC$ /delete /y >nul 2>&1
net use \\%COMPUTERNAME_TARGET% /delete /y >nul 2>&1
cmdkey /delete:%COMPUTERNAME_TARGET% >nul 2>&1

cmdkey /add:%COMPUTERNAME_TARGET% /user:%REMOTE_USER% /pass:%REMOTE_PASS% >nul 2>&1
SET "REMOTE_PASS="
REM Password variable cleared immediately after caching - it is not
REM referenced again anywhere else in this script from this point on.

call :LOG INFO "Connecting to \\%COMPUTERNAME_TARGET%\C$ ..."
net use \\%COMPUTERNAME_TARGET%\C$ /persistent:no >> "%LOGFILE%" 2>&1

dir "\\%COMPUTERNAME_TARGET%\C$\" >nul 2>&1
IF ERRORLEVEL 1 (
    call :LOG ERROR "Could not authenticate/access \\%COMPUTERNAME_TARGET%\C$ - check credentials/network. Aborting."
    call :CLEANUP_CREDS
    goto :END
)
call :LOG SUCCESS "Authenticated to \\%COMPUTERNAME_TARGET%\C$"

REM ---------- Detect an existing TeamViewer install on the target so we can warn about duplicate-device risk ----------
IF /I "%ACTION_MODE%"=="INSTALL" IF "%DO_FRESH_INSTALL%"=="1" (
    SET "PRIOR_TVID="
    FOR /F "usebackq tokens=3" %%V IN (`reg query "\\%COMPUTERNAME_TARGET%\HKLM\SOFTWARE\TeamViewer" /v ClientID 2^>nul ^| find "ClientID"`) DO SET "PRIOR_TVID=%%V"
    IF NOT DEFINED PRIOR_TVID FOR /F "usebackq tokens=3" %%V IN (`reg query "\\%COMPUTERNAME_TARGET%\HKLM\SOFTWARE\Wow6432Node\TeamViewer" /v ClientID 2^>nul ^| find "ClientID"`) DO SET "PRIOR_TVID=%%V"
    IF DEFINED PRIOR_TVID (
        call :LOG WARN "Target already has TeamViewer installed (existing TeamViewer ID: !PRIOR_TVID!). Fresh Install will register a NEW device in the console - check for/remove a stale duplicate with this ID afterward."
    )
)

IF NOT EXIST "\\%COMPUTERNAME_TARGET%\C$\Temp" mkdir "\\%COMPUTERNAME_TARGET%\C$\Temp"

REM  (The MSI is downloaded by the helper on the target itself - see the
REM   :DOFRESHINSTALL section above - so nothing is copied over here.)

REM ---------- 3. Copy the helper script to the target's C:\Temp ----------
copy /Y "%HELPER_LOCAL%" "\\%COMPUTERNAME_TARGET%\C$\Temp\reassign_helper.bat" >> "%LOGFILE%" 2>&1
IF NOT EXIST "\\%COMPUTERNAME_TARGET%\C$\Temp\reassign_helper.bat" (
    call :LOG ERROR "Could not copy helper script to target. Aborting."
    call :CLEANUP_CREDS
    goto :END
)

REM ---------- 3b. If fresh install, also copy the generated cleanup script to the target's C:\Temp ----------
IF "%DO_FRESH_INSTALL%"=="1" (
    copy /Y "%TVCLEANUP_LOCAL%" "\\%COMPUTERNAME_TARGET%\C$\Temp\TV_Cleanup.ps1" >> "%LOGFILE%" 2>&1
    IF NOT EXIST "\\%COMPUTERNAME_TARGET%\C$\Temp\TV_Cleanup.ps1" (
        call :LOG ERROR "Could not copy TV_Cleanup.ps1 to target. Aborting."
        call :CLEANUP_CREDS
        goto :END
    )
)

REM ---------- 4. If fresh install, tell the operator up front whether the target must download the ~50MB MSI ----------
IF /I "%ACTION_MODE%"=="INSTALL" IF "%DO_FRESH_INSTALL%"=="1" (
    IF EXIST "\\%COMPUTERNAME_TARGET%\C$\Temp\TeamViewer_Host.msi" (
        call :LOG INFO "TeamViewer_Host.msi already present on %COMPUTERNAME_TARGET% - no download needed."
    ) ELSE (
        call :LOG INFO "TeamViewer_Host.msi not on %COMPUTERNAME_TARGET% - target will download ~50MB from the URL first, this can take several minutes."
    )
)

REM ---------- 5. Run the helper script remotely via PsExec (no -u/-p needed - uses cached cmdkey credential) ----------
call :LOG INFO "Running TeamViewer %ACTION_MODE% helper on %COMPUTERNAME_TARGET% (this may take a minute)..."
"%PSEXEC_CMD%" \\%COMPUTERNAME_TARGET% -accepteula -nobanner -s cmd /c C:\Temp\reassign_helper.bat < nul > "%PSEXEC_OUT%" 2>&1
SET "RUN_RC=%ERRORLEVEL%"

echo Remote helper output: >> "%LOGFILE%"
type "%PSEXEC_OUT%" >> "%LOGFILE%"
echo End of remote helper output. >> "%LOGFILE%"

REM  Re-log the helper's STATUS/ERROR/WARNING lines by matching the line
REM  prefix - never echo the captured line itself, so nothing in the remote
REM  output can be executed as a command.
FOR /F "usebackq delims=" %%L IN ("%PSEXEC_OUT%") DO (
    SET "LINE=%%L"
    IF /I "!LINE:~0,7!"=="STATUS:" call :LOG INFO "!LINE:~7!"
    IF /I "!LINE:~0,6!"=="ERROR:" call :LOG ERROR "!LINE:~6!"
    IF /I "!LINE:~0,8!"=="WARNING:" call :LOG WARN "!LINE:~8!"
)

del "%PSEXEC_OUT%" >nul 2>&1

call :LOG INFO "Helper script exit code: %RUN_RC%"

REM ---------- 6. Final colored summary ----------
IF "%RUN_RC%"=="0" (
    call :LOG SUCCESS "===== Script completed successfully on %COMPUTERNAME_TARGET% ====="
) ELSE (
    call :LOG ERROR "===== Script completed with errors on %COMPUTERNAME_TARGET% (exit code %RUN_RC%) ====="
)

REM ---------- 7. Clean downloaded files off the target, per the earlier prompt (while still connected) ----------
IF /I "%CLEAN_YN%"=="YES" (
    call :LOG INFO "Cleaning downloaded files from %COMPUTERNAME_TARGET%..."
    del "\\%COMPUTERNAME_TARGET%\C$\Temp\TeamViewer_Host.msi" >nul 2>&1
    del "\\%COMPUTERNAME_TARGET%\C$\Temp\tv_install.log" >nul 2>&1
    del "\\%COMPUTERNAME_TARGET%\C$\Temp\reassign_helper.bat" >nul 2>&1
    del "\\%COMPUTERNAME_TARGET%\C$\Temp\TV_Cleanup.ps1" >nul 2>&1
    call :LOG SUCCESS "Downloaded files removed from %COMPUTERNAME_TARGET%."
)

REM ---------- 8. Clean up creds and local temp helper ----------
call :CLEANUP_CREDS
del "%HELPER_LOCAL%" >nul 2>&1
IF DEFINED TVCLEANUP_LOCAL del "%TVCLEANUP_LOCAL%" >nul 2>&1

REM ---------- 9. Remove PsExec.exe from this machine, but only if THIS run downloaded it ----------
IF /I "%CLEAN_YN%"=="YES" IF "%PSEXEC_DOWNLOADED%"=="1" (
    call :LOG INFO "Removing downloaded PsExec.exe from this machine..."
    del "%~dp0PsExec.exe" >nul 2>&1
    call :LOG SUCCESS "PsExec.exe removed."
)

GOTO :DONE

:DONE
call :LOG INFO "Full log: %LOGFILE%"

:END
ENDLOCAL
pause
