###############################################################################
#                                                                             #
#   File name       dw-service.ps1                                            #
#                                                                             #
#   Description     Enhanced DirectoryWatcher Windows Service                 #
#                   Monitors DirectoryWatcher logs and errors                 #
#                                                                             #
#   Version         1.1.0 - Added DirectoryWatcher log monitoring            #
#                                                                             #
###############################################################################

#Requires -version 2

[CmdletBinding(DefaultParameterSetName='Status')]
Param(
  [Parameter(ParameterSetName='Start', Mandatory=$true)]
  [Switch]$Start,
  [Parameter(ParameterSetName='Stop', Mandatory=$true)]
  [Switch]$Stop,
  [Parameter(ParameterSetName='Restart', Mandatory=$true)]
  [Switch]$Restart,
  [Parameter(ParameterSetName='Status', Mandatory=$false)]
  [Switch]$Status = $($PSCmdlet.ParameterSetName -eq 'Status'),
  [Parameter(ParameterSetName='Setup', Mandatory=$true)]
  [Parameter(ParameterSetName='Setup2', Mandatory=$true)]
  [Switch]$Setup,
  [Parameter(ParameterSetName='Setup', Mandatory=$true)]
  [String]$UserName,
  [Parameter(ParameterSetName='Setup', Mandatory=$false)]
  [String]$Password,
  [Parameter(ParameterSetName='Setup2', Mandatory=$false)]
  [System.Management.Automation.PSCredential]$Credential,
  [Parameter(ParameterSetName='Remove', Mandatory=$true)]
  [Switch]$Remove,
  [Parameter(ParameterSetName='Service', Mandatory=$true)]
  [Switch]$Service,
  [Parameter(ParameterSetName='SCMStart', Mandatory=$true)]
  [Switch]$SCMStart,
  [Parameter(ParameterSetName='SCMStop', Mandatory=$true)]
  [Switch]$SCMStop,
  [Parameter(ParameterSetName='SCMPreShutdown', Mandatory=$true)]
  [Switch]$SCMPreShutdown,
  [Parameter(ParameterSetName='Control', Mandatory=$true)]
  [String]$Control = $null,
  [Parameter(ParameterSetName='Version', Mandatory=$true)]
  [Switch]$Version
)

$scriptVersion = "1.1.0"

# Script identification
$argv0 = Get-Item $MyInvocation.MyCommand.Definition
$script = $argv0.basename
$scriptName = $argv0.name
$scriptFullName = $argv0.fullname

# Global settings
$serviceName = "DirectoryWatcherService"
$serviceDisplayName = "DirectoryWatcher Monitor Service"
$ServiceDescription = "Monitors and manages DirectoryWatcher Java process"
$pipeName = "Service_$serviceName"
$installDir = "${ENV:windir}\System32"
$scriptCopy = "$installDir\$scriptName"
$exeName = "$serviceName.exe"
$exeFullName = "$installDir\$exeName"
$logDir = ""  # Will be set dynamically based on AM_HOME
$logFile = ""  # Will be set dynamically based on AM_HOME
$logName = "Application"
$stopOnPreShutdown = 1

# DirectoryWatcher specific settings
$dwMonitorInterval = 5          # seconds between DirectoryWatcher checks
$dwLogMonitorInterval = 10       # seconds between log checks
$dwStartupTimeout = 5           # seconds to wait after starting DirectoryWatcher
$dwMaxRestartAttempts = 3        # maximum restart attempts
$dwLogTailLines = 50             # number of log lines to monitor for errors

# Smart logging settings to reduce log volume
$smartLogging = $true            # Enable smart logging (only log state changes) - set to $false to restore original behavior
$logStatusInterval = 300         # Log status only every 5 minutes (300 seconds) when everything is normal
$lastProcessStatusLog = 0        # Track when we last logged process status
$lastLogHealthLog = 0            # Track when we last logged log health
$lastProcessState = $null        # Track last known process state (PID or $null)
$lastLogHealthState = $null      # Track last known log health state

# DirectoryWatcher log paths (will be set dynamically)
$dwLogFile = ""                  # $AM_HOME/logs/directorywatcher.log
$dwErrorFile = ""                # $AM_HOME/logs/directorywatcher.err

#-----------------------------------------------------------------------------#
#                          Log Path Initialization                           #
#-----------------------------------------------------------------------------#

Function Initialize-ServiceLogPaths {
  # Determine AM_HOME path
  $amHome = $env:AM_HOME
  if (!$amHome) {
    $muleHome = $env:MULE_HOME
    if ($muleHome) {
      $amHome = Join-Path $muleHome "am"
      $env:AM_HOME = $amHome
    } else {
      # Fallback to Windows Logs if AM_HOME cannot be determined
      Write-Warning "AM_HOME and MULE_HOME not set, falling back to Windows system logs"
      $script:logDir = "${ENV:windir}\Logs"
      $script:logFile = "$script:logDir\$serviceName.log"
      return
    }
  }

  # Set service log paths under AM_HOME
  $script:logDir = Join-Path $amHome "logs"
  $script:logFile = "$script:logDir\$serviceName.log"
  
  # Ensure logs directory exists
  if (!(Test-Path $script:logDir)) {
    New-Item -ItemType Directory -Path $script:logDir -Force | Out-Null
  }
}

# Display version and exit if requested
if ($Version) {
  Write-Output $scriptVersion
  return
}

# Initialize service log paths based on AM_HOME
Initialize-ServiceLogPaths

#-----------------------------------------------------------------------------#
#                          Utility Functions                                 #
#-----------------------------------------------------------------------------#

Function Now {
  Param (
    [Switch]$ms,
    [Switch]$ns
  )
  $Date = Get-Date
  $now = ""
  $now += "{0:0000}-{1:00}-{2:00} " -f $Date.Year, $Date.Month, $Date.Day
  $now += "{0:00}:{1:00}:{2:00}" -f $Date.Hour, $Date.Minute, $Date.Second
  $nsSuffix = ""
  if ($ns) {
    if ("$($Date.TimeOfDay)" -match "\.\d\d\d\d\d\d") {
      $now += $matches[0]
      $ms = $false
    } else {
      $ms = $true
      $nsSuffix = "000"
    }
  }
  if ($ms) {
    $now += ".{0:000}$nsSuffix" -f $Date.MilliSecond
  }
  return $now
}

Function Log () {
  Param(
    [Parameter(Mandatory=$false, ValueFromPipeline=$true, Position=0)]
    [String]$string
  )
  if (!(Test-Path $logDir)) {
    New-Item -ItemType directory -Path $logDir | Out-Null
  }
  if ($String.length) {
    $string = "$(Now) $pid $currentUserName $string"
  }
  $string | Out-File -Encoding ASCII -Append "$logFile"
}

#-----------------------------------------------------------------------------#
#                    DirectoryWatcher Management Functions                   #
#-----------------------------------------------------------------------------#

Function Test-DirectoryWatcherEnvironment {
  $muleHome = $env:MULE_HOME
  $amHome = $env:AM_HOME

  if (!$muleHome) {
    Log "ERROR: MULE_HOME environment variable not set"
    return $false
  }

  if (!$amHome) {
    $amHome = Join-Path $muleHome "am"
    $env:AM_HOME = $amHome
  }

  $libPath = Join-Path $amHome "lib"
  if (!(Test-Path $libPath)) {
    Log "ERROR: DirectoryWatcher lib directory not found: $libPath"
    return $false
  }

  # Set DirectoryWatcher log paths
  $script:dwLogFile = Join-Path $amHome "logs\directorywatcher.log"
  $script:dwErrorFile = Join-Path $amHome "logs\directorywatcher.err"

  # Ensure logs directory exists
  $logsDir = Join-Path $amHome "logs"
  if (!(Test-Path $logsDir)) {
    New-Item -ItemType Directory -Path $logsDir -Force | Out-Null
    Log "Created DirectoryWatcher logs directory: $logsDir"
  }

  Log "DirectoryWatcher environment validated successfully"
  Log "MULE_HOME: $muleHome"
  Log "AM_HOME: $amHome"
  Log "DirectoryWatcher log file: $script:dwLogFile"
  Log "DirectoryWatcher error file: $script:dwErrorFile"
  return $true
}

Function Test-DirectoryWatcherRunning {
  try {
    $javaProcesses = Get-WmiObject Win32_Process -Filter "Name = 'java.exe'" | Where-Object {
      $_.CommandLine -match "com\.mulesoft\.dias\.mule\.agent\.DirectoryWatcher"
    }

    if ($javaProcesses) {
      $currentPid = ($javaProcesses | Select-Object -First 1).ProcessId
      $currentTime = Get-Date
      $timeSinceLastLog = if ($script:lastProcessStatusLog -eq 0) { 9999 } else { ($currentTime - [datetime]::FromFileTime($script:lastProcessStatusLog)).TotalSeconds }
      
      # Smart logging: only log if PID changed or enough time has passed
      if ($smartLogging) {
        if ($script:lastProcessState -ne $currentPid -or $timeSinceLastLog -gt $logStatusInterval) {
          if ($script:lastProcessState -ne $currentPid) {
            Log "DirectoryWatcher state change: now running with PID $currentPid (was: $script:lastProcessState)"
          } else {
            Log "DirectoryWatcher periodic status: running with PID $currentPid"
          }
          $script:lastProcessState = $currentPid
          $script:lastProcessStatusLog = $currentTime.ToFileTime()
        }
      } else {
        # Original logging behavior (every time)
        Log "DirectoryWatcher found running with PID: $currentPid"
      }
      return $true
    } else {
      # Process not running - always log this important state change
      if ($script:lastProcessState -ne $null) {
        Log "DirectoryWatcher state change: process stopped (was PID: $script:lastProcessState)"
        $script:lastProcessState = $null
        $script:lastProcessStatusLog = (Get-Date).ToFileTime()
      }
      return $false
    }
  } catch {
    Log "ERROR: Failed to check DirectoryWatcher status: $($_.Exception.Message)"
    return $false
  }
}

Function Start-DirectoryWatcherProcess {
  try {
    if (!(Test-DirectoryWatcherEnvironment)) {
      return $false
    }

    $muleHome = $env:MULE_HOME
    $amHome = $env:AM_HOME
    $classpath = Join-Path $amHome "lib\*"
    $workingDir = Join-Path $amHome "bin"

    # Determine Java executable
    $javaExe = "java"
    if ($env:JAVA_HOME) {
      $javaExe = Join-Path $env:JAVA_HOME "bin\java.exe"
    }

    $arguments = @(
      "-DmuleHome=`"$muleHome`""
      "-cp"
      "`"$classpath`""
      "com.mulesoft.dias.mule.agent.DirectoryWatcher"
    )

    Log "Starting DirectoryWatcher process..."
    Log "Java executable: $javaExe"
    Log "Working directory: $workingDir"
    Log "Arguments: $($arguments -join ' ')"
    Log "Output will be redirected to: $script:dwLogFile"
    Log "Errors will be redirected to: $script:dwErrorFile"

    $processInfo = New-Object System.Diagnostics.ProcessStartInfo
    $processInfo.FileName = $javaExe
    $processInfo.Arguments = $arguments -join ' '
    $processInfo.WorkingDirectory = $workingDir
    $processInfo.UseShellExecute = $false
    $processInfo.CreateNoWindow = $true
    $processInfo.RedirectStandardOutput = $true
    $processInfo.RedirectStandardError = $true

    # Set environment variables
    $processInfo.EnvironmentVariables["MULE_HOME"] = $muleHome
    $processInfo.EnvironmentVariables["AM_HOME"] = $amHome

    $process = [System.Diagnostics.Process]::Start($processInfo)

    if ($process) {
      Log "DirectoryWatcher started with PID: $($process.Id)"

      # Start background threads to capture output and errors
      Start-PSThread -ScriptBlock {
        param($process, $logFile)
        try {
          $reader = $process.StandardOutput
          while (!$reader.EndOfStream) {
            $line = $reader.ReadLine()
            if ($line) {
              Add-Content -Path $logFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss.fff') $line" -Encoding UTF8
            }
          }
        } catch {
          # Output capture ended
        }
      } -Arguments $process, $script:dwLogFile -Name "DW-Output-Capture"

      Start-PSThread -ScriptBlock {
        param($process, $errorFile)
        try {
          $reader = $process.StandardError
          while (!$reader.EndOfStream) {
            $line = $reader.ReadLine()
            if ($line) {
              Add-Content -Path $errorFile -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss.fff') $line" -Encoding UTF8
            }
          }
        } catch {
          # Error capture ended
        }
      } -Arguments $process, $script:dwErrorFile -Name "DW-Error-Capture"

      # Wait a moment and check if process is still running
      Start-Sleep $dwStartupTimeout

      if ($process.HasExited) {
        Log "WARNING: DirectoryWatcher exited quickly with code: $($process.ExitCode)"
        # Check error file for startup issues
        if (Test-Path $script:dwErrorFile) {
          $errorContent = Get-Content $script:dwErrorFile -Tail 10 | Out-String
          if ($errorContent.Trim()) {
            Log "ERROR: DirectoryWatcher startup errors detected:"
            Log $errorContent.Trim()
          }
        }
        return $false
      } else {
        Log "DirectoryWatcher is running successfully"
        return $true
      }
    } else {
      Log "ERROR: Failed to start DirectoryWatcher process"
      return $false
    }

  } catch {
    Log "ERROR: Exception starting DirectoryWatcher: $($_.Exception.Message)"
    return $false
  }
}

Function Stop-DirectoryWatcherProcess {
  try {
    $javaProcesses = Get-WmiObject Win32_Process -Filter "Name = 'java.exe'" | Where-Object {
      $_.CommandLine -match "com\.mulesoft\.dias\.mule\.agent\.DirectoryWatcher"
    }

    if ($javaProcesses) {
      foreach ($proc in $javaProcesses) {
        Log "Stopping DirectoryWatcher process PID: $($proc.ProcessId)"
        try {
          $process = Get-Process -Id $proc.ProcessId -ErrorAction SilentlyContinue
          if ($process) {
            $process.Kill()
            $process.WaitForExit(5000)
            Log "DirectoryWatcher process $($proc.ProcessId) stopped successfully"
          }
        } catch {
          Log "WARNING: Failed to stop DirectoryWatcher process $($proc.ProcessId): $($_.Exception.Message)"
        }
      }
      return $true
    } else {
      Log "No DirectoryWatcher processes found running"
      return $true
    }
  } catch {
    Log "ERROR: Exception stopping DirectoryWatcher: $($_.Exception.Message)"
    return $false
  }
}

#-----------------------------------------------------------------------------#
#                    DirectoryWatcher Log Monitoring Functions              #
#-----------------------------------------------------------------------------#

Function Test-DirectoryWatcherLogHealth {
  try {
    $healthStatus = @{
      LogExists = $false
      ErrorExists = $false
      RecentActivity = $false
      ErrorCount = 0
      LastLogEntry = ""
      LastErrorEntry = ""
      CriticalErrors = @()
    }

    # Check main log file
    if (Test-Path $script:dwLogFile) {
      $healthStatus.LogExists = $true
      $logInfo = Get-Item $script:dwLogFile

      # Check for recent activity (within last 5 minutes)
      $fiveMinutesAgo = (Get-Date).AddMinutes(-5)
      if ($logInfo.LastWriteTime -gt $fiveMinutesAgo) {
        $healthStatus.RecentActivity = $true
      }

      # Get last few log entries
      $lastLogLines = Get-Content $script:dwLogFile -Tail $dwLogTailLines -ErrorAction SilentlyContinue
      if ($lastLogLines) {
        $healthStatus.LastLogEntry = $lastLogLines[-1]

        # Check for critical patterns in log
        $criticalPatterns = @(
          "Exception",
          "Error",
          "FATAL",
          "Failed",
          "Cannot",
          "Unable to",
          "Connection refused",
          "OutOfMemoryError",
          "ClassNotFoundException"
        )

        foreach ($line in $lastLogLines) {
          foreach ($pattern in $criticalPatterns) {
            if ($line -match $pattern) {
              $healthStatus.CriticalErrors += $line
            }
          }
        }
      }
    }

    # Check error file
    if (Test-Path $script:dwErrorFile) {
      $healthStatus.ErrorExists = $true
      $errorInfo = Get-Item $script:dwErrorFile

      if ($errorInfo.Length -gt 0) {
        $errorLines = Get-Content $script:dwErrorFile -Tail $dwLogTailLines -ErrorAction SilentlyContinue
        if ($errorLines) {
          $healthStatus.LastErrorEntry = $errorLines[-1]
          $healthStatus.ErrorCount = $errorLines.Count
        }
      }
    }

    return $healthStatus
  } catch {
    Log "ERROR: Failed to check DirectoryWatcher log health: $($_.Exception.Message)"
    return $null
  }
}

Function Get-DirectoryWatcherLogSummary {
  try {
    $summary = @{
      LogFile = $script:dwLogFile
      ErrorFile = $script:dwErrorFile
      LogSize = 0
      ErrorSize = 0
      LogLines = 0
      ErrorLines = 0
      LastActivity = "Never"
    }

    if (Test-Path $script:dwLogFile) {
      $logInfo = Get-Item $script:dwLogFile
      $summary.LogSize = [math]::Round($logInfo.Length / 1KB, 2)
      $summary.LastActivity = $logInfo.LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss")

      # Count lines (approximate)
      $content = Get-Content $script:dwLogFile -ErrorAction SilentlyContinue
      if ($content) {
        $summary.LogLines = $content.Count
      }
    }

    if (Test-Path $script:dwErrorFile) {
      $errorInfo = Get-Item $script:dwErrorFile
      $summary.ErrorSize = [math]::Round($errorInfo.Length / 1KB, 2)

      # Count lines (approximate)
      $errorContent = Get-Content $script:dwErrorFile -ErrorAction SilentlyContinue
      if ($errorContent) {
        $summary.ErrorLines = $errorContent.Count
      }
    }

    return $summary
  } catch {
    Log "ERROR: Failed to get DirectoryWatcher log summary: $($_.Exception.Message)"
    return $null
  }
}

Function Monitor-DirectoryWatcherLogs {
  try {
    $healthStatus = Test-DirectoryWatcherLogHealth
    if (!$healthStatus) {
      Log "WARNING: Unable to check DirectoryWatcher log health"
      return
    }

    # Smart log health summary
    $currentLogState = $null
    if ($healthStatus.LogExists) {
      if ($healthStatus.RecentActivity) {
        $currentLogState = "active:$($healthStatus.LastLogEntry -replace '\s+', ' ' | Select-Object -First 50)..."
      } else {
        $currentLogState = "inactive"
      }
    } else {
      $currentLogState = "missing"
    }
    
    $currentTime = Get-Date
    $timeSinceLastHealthLog = if ($script:lastLogHealthLog -eq 0) { 9999 } else { ($currentTime - [datetime]::FromFileTime($script:lastLogHealthLog)).TotalSeconds }
    
    # Smart logging: only log if health state changed or enough time has passed
    if ($smartLogging) {
      if ($script:lastLogHealthState -ne $currentLogState -or $timeSinceLastHealthLog -gt $logStatusInterval) {
        if ($script:lastLogHealthState -ne $currentLogState) {
          # State changed - always log
          if ($healthStatus.LogExists) {
            if ($healthStatus.RecentActivity) {
              Log "DirectoryWatcher log state change: now showing recent activity (last entry: $($healthStatus.LastLogEntry -replace '\s+', ' '))"
            } else {
              Log "WARNING: DirectoryWatcher log state change: now shows no recent activity"
            }
          } else {
            Log "WARNING: DirectoryWatcher log state change: log file not found: $script:dwLogFile"
          }
        } else {
          # Periodic status log (every 5 minutes)
          if ($healthStatus.LogExists) {
            if ($healthStatus.RecentActivity) {
              Log "DirectoryWatcher log periodic status: showing recent activity"
            } else {
              Log "WARNING: DirectoryWatcher log periodic status: no recent activity"
            }
          } else {
            Log "WARNING: DirectoryWatcher log periodic status: log file not found"
          }
        }
        $script:lastLogHealthState = $currentLogState
        $script:lastLogHealthLog = $currentTime.ToFileTime()
      }
    } else {
      # Original logging behavior (every time)
      if ($healthStatus.LogExists) {
        if ($healthStatus.RecentActivity) {
          Log "DirectoryWatcher log shows recent activity (last entry: $($healthStatus.LastLogEntry -replace '\s+', ' '))"
        } else {
          Log "WARNING: DirectoryWatcher log exists but shows no recent activity"
        }
      } else {
        Log "WARNING: DirectoryWatcher log file not found: $script:dwLogFile"
      }
    }

    # Check for errors
    if ($healthStatus.ErrorExists -and $healthStatus.ErrorCount -gt 0) {
      Log "WARNING: DirectoryWatcher has $($healthStatus.ErrorCount) error entries"
      if ($healthStatus.LastErrorEntry) {
        Log "Last error: $($healthStatus.LastErrorEntry -replace '\s+', ' ')"
      }
    }

    # Report critical errors
    if ($healthStatus.CriticalErrors.Count -gt 0) {
      Log "CRITICAL: Found $($healthStatus.CriticalErrors.Count) critical errors in DirectoryWatcher logs"
      foreach ($error in $healthStatus.CriticalErrors | Select-Object -Last 3) {
        Log "CRITICAL ERROR: $($error -replace '\s+', ' ')"
      }

      # Write to Windows Event Log for critical errors
      Write-EventLog -LogName $logName -Source $serviceName -EventId 1020 -EntryType Error -Message "DirectoryWatcher critical errors detected. Check service logs for details." -ErrorAction SilentlyContinue
    }

  } catch {
    Log "ERROR: Exception monitoring DirectoryWatcher logs: $($_.Exception.Message)"
  }
}

#-----------------------------------------------------------------------------#
#                        PSThread Management Functions                       #
#-----------------------------------------------------------------------------#

$PSThreadCount = 0
$PSThreadList = @{}

Function Get-PSThread () {
  Param(
    [Parameter(Mandatory=$false, ValueFromPipeline=$true, Position=0)]
    [int[]]$Id = $PSThreadList.Keys
  )
  $Id | % { $PSThreadList.$_ }
}

Function Start-PSThread () {
  Param(
    [Parameter(Mandatory=$true, Position=0)]
    [ScriptBlock]$ScriptBlock,
    [Parameter(Mandatory=$false)]
    [String]$Name = "",
    [Parameter(Mandatory=$false)]
    [String]$Event = "",
    [Parameter(Mandatory=$false)]
    [Hashtable]$Variables = @{},
    [Parameter(Mandatory=$false)]
    [String[]]$Functions = @(),
    [Parameter(Mandatory=$false)]
    [Object[]]$Arguments = @()
  )

  $Id = $script:PSThreadCount
  $script:PSThreadCount += 1
  if (!$Name.Length) {
    $Name = "PSThread$Id"
  }
  $InitialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
  foreach ($VarName in $Variables.Keys) {
    $value = $Variables.$VarName
    Write-Debug "Adding variable $VarName=[$($Value.GetType())]$Value"
    $var = New-Object System.Management.Automation.Runspaces.SessionStateVariableEntry($VarName, $value, "")
    $InitialSessionState.Variables.Add($var)
  }
  foreach ($FuncName in $Functions) {
    $Body = Get-Content function:$FuncName
    Write-Debug "Adding function $FuncName () {$Body}"
    $func = New-Object System.Management.Automation.Runspaces.SessionStateFunctionEntry($FuncName, $Body)
    $InitialSessionState.Commands.Add($func)
  }
  $RunSpace = [RunspaceFactory]::CreateRunspace($InitialSessionState)
  $RunSpace.Open()
  $PSPipeline = [powershell]::Create()
  $PSPipeline.Runspace = $RunSpace
  $PSPipeline.AddScript($ScriptBlock) | Out-Null
  $Arguments | % {
    Write-Debug "Adding argument [$($_.GetType())]'$_'"
    $PSPipeline.AddArgument($_) | Out-Null
  }
  $Handle = $PSPipeline.BeginInvoke()
  if ($Event.Length) {
    Register-ObjectEvent $PSPipeline -EventName InvocationStateChanged -SourceIdentifier $Name -MessageData $Event
  }
  $PSThread = New-Object PSObject -Property @{
    Id = $Id
    Name = $Name
    Event = $Event
    RunSpace = $RunSpace
    PSPipeline = $PSPipeline
    Handle = $Handle
  }
  $script:PSThreadList[$Id] = $PSThread
  $PSThread
}

Function Receive-PSThread () {
  [CmdletBinding()]
  Param(
    [Parameter(Mandatory=$false, ValueFromPipeline=$true, Position=0)]
    [PSObject]$PSThread,
    [Parameter(Mandatory=$false)]
    [Switch]$AutoRemove
  )
  Process {
    if ($PSThread.Event -and $AutoRemove) {
      Unregister-Event -SourceIdentifier $PSThread.Name
      Get-Event -SourceIdentifier $PSThread.Name | Remove-Event
    }
    try {
      $PSThread.PSPipeline.EndInvoke($PSThread.Handle)
    } catch {
      $_
    }
    if ($AutoRemove) {
      $PSThread.RunSpace.Close()
      $PSThread.PSPipeline.Dispose()
      $PSThreadList.Remove($PSThread.Id)
    }
  }
}

Function Remove-PSThread () {
  [CmdletBinding()]
  Param(
    [Parameter(Mandatory=$false, ValueFromPipeline=$true, Position=0)]
    [PSObject]$PSThread
  )
  Process {
    $_ | Receive-PSThread -AutoRemove | Out-Null
  }
}

#-----------------------------------------------------------------------------#
#                         Named Pipe Functions                               #
#-----------------------------------------------------------------------------#

Function Send-PipeMessage () {
  Param(
    [Parameter(Mandatory=$true)]
    [String]$PipeName,
    [Parameter(Mandatory=$true)]
    [String]$Message
  )
  $PipeDir  = [System.IO.Pipes.PipeDirection]::Out
  $PipeOpt  = [System.IO.Pipes.PipeOptions]::Asynchronous

  $pipe = $null
  $sw = $null
  try {
    $pipe = new-object System.IO.Pipes.NamedPipeClientStream(".", $PipeName, $PipeDir, $PipeOpt)
    $sw = new-object System.IO.StreamWriter($pipe)
    $pipe.Connect(1000)
    if (!$pipe.IsConnected) {
      throw "Failed to connect client to pipe $pipeName"
    }
    $sw.AutoFlush = $true
    $sw.WriteLine($Message)
  } catch {
    Log "Error sending pipe $pipeName message: $_"
  } finally {
    if ($sw) {
      $sw.Dispose()
      $sw = $null
    }
    if ($pipe) {
      $pipe.Dispose()
      $pipe = $null
    }
  }
}

Function Receive-PipeMessage () {
  Param(
    [Parameter(Mandatory=$true)]
    [String]$PipeName
  )
  $PipeDir  = [System.IO.Pipes.PipeDirection]::In
  $PipeOpt  = [System.IO.Pipes.PipeOptions]::Asynchronous
  $PipeMode = [System.IO.Pipes.PipeTransmissionMode]::Message

  try {
    $pipe = $null
    $pipe = New-Object system.IO.Pipes.NamedPipeServerStream($PipeName, $PipeDir, 1, $PipeMode, $PipeOpt)
    $sr = $null
    $sr = new-object System.IO.StreamReader($pipe)
    $pipe.WaitForConnection()
    $Message = $sr.Readline()
    $Message
  } catch {
    Log "Error receiving pipe message: $_"
  } finally {
    if ($sr) {
      $sr.Dispose()
      $sr = $null
    }
    if ($pipe) {
      $pipe.Dispose()
      $pipe = $null
    }
  }
}

$pipeThreadName = "Control Pipe Handler"

Function Start-PipeHandlerThread () {
  Param(
    [Parameter(Mandatory=$true)]
    [String]$pipeName,
    [Parameter(Mandatory=$false)]
    [String]$Event = "ControlMessage"
  )
  Start-PSThread -Variables @{
    logDir = $logDir
    logFile = $logFile
    currentUserName = $currentUserName
  } -Functions Now, Log, Receive-PipeMessage -ScriptBlock {
    Param($pipeName, $pipeThreadName)
    try {
      Receive-PipeMessage "$pipeName"
    } catch {
      Log "$pipeThreadName # Error: $_"
      throw $_
    }
  } -Name $pipeThreadName -Event $Event -Arguments $pipeName, $pipeThreadName
}

Function Receive-PipeHandlerThread () {
  Param(
    [Parameter(Mandatory=$true)]
    [PSObject]$pipeThread
  )
  Receive-PSThread -PSThread $pipeThread -AutoRemove
}

#-----------------------------------------------------------------------------#
#                         C# Service Wrapper Source                          #
#-----------------------------------------------------------------------------#

# Add this function to dw-service.ps1 after the utility functions

Function Get-EmbeddedEnvironmentPaths {
  param([switch]$Validate)

  # Capture current environment values
  $muleHome = $env:MULE_HOME
  $amHome = $env:AM_HOME
  $javaHome = $env:JAVA_HOME

  # Validate and set defaults
  if (!$muleHome) {
    throw "MULE_HOME environment variable must be set during service installation"
  }

  if (!$amHome) {
    $amHome = Join-Path $muleHome "am"
  }

  if ($Validate) {
    # Validate paths exist
    if (!(Test-Path $muleHome)) {
      throw "MULE_HOME path does not exist: $muleHome"
    }

    if (!(Test-Path $amHome)) {
      throw "AM_HOME path does not exist: $amHome"
    }

    $libPath = Join-Path $amHome "lib"
    if (!(Test-Path $libPath)) {
      throw "DirectoryWatcher lib directory not found: $libPath"
    }

    # Validate Java
    if ($javaHome) {
      $javaExe = Join-Path $javaHome "bin\java.exe"
      if (!(Test-Path $javaExe)) {
        throw "Java executable not found: $javaExe"
      }
    } else {
      # Test if java is in PATH
      try {
        $null = & java -version 2>&1
        if ($LASTEXITCODE -ne 0) {
          throw "Java not accessible via PATH"
        }
      } catch {
        throw "Java not found in PATH and JAVA_HOME not set"
      }
    }
  }

  return @{
    MuleHome = $muleHome
    AmHome = $amHome
    JavaHome = $javaHome
  }
}

# Capture and validate environment paths during setup
if ($Setup) {
  try {
    $embeddedPaths = Get-EmbeddedEnvironmentPaths -Validate
    Log "Embedding paths in service executable:"
    Log "  MULE_HOME: $($embeddedPaths.MuleHome)"
    Log "  AM_HOME: $($embeddedPaths.AmHome)"
    Log "  JAVA_HOME: $($embeddedPaths.JavaHome)"
  } catch {
    Write-Error "Environment validation failed: $($_.Exception.Message)"
    exit 1
  }
} else {
  # For non-setup operations, get current values without validation
  $embeddedPaths = Get-EmbeddedEnvironmentPaths
}

$scriptCopyCname = $scriptCopy -replace "\\", "\\"
$embeddedMuleHome = $embeddedPaths.MuleHome -replace "\\", "\\"
$embeddedAmHome = $embeddedPaths.AmHome -replace "\\", "\\"
$embeddedJavaHome = if ($embeddedPaths.JavaHome) { $embeddedPaths.JavaHome -replace "\\", "\\" } else { "" }

$source = @"
  using System;
  using System.ServiceProcess;
  using System.Diagnostics;
  using System.Runtime.InteropServices;
  using System.ComponentModel;
  using System.Reflection;

  public enum ServiceType : int {
    SERVICE_WIN32_OWN_PROCESS = 0x00000010,
    SERVICE_WIN32_SHARE_PROCESS = 0x00000020,
  };

  public enum ServiceState : int {
    SERVICE_STOPPED = 0x00000001,
    SERVICE_START_PENDING = 0x00000002,
    SERVICE_STOP_PENDING = 0x00000003,
    SERVICE_RUNNING = 0x00000004,
    SERVICE_CONTINUE_PENDING = 0x00000005,
    SERVICE_PAUSE_PENDING = 0x00000006,
    SERVICE_PAUSED = 0x00000007,
  };

  [StructLayout(LayoutKind.Sequential)]
  public struct ServiceStatus {
    public ServiceType dwServiceType;
    public ServiceState dwCurrentState;
    public int dwControlsAccepted;
    public int dwWin32ExitCode;
    public int dwServiceSpecificExitCode;
    public int dwCheckPoint;
    public int dwWaitHint;
  };

  public enum Win32Error : int {
    NO_ERROR = 0,
    ERROR_APP_INIT_FAILURE = 575,
    ERROR_FATAL_APP_EXIT = 713,
    ERROR_SERVICE_NOT_ACTIVE = 1062,
    ERROR_EXCEPTION_IN_SERVICE = 1064,
    ERROR_SERVICE_SPECIFIC_ERROR = 1066,
    ERROR_PROCESS_ABORTED = 1067,
  };

  public class Service_$serviceName : ServiceBase {
    private System.Diagnostics.EventLog eventLog;
    private ServiceStatus serviceStatus;
    private int stopOnPreShutdown = $stopOnPreShutdown;

    // Embedded paths - captured during service installation
    private static readonly string EMBEDDED_MULE_HOME = "$embeddedMuleHome";
    private static readonly string EMBEDDED_AM_HOME = "$embeddedAmHome";
    private static readonly string EMBEDDED_JAVA_HOME = "$embeddedJavaHome";

    public const int SERVICE_ACCEPT_PRESHUTDOWN = 0x100;
    public const int SERVICE_CONTROL_PRESHUTDOWN = 0xf;

    public Service_$serviceName() {
      ServiceName = "$serviceName";
      CanStop = true;
      CanShutdown = true;
      CanPauseAndContinue = false;
      AutoLog = true;

      eventLog = new System.Diagnostics.EventLog();
      if (!System.Diagnostics.EventLog.SourceExists(ServiceName)) {
        System.Diagnostics.EventLog.CreateEventSource(ServiceName, "$logName");
      }
      eventLog.Source = ServiceName;
      eventLog.Log = "$logName";

      if (stopOnPreShutdown != 0) {
        FieldInfo acceptedCommandsFieldInfo = typeof(ServiceBase).GetField("acceptedCommands", BindingFlags.Instance | BindingFlags.NonPublic);
        if (acceptedCommandsFieldInfo == null) {
          EventLog.WriteEntry(ServiceName, "$exeName $serviceName() // acceptedCommands field not found. The $serviceName service will not stop on OS shutdown.", EventLogEntryType.Warning);
        } else {
          int value = (int)acceptedCommandsFieldInfo.GetValue(this);
          acceptedCommandsFieldInfo.SetValue(this, value | SERVICE_ACCEPT_PRESHUTDOWN);
        }
      }

      EventLog.WriteEntry(ServiceName, "$exeName $serviceName() // DirectoryWatcher service initialized with embedded paths: MULE_HOME=" + EMBEDDED_MULE_HOME + ", AM_HOME=" + EMBEDDED_AM_HOME + ", JAVA_HOME=" + EMBEDDED_JAVA_HOME);
    }

    [DllImport("advapi32.dll", SetLastError=true)]
    private static extern bool SetServiceStatus(IntPtr handle, ref ServiceStatus serviceStatus);

    protected override void OnStart(string [] args) {
      EventLog.WriteEntry(ServiceName, "$exeName OnStart() // Entry");
      serviceStatus.dwServiceType = ServiceType.SERVICE_WIN32_OWN_PROCESS;
      serviceStatus.dwCurrentState = ServiceState.SERVICE_START_PENDING;
      serviceStatus.dwWin32ExitCode = 0;
      serviceStatus.dwServiceSpecificExitCode = 0;
      serviceStatus.dwWaitHint = 2000;
      SetServiceStatus(ServiceHandle, ref serviceStatus);
      try {
        RunPSServiceCommand("SCMStart");
        serviceStatus.dwCurrentState = ServiceState.SERVICE_RUNNING;
      } catch (Exception e) {
        EventLog.WriteEntry(ServiceName, "$exeName OnStart() // Failed to start $scriptCopyCname. " + e.Message, EventLogEntryType.Error);
        serviceStatus.dwCurrentState = ServiceState.SERVICE_STOPPED;
        serviceStatus.dwWin32ExitCode = GetExceptionWin32Error(e);
      } finally {
        serviceStatus.dwWaitHint = 0;
        SetServiceStatus(ServiceHandle, ref serviceStatus);
        EventLog.WriteEntry(ServiceName, "$exeName OnStart() // Exit");
      }
    }

    private int GetExceptionWin32Error(Exception e) {
      Win32Exception w32ex = e as Win32Exception;
      if (w32ex == null) {
        w32ex = e.InnerException as Win32Exception;
      }
      if (w32ex != null) {
        return w32ex.NativeErrorCode;
      } else {
        return (int)(Win32Error.ERROR_EXCEPTION_IN_SERVICE);
      }
    }

    private void RunPSServiceCommand(string cmd) {
      try {
        Process p = new Process();
        p.StartInfo.UseShellExecute = false;
        p.StartInfo.RedirectStandardOutput = true;
        p.StartInfo.FileName = "PowerShell.exe";

        // Set embedded environment variables for the PowerShell process
        p.StartInfo.EnvironmentVariables["MULE_HOME"] = EMBEDDED_MULE_HOME;
        p.StartInfo.EnvironmentVariables["AM_HOME"] = EMBEDDED_AM_HOME;
        if (!string.IsNullOrEmpty(EMBEDDED_JAVA_HOME)) {
          p.StartInfo.EnvironmentVariables["JAVA_HOME"] = EMBEDDED_JAVA_HOME;
        }

        p.StartInfo.Arguments = "-ExecutionPolicy Bypass -c & '$scriptCopyCname' -" + cmd;
        string cmdLine = p.StartInfo.FileName + " " + p.StartInfo.Arguments;
        EventLog.WriteEntry(ServiceName, "$exeName RunPSServiceCommand() // Running: " + cmdLine + " with MULE_HOME=" + EMBEDDED_MULE_HOME);
        p.Start();
        string output = p.StandardOutput.ReadToEnd();
        p.WaitForExit();
        if (p.ExitCode != 0) {
          EventLog.WriteEntry(ServiceName, "$exeName RunPSServiceCommand() // PowerShell.exe returned " + p.ExitCode);
          throw new Win32Exception((int)(Win32Error.ERROR_APP_INIT_FAILURE));
        }
      } catch (Exception e) {
        EventLog.WriteEntry(ServiceName, "$exeName RunPSServiceCommand() // Command failed: " + e.Message, EventLogEntryType.Error);
        throw e;
      }
    }

    protected override void OnStop() {
      EventLog.WriteEntry(ServiceName, "$exeName OnStop() // Entry");
      serviceStatus.dwWin32ExitCode = 0;
      try {
        RunPSServiceCommand("SCMStop");
        serviceStatus.dwCurrentState = ServiceState.SERVICE_STOPPED;
      } catch(Exception e) {
        EventLog.WriteEntry(ServiceName, "$exeName OnStop() // Failed to stop $scriptCopyCname. " + e.Message, EventLogEntryType.Error);
        serviceStatus.dwWin32ExitCode = GetExceptionWin32Error(e);
      } finally {
        SetServiceStatus(ServiceHandle, ref serviceStatus);
        EventLog.WriteEntry(ServiceName, "$exeName OnStop() // Exit");
      }
    }

    protected override void OnCustomCommand(int command) {
      if (command == SERVICE_CONTROL_PRESHUTDOWN) {
        EventLog.WriteEntry(ServiceName, "$exeName OnPreShutdown() // Entry");
        serviceStatus.dwWin32ExitCode = 0;
        try {
          RunPSServiceCommand("SCMPreShutdown");
          serviceStatus.dwCurrentState = ServiceState.SERVICE_STOPPED;
        } catch(Exception e) {
          EventLog.WriteEntry(ServiceName, "$exeName OnPreShutdown() // Failed to stop $scriptCopyCname. " + e.Message, EventLogEntryType.Error);
          serviceStatus.dwWin32ExitCode = GetExceptionWin32Error(e);
        } finally {
          SetServiceStatus(ServiceHandle, ref serviceStatus);
          EventLog.WriteEntry(ServiceName, "$exeName OnPreShutdown() // Exit");
        }
      } else {
        base.OnCustomCommand(command);
      }
    }

    protected override void OnShutdown() {
      // Do nothing - PreShutdown handles graceful shutdown
    }

    public static void Main() {
      System.ServiceProcess.ServiceBase.Run(new Service_$serviceName());
    }
  }
"@

#-----------------------------------------------------------------------------#
#                              Main Logic                                    #
#-----------------------------------------------------------------------------#

# Identify the current user
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
$currentUserName = $identity.Name

if ($Setup) {Log ""}
Log $MyInvocation.Line

# Ensure Event Log source exists
New-EventLog -LogName $logName -Source $serviceName -ea SilentlyContinue

# Handle PowerShell v2 compatibility
$Status = ($PSCmdlet.ParameterSetName -eq 'Status')

if ($SCMStart) {
  Log "$scriptName -SCMStart: Starting script '$scriptFullName' -Service"
  Write-EventLog -LogName $logName -Source $serviceName -EventId 1001 -EntryType Information -Message "$scriptName -SCMStart: Starting script '$scriptFullName' -Service"
  Start-Process PowerShell.exe -ArgumentList ("-c & '$scriptFullName' -Service")
  return
}

if ($Start) {
  Write-Verbose "Starting service $serviceName"
  Write-EventLog -LogName $logName -Source $serviceName -EventId 1002 -EntryType Information -Message "$scriptName -Start: Starting service $serviceName"
  Start-Service $serviceName
  return
}

if ($SCMStop) {
  Write-EventLog -LogName $logName -Source $serviceName -EventId 1003 -EntryType Information -Message "$scriptName -SCMStop: Stopping script $scriptName -Service"
  Log "$scriptName -SCMStop: Stopping script $scriptName -Service"
  Send-PipeMessage $pipeName "exit"
  return
}

if ($SCMPreShutdown) {
  Write-EventLog -LogName $logName -Source $serviceName -EventId 1005 -EntryType Information -Message "$scriptName -SCMPreShutdown: Stopping script $scriptName -Service"
  Log "$scriptName -SCMPreShutdown: Stopping script $scriptName -Service"
  Send-PipeMessage $pipeName "exit"
  return
}

if ($Stop) {
  Write-Verbose "Stopping service $serviceName"
  Write-EventLog -LogName $logName -Source $serviceName -EventId 1004 -EntryType Information -Message "$scriptName -Stop: Stopping service $serviceName"
  Stop-Service $serviceName
  return
}

if ($Restart) {
  & $scriptFullName -Stop
  & $scriptFullName -Start
  return
}

if ($Status) {
  $spid = $null
  $processes = @(Get-WmiObject Win32_Process -filter "Name = 'powershell.exe'" | Where-Object {
    $_.CommandLine -match ".*$scriptCopyCname.*-Service"
  })
  foreach ($process in $processes) {
    $spid = $process.ProcessId
    Write-Verbose "$serviceName Process ID = $spid"
  }
  try {
    $pss = Get-Service $serviceName -ea stop
  } catch {
    "Not Installed"
    return
  }
  $pss.Status
  if (($pss.Status -eq "Running") -and (!$spid)) {
    Write-Error "The Service Control Manager thinks $serviceName is started, but $serviceName.ps1 -Service is not running."
    exit 1
  }

  # Enhanced status with DirectoryWatcher log information
  if ($pss.Status -eq "Running") {
    if (Test-DirectoryWatcherEnvironment) {
      $logSummary = Get-DirectoryWatcherLogSummary
      if ($logSummary) {
        Write-Host "DirectoryWatcher Log Status:" -ForegroundColor Yellow
        Write-Host "  Main Log: $($logSummary.LogFile)" -ForegroundColor Cyan
        Write-Host "  Log Size: $($logSummary.LogSize) KB ($($logSummary.LogLines) lines)" -ForegroundColor Cyan
        Write-Host "  Error Log: $($logSummary.ErrorFile)" -ForegroundColor Cyan
        Write-Host "  Error Size: $($logSummary.ErrorSize) KB ($($logSummary.ErrorLines) lines)" -ForegroundColor Cyan
        Write-Host "  Last Activity: $($logSummary.LastActivity)" -ForegroundColor Cyan
      }
    }
  }
  return
}

if ($Setup) {
  try {
    $pss = Get-Service $serviceName -ea stop
    if ((Get-Item $scriptCopy -ea SilentlyContinue).LastWriteTime -lt (Get-Item $scriptFullName -ea SilentlyContinue).LastWriteTime) {
      Write-Verbose "Service $serviceName is already Installed, but requires upgrade"
      & $scriptFullName -Remove
      throw "continue"
    } else {
      Write-Verbose "Service $serviceName is already Installed, and up-to-date"
    }
    exit 0
  } catch {
    Write-Debug "Installation is necessary"
  }
  if (!(Test-Path $installDir)) {
    New-Item -ItemType directory -Path $installDir | Out-Null
  }
  if ($ScriptFullName -ne $scriptCopy) {
    Write-Verbose "Installing $scriptCopy"
    Copy-Item $ScriptFullName $scriptCopy
  }
  try {
    Write-Verbose "Compiling $exeFullName"
    Add-Type -TypeDefinition $source -Language CSharp -OutputAssembly $exeFullName -OutputType ConsoleApplication -ReferencedAssemblies "System.ServiceProcess" -Debug:$false
  } catch {
    $msg = $_.Exception.Message
    Write-error "Failed to create the $exeFullName service stub. $msg"
    exit 1
  }
  Write-Verbose "Registering service $serviceName"
  if ($UserName -and !$Credential.UserName) {
    $emptyPassword = New-Object -Type System.Security.SecureString
    switch ($UserName) {
      {"LocalService", "NetworkService" -contains $_} {
        $Credential = New-Object -Type System.Management.Automation.PSCredential ("NT AUTHORITY\$UserName", $emptyPassword)
      }
      {"LocalSystem", ".\LocalSystem", "${env:COMPUTERNAME}\LocalSystem", "NT AUTHORITY\LocalService", "NT AUTHORITY\NetworkService" -contains $_} {
        $Credential = New-Object -Type System.Management.Automation.PSCredential ($UserName, $emptyPassword)
      }
      default {
        if (!$Password) {
          $Credential = Get-Credential -UserName $UserName -Message "Please enter the password for the service user"
        } else {
          $securePassword = ConvertTo-SecureString $Password -AsPlainText -Force
          $Credential = New-Object -Type System.Management.Automation.PSCredential ($UserName, $securePassword)
        }
      }
    }
  }
  if ($Credential.UserName) {
    Log "$scriptName -Setup # Configuring the service to run as $($Credential.UserName)"
    $pss = New-Service $serviceName $exeFullName -DisplayName $serviceDisplayName -Description $ServiceDescription -StartupType Automatic -Credential $Credential
  } else {
    Log "$scriptName -Setup # Configuring the service to run by default as LocalSystem"
    $pss = New-Service $serviceName $exeFullName -DisplayName $serviceDisplayName -Description $ServiceDescription -StartupType Automatic
  }
  
  # Set service recovery options to restart on failure (same as OTEL Collector)
  try {
    Log "$scriptName -Setup # Setting recovery options for DirectoryWatcher service..."
    $failureResult = sc.exe failure $serviceName reset= 60 actions= restart/60000/restart/60000/restart/60000
    if ($LASTEXITCODE -eq 0) {
      Log "$scriptName -Setup # Recovery options set successfully for '$serviceName'"
    } else {
      Log "$scriptName -Setup # WARNING: Failed to set recovery options. sc.exe exit code: $LASTEXITCODE"
    }
  } catch {
    Log "$scriptName -Setup # WARNING: Exception setting recovery options: $($_.Exception.Message)"
  }
  
  return
}

if ($Remove) {
  try {
    $pss = Get-Service $serviceName -ea stop
  } catch {
    Write-Verbose "Already uninstalled"
    return
  }
  Stop-Service $serviceName
  Write-Verbose "Removing service $serviceName"
  $msg = sc.exe delete $serviceName
  if ($LastExitCode) {
    Write-Error "Failed to remove the service ${serviceName}: $msg"
    exit 1
  } else {
    Write-Verbose $msg
  }
  if (Test-Path $installDir) {
    foreach ($ext in ("exe", "pdb", "ps1")) {
      $file = "$installDir\$serviceName.$ext"
      if (Test-Path $file) {
        Write-Verbose "Deleting file $file"
        Remove-Item $file
      }
    }
    if (!(@(Get-ChildItem $installDir -ea SilentlyContinue)).Count) {
      Write-Verbose "Removing directory $installDir"
      Remove-Item $installDir
    }
  }
  Log ""
  return
}

if ($Control) {
  Send-PipeMessage $pipeName $control
}

if ($Service) {
  Write-EventLog -LogName $logName -Source $serviceName -EventId 1005 -EntryType Information -Message "$scriptName -Service # Beginning enhanced DirectoryWatcher service with log monitoring"

  try {
    # Start the control pipe handler thread
    $pipeThread = Start-PipeHandlerThread $pipeName -Event "ControlMessage"

    # Initialize DirectoryWatcher monitoring
    Log "Enhanced DirectoryWatcher Service starting with log monitoring..."

    if (!(Test-DirectoryWatcherEnvironment)) {
      throw "DirectoryWatcher environment validation failed"
    }
    # PATCH: Start DirectoryWatcher immediately (don't wait for timer)
    Log "OPTIMIZATION: Starting DirectoryWatcher immediately on service startup..."
    if (!(Test-DirectoryWatcherRunning)) {
      if (Start-DirectoryWatcherProcess) {
        Log "SUCCESS: DirectoryWatcher started immediately"
      } else {
        Log "WARNING: Failed to start DirectoryWatcher immediately - will retry on timer"
      }
    } else {
      Log "INFO: DirectoryWatcher already running"
    }

    # Service monitoring variables
    $restartAttempts = 0
    $lastRestartTime = Get-Date

    # Start periodic monitoring timer for process checks
    $processTimerName = "DirectoryWatcher Process Monitor Timer"
    $processTimer = new-object System.Timers.Timer
    $processTimer.Interval = ($dwMonitorInterval * 1000)
    $processTimer.AutoReset = $true
    Register-ObjectEvent $processTimer -EventName Elapsed -SourceIdentifier $processTimerName -MessageData "ProcessMonitorTick"
    $processTimer.start()

    # Start periodic monitoring timer for log checks
    $logTimerName = "DirectoryWatcher Log Monitor Timer"
    $logTimer = new-object System.Timers.Timer
    $logTimer.Interval = ($dwLogMonitorInterval * 1000)
    $logTimer.AutoReset = $true
    Register-ObjectEvent $logTimer -EventName Elapsed -SourceIdentifier $logTimerName -MessageData "LogMonitorTick"
    $logTimer.start()

    Log "DirectoryWatcher monitoring started:"
    Log "  Process checks every $dwMonitorInterval seconds"
    Log "  Log monitoring every $dwLogMonitorInterval seconds"
    Log "  DirectoryWatcher output: $script:dwLogFile"
    Log "  DirectoryWatcher errors: $script:dwErrorFile"

    # Main service event loop
    do {
      $event = Wait-Event
      $source = $event.SourceIdentifier
      $message = $event.MessageData
      $eventTime = $event.TimeGenerated.TimeofDay
      Write-Debug "Event at $eventTime from ${source}: $message"
      $event | Remove-Event

      switch ($message) {
        "ControlMessage" {
          $state = $event.SourceEventArgs.InvocationStateInfo.state
          Write-Debug "$script -Service # Thread $source state changed to $state"
          switch ($state) {
            "Completed" {
              $message = Receive-PipeHandlerThread $pipeThread
              Log "$scriptName -Service # Received control message: $Message"
              if ($message -ne "exit") {
                $pipeThread = Start-PipeHandlerThread $pipeName -Event "ControlMessage"
              }
            }
            "Failed" {
              $error = Receive-PipeHandlerThread $pipeThread
              Log "$scriptName -Service # $source thread failed: $error"
              Start-Sleep 1
              $pipeThread = Start-PipeHandlerThread $pipeName -Event "ControlMessage"
            }
          }
        }
        "ProcessMonitorTick" {
          try {
            if (!(Test-DirectoryWatcherRunning)) {
              Log "DirectoryWatcher not running - attempting to start"

              # Reset restart attempts if enough time has passed
              $timeSinceLastRestart = (Get-Date) - $lastRestartTime
              if ($timeSinceLastRestart.TotalMinutes -gt 10) {
                $restartAttempts = 0
              }

              if ($restartAttempts -lt $dwMaxRestartAttempts) {
                if (Start-DirectoryWatcherProcess) {
                  Log "DirectoryWatcher started successfully"
                  $restartAttempts = 0
                } else {
                  $restartAttempts++
                  $lastRestartTime = Get-Date
                  Log "DirectoryWatcher start failed (attempt $restartAttempts of $dwMaxRestartAttempts)"

                  if ($restartAttempts -ge $dwMaxRestartAttempts) {
                    Log "ERROR: Maximum restart attempts reached - DirectoryWatcher monitoring suspended"
                    Write-EventLog -LogName $logName -Source $serviceName -EventId 1010 -EntryType Error -Message "DirectoryWatcher maximum restart attempts reached"
                  }
                }
              }
            } else {
              Write-Debug "DirectoryWatcher is running normally"
            }
          } catch {
            Log "ERROR: Exception during DirectoryWatcher process monitoring: $($_.Exception.Message)"
          }
        }
        "LogMonitorTick" {
          try {
            Monitor-DirectoryWatcherLogs
          } catch {
            Log "ERROR: Exception during DirectoryWatcher log monitoring: $($_.Exception.Message)"
          }
        }
        default {
          Log "$scriptName -Service # Unexpected event from ${source}: $Message"
        }
      }
    } while ($message -ne "exit")

  } catch {
    $msg = $_.Exception.Message
    $line = $_.InvocationInfo.ScriptLineNumber
    Log "$scriptName -Service # Error at line ${line}: $msg"
  } finally {
    # Cleanup operations
    Log "$scriptName -Service # Shutting down enhanced DirectoryWatcher service..."

    # Stop DirectoryWatcher processes
    Stop-DirectoryWatcherProcess

    # Cleanup timers
    Unregister-Event -SourceIdentifier $processTimerName -ErrorAction SilentlyContinue
    Unregister-Event -SourceIdentifier $logTimerName -ErrorAction SilentlyContinue
    if ($processTimer) {
      $processTimer.stop()
      $processTimer.Dispose()
    }
    if ($logTimer) {
      $logTimer.stop()
      $logTimer.Dispose()
    }

    # Cleanup threads
    Get-PSThread | Remove-PSThread

    # Flush events
    $events = Get-Event | Remove-Event

    # Final log entries
    Write-EventLog -LogName $logName -Source $serviceName -EventId 1006 -EntryType Information -Message "$script -Service # Enhanced DirectoryWatcher service with log monitoring exiting"
    Log "$scriptName -Service # Enhanced DirectoryWatcher service exited"
  }
  return
}