param (
  [Parameter(Position=0, Mandatory=$true)]
  [string]$SourceDirPath,

  [Parameter(Position=1, Mandatory=$true)]
  [string]$SharedDirPath,

  [Parameter(Position=2, Mandatory=$true)]
  [string]$FilesToCopy
)

Write-Host "[SDataAppServer] INFO: Starting installUpdate.ps1 script"

# Fix all paths just in case
if (-not (Test-Path -Path $SourceDirPath)) {
  $SourceDirPath = (Get-Item -Path $SourceDirPath).FullName
  if (-not (Test-Path -Path $SourceDirPath)) {
    throw "[SDataAppServer] ERROR: Could not find directory '$SourceDirPath'"
  }
  Write-Host "[SDataAppServer] INFO: Fixed SourceDirPath: $SourceDirPath"
}

if (-not (Test-Path -Path $SharedDirPath)) {
  $SharedDirPath = (Get-Item -Path $SharedDirPath).FullName
  if (-not (Test-Path -Path $SharedDirPath)) {
    throw "[SDataAppServer] ERROR: Could not find directory '$SharedDirPath'"
  }
  Write-Host "[SDataAppServer] INFO: Fixed SharedDirPath: $SharedDirPath"
}

Write-Host "[SDataAppServer] INFO: SourceDirPath: $SourceDirPath"
Write-Host "[SDataAppServer] INFO: SharedDirPath: $SharedDirPath"
Write-Host "[SDataAppServer] INFO: FilesToCopy: $FilesToCopy"

$updateRequired = $false

$sourceOrderServiceConfigPath = Join-Path -Path $SourceDirPath -ChildPath "OrderService.config"
if (-not (Test-Path -Path $sourceOrderServiceConfigPath)) {
  throw "[SDataAppServer] ERROR: Could not find file '$sourceOrderServiceConfigPath'"
}
Write-Host "[SDataAppServer] INFO: Found Source OrderService.config file at '$sourceOrderServiceConfigPath'"

######################################################################
# Update Required Check:
# We check if we need to update the files by comparing the version of the source and shared OrderService.config files
# If the versions are equal
# If the versions, we need to update the files
# !!! Repair installations are currently not supported !!! (We don't have a way to distinguish between a repair and a normal installation)
######################################################################
$sharedOrderServiceConfigPath = Join-Path -Path $SharedDirPath -ChildPath "OrderService.config"
if (Test-Path -Path $sharedOrderServiceConfigPath) {
  $sourceOrderServiceConfig = [xml](Get-Content -Path $sourceOrderServiceConfigPath)
  $sourceOrderServiceVersion = $sourceOrderServiceConfig.configuration.orderServiceConfiguration.version
  Write-Host "[SDataAppServer] INFO: Source OrderService version is '$sourceOrderServiceVersion'"

  Write-Host "[SDataAppServer] INFO: Found Shared OrderService.config file at '$sharedOrderServiceConfigPath'"
  $sharedOrderServiceConfig = [xml](Get-Content -Path $sharedOrderServiceConfigPath)
  $sharedOrderServiceVersion = $sharedOrderServiceConfig.configuration.orderServiceConfiguration.version
  Write-Host "[SDataAppServer] INFO: Shared OrderService version is '$sharedOrderServiceVersion'"

  if ($sourceOrderServiceVersion -eq $sharedOrderServiceVersion) {
    Write-Host "[SDataAppServer] INFO: Source and Shared OrderService versions are equal, checking if the files are the same"

    $FilesToCheckList = ([System.String]$FilesToCopy).Split(";")
    Write-Host "[SDataAppServer] INFO: Checking $($FilesToCheckList.Count) files"

    foreach ($fileToCheck in $FilesToCheckList) {
      $file = ([System.String]$fileToCheck).Split("|")[0]

      $sourceFilePath = Join-Path -Path $SourceDirPath -ChildPath $file
      $sharedFilePath = Join-Path -Path $SharedDirPath -ChildPath $file

      if (-not (Test-Path -Path $sharedFilePath)) {
        Write-Host "[SDataAppServer] INFO: File '$sharedFilePath' does not exist, update required"
        $updateRequired = $true
      }
    }
  } else {
    Write-Host "[SDataAppServer] INFO: Source and Shared OrderService versions are not equal, update required"
    $updateRequired = $true
  }
} else {
  Write-Host "[SDataAppServer] INFO: No Shared OrderService.config file found, update required"
  $updateRequired = $true
}

if ($updateRequired) {
  Write-Host "[SDataAppServer] INFO: Update required, stopping service and copying files"
} else {
  Write-Host "[SDataAppServer] INFO: No update required, skipping"
  exit 0
}

######################################################################
# Find & Stop Sage AppServer service
######################################################################

$sageServiceName = "SagedeApplicationServerService"
$sageServices = Get-Service -Name "$sageServiceName*" -ErrorAction SilentlyContinue
if ($sageServices.Count -gt 1) {
  # TODO: What do we do here?
  throw "We could not find a proper service to stop, we found $($sageServices.Count) services, but expected 0-1. (Searched for '$sageServiceName*')"
}

if ($sageServices.Count -eq 1) {
  $sageService = $sageServices[0]

  Write-Host "[SDataAppServer] INFO: Found service $($sageService.Name) with status $($sageService.Status)"

  if ($sageService.Status -eq "Running") {
    Write-Host "[SDataAppServer] INFO: Stopping service $($sageService.Name)"
    Stop-Service -Name $sageService.Name -Force
  }

  # Check if a process with the "Sagede.ApplicationServer.WindowsService.exe" is still running
  $processes = Get-Process -Name "Sagede.ApplicationServer.WindowsService" -ErrorAction SilentlyContinue
  if ($processes.Count -gt 0) {
    Write-Host "[SDataAppServer] INFO: Found $($processes.Count) processes with name 'Sagede.ApplicationServer.WindowsService.exe', killing them"
    $processes | Stop-Process -Force -ErrorAction Stop
  }

  # Do the same for "Sagede.ApplicationServer.IsolationProcess.exe"
  $processes = Get-Process -Name "Sagede.ApplicationServer.IsolationProcess" -ErrorAction SilentlyContinue
  if ($processes.Count -gt 0) {
    Write-Host "[SDataAppServer] INFO: Found $($processes.Count) processes with name 'Sagede.ApplicationServer.IsolationProcess.exe', killing them"
    $processes | Stop-Process -Force
  }
}

######################################################################
# Find & Stop TaskService service
######################################################################

$taskServiceName = "ForSellers.TaskService"
$taskServiceServices = Get-Service -Name "$taskServiceName" -ErrorAction SilentlyContinue
if ($taskServiceServices.Count -gt 1) {
  throw "We could not find a proper service to stop, we found $($taskServiceServices.Count) services, but expected 0-1. (Searched for '$taskServiceName')"
}

if ($taskServiceServices.Count -eq 1) {
  $taskService = $taskServiceServices[0]

  Write-Host "[SDataAppServer] INFO: Found service $($taskService.Name) with status $($taskService.Status)"

  if ($taskService.Status -eq "Running") {
    Write-Host "[SDataAppServer] INFO: Stopping service $($taskService.Name)"
    Stop-Service -Name $taskService.Name -Force
  }

  # Check if a process with the "4Sellers.TaskService.exe" is still running
  $processes = Get-Process -Name "4Sellers.TaskService" -ErrorAction SilentlyContinue
  if ($processes.Count -gt 0) {
    Write-Host "[SDataAppServer] INFO: Found $($processes.Count) processes with name '4Sellers.TaskService.exe', killing them"
    $processes | Stop-Process -Force -ErrorAction Stop
  }

  # Check if a process with the "4sellers.TaskService.IsolationProcess.exe" is still running
  $processes = Get-Process -Name "4sellers.TaskService.IsolationProcess" -ErrorAction SilentlyContinue
  if ($processes.Count -gt 0) {
    Write-Host "[SDataAppServer] INFO: Found $($processes.Count) processes with name '4sellers.TaskService.IsolationProcess.exe', killing them"
    $processes | Stop-Process -Force -ErrorAction Stop
  }
}

######################################################################
# Find & Stop Cashline service
######################################################################

$cashLineServiceName = "Cashline Service"
$cashLineServices = Get-Service -Name "$cashLineServiceName" -ErrorAction SilentlyContinue
if ($cashLineServices.Count -gt 1) {
  throw "We could not find a proper service to stop, we found $($cashLineServices.Count) services, but expected 0-1. (Searched for '$cashLineServiceName')"
}
if ($cashLineServices.Count -eq 1) {
  $cashLineService = $cashLineServices[0]

  Write-Host "[SDataAppServer] INFO: Found service $($cashLineService.Name) with status $($cashLineService.Status)"

  if ($cashLineService.Status -eq "Running") {
    Write-Host "[SDataAppServer] INFO: Stopping service $($cashLineService.Name)"
    Stop-Service -Name $cashLineService.Name -Force
  }

  # Check if a process with the "CService.exe" is still running
  $processes = Get-Process -Name "CService" -ErrorAction SilentlyContinue
  if ($processes.Count -gt 0) {
    Write-Host "[SDataAppServer] INFO: Found $($processes.Count) processes with name 'CService.exe', killing them"
    $processes | Stop-Process -Force -ErrorAction Stop
  }
}

######################################################################
# Find & Stop Sage AppServer service
######################################################################

$abacusServiceName = "abacus AppServer"
$abacusServices = Get-Service -Name "$abacusServiceName" -ErrorAction SilentlyContinue
if ($abacusServices.Count -gt 1) {
  throw "We could not find a proper service to stop, we found $($abacusServices.Count) services, but expected 0-1. (Searched for '$abacusServiceName')"
}
if ($abacusServices.Count -eq 1) {
  $abacusService = $abacusServices[0]

  Write-Host "[SDataAppServer] INFO: Found service $($abacusService.Name) with status $($abacusService.Status)"

  if ($abacusService.Status -eq "Running") {
    Write-Host "[SDataAppServer] INFO: Stopping service $($abacusService.Name)"
    Stop-Service -Name $abacusService.Name -Force
  }

  # Check if a process with the "abacus.App.Server.WinService.exe" is still running
  $processes = Get-Process -Name "abacus.App.Server.WinService" -ErrorAction SilentlyContinue
  if ($processes.Count -gt 0) {
    Write-Host "[SDataAppServer] INFO: Found $($processes.Count) processes with name 'abacus.App.Server.WinService.exe', killing them"
    $processes | Stop-Process -Force -ErrorAction Stop
  }
}


######################################################################
# Copy files
######################################################################

$filesToCopyList = ([System.String]$FilesToCopy).Split(";")
Write-Host "[SDataAppServer] INFO: Trying to copy $($filesToCopyList.Count) files"

# DEBUG: list all directories, subdirectories and files in the source directory
# $sourceFiles = Get-ChildItem -Path $SourceDirPath -Recurse -Force
# Write-Host "$($sourceFiles)"

$system32Path = Join-Path -Path $env:SystemRoot -ChildPath "System32"
$regsvr32Path = Join-Path -Path $system32Path -ChildPath "regsvr32.exe"
if (-not (Test-Path -Path $regsvr32Path)) {
  throw "[SDataAppServer] ERROR: Could not find file '$regsvr32Path'"
}

# registeCom & registerNet logic taken from here:
# https://tfs.4sellers.de/tfs/Entwicklung/4SELLERS/_versionControl#path=%24%2F4SELLERS%2Flogicbase%2FInstaller%2FDevelopment%2Flogicbase.Installer.Common%2FInfrastructure%2FRunntimeInteropExtensions.cs
# https://tfs.4sellers.de/tfs/Entwicklung/4SELLERS/_versionControl?path=%24%2F4SELLERS%2Flogicbase%2FInstaller%2FMain%2Flogicbase.Installer.Common%2FInfrastructure%2FRegistrationService.cs
$netFrameworkRuntimeDirPath = [System.Runtime.InteropServices.RuntimeEnvironment]::GetRuntimeDirectory().Replace("Framework64", "Framework").TrimEnd("\")
# Get partent directory of the netFrameworkRuntimeDirPath
$netFrameworkRuntimeDirPath = $netFrameworkRuntimeDirPath.Substring(0, $netFrameworkRuntimeDirPath.LastIndexOf("\"))

# Copy the files
foreach ($fileToCopy in $filesToCopyList) {
  $split =([System.String]$fileToCopy).Split("|")
  $file = $split[0]

  $registerCom = $false
  $registerNet = $false

  foreach ($item in $split) {
    if ($item.ToLowerInvariant() -eq "registercom=true") {
      $registerCom = $true
    } elseif ($item.ToLowerInvariant() -eq "registernet=true") {
      $registerNet = $true
    }
  }

  $sourceFilePath = Join-Path -Path $SourceDirPath -ChildPath $file
  $destinationFilePath = Join-Path -Path $SharedDirPath -ChildPath $file

  # Check if the file exists
  if (-not (Test-Path -Path $sourceFilePath)) {
    throw "[SDataAppServer] ERROR: Could not find file '$sourceFilePath'"
  } else {
    Write-Host "[SDataAppServer] INFO: Copying file '$sourceFilePath' to '$destinationFilePath'"
    Copy-Item -Path $sourceFilePath -Destination $destinationFilePath -Force
  }

  if ($registerCom) {
    Write-Host "[SDataAppServer] INFO: Registering COM component '$destinationFilePath'"
    Start-Process -FilePath $regsvr32Path -ArgumentList "/s '$destinationFilePath'" -Wait
  }

  if ($registerNet) {
    $assembly = [System.Reflection.Assembly]::Load([System.IO.File]::ReadAllBytes($destinationFilePath))
    $assemblyRuntimeVersion = $assembly.ImageRuntimeVersion

    $assemblyRuntimeVersionPath = Join-Path -Path $netFrameworkRuntimeDirPath -ChildPath $assemblyRuntimeVersion
    if (-not (Test-Path -Path $assemblyRuntimeVersionPath)) {
      throw "[SDataAppServer] ERROR: Could not find directory '$assemblyRuntimeVersionPath'"
    }

    $regAsmPath = Join-Path -Path $assemblyRuntimeVersionPath -ChildPath "regasm.exe"
    if (-not (Test-Path -Path $regAsmPath)) {
      throw "[SDataAppServer] ERROR: Could not find file '$regAsmPath'"
    }

    Write-Host "[SDataAppServer] INFO: Registering .NET component '$destinationFilePath'"
    Start-Process -FilePath $regAsmPath -ArgumentList "/codebase /tlb '$destinationFilePath'" -Wait

    $tlbFilePath = $destinationFilePath -replace ".dll", ".tlb"
    if (-not (Test-Path -Path $tlbFilePath)) {
      throw "[SDataAppServer] ERROR: Could not find file '$tlbFilePath'"
    }

    Write-Host "[SDataAppServer] INFO: Registering .NET component '$tlbFilePath'"
  }
}

######################################################################
# Start the services again
######################################################################

if ($sageServices.Count -eq 1) {
  Write-Host "[SDataAppServer] INFO: Starting service $($sageService.Name)"
  Start-Service -Name $sageService.Name
}

if ($taskServiceServices.Count -eq 1) {
  Write-Host "[SDataAppServer] INFO: Starting service $($taskService.Name)"
  Start-Service -Name $taskService.Name
}

if ($cashLineServices.Count -eq 1) {
  Write-Host "[SDataAppServer] INFO: Starting service $($cashLineService.Name)"
  Start-Service -Name $cashLineService.Name
}

if ($abacusServices.Count -eq 1) {
  Write-Host "[SDataAppServer] INFO: Starting service $($abacusService.Name)"
  Start-Service -Name $abacusService.Name
}
