Private/Write-BridgeLog.ps1

function Write-BridgeLog {
    <#
    .SYNOPSIS
    Καταγράφει μηνύματα λειτουργίας του συστήματος.

    .DESCRIPTION
    Η Write-BridgeLog γράφει μηνύματα με κατάλληλη κατηγοριοποίηση
    (Verbose, Debug, Warning) και αποθηκεύει σε ημερήσια αρχεία καταγραφής.

    .PARAMETER Stage
    The operation stage (Ανάλυση, Απόφαση, Ειδοποίηση, Σφάλμα).

    .PARAMETER Message
    Το μήνυμα που θα καταγραφεί.

    .PARAMETER Level
    Το επίπεδο λογιστικού μηνύματος (Verbose, Debug, Warning).

    .PARAMETER Configuration
    Το configuration object που περιέχει τις ρυθμίσεις.

    .OUTPUTS
    None.

    .EXAMPLE
    Write-BridgeLog -Stage 'Ανάλυση' -Message 'Έλεγχος OCR...' -Level 'Verbose'

    .NOTES
    Δημιουργεί log directory αν δεν υπάρχει και καταγράφει ημερήσια αρχεία.
    #>

    [CmdletBinding()]
    [OutputType([void])]
    param (
        [Parameter(Mandatory)][ValidateNotNullOrEmpty()][ValidateSet('Ανάλυση', 'Απόφαση', 'Ειδοποίηση', 'Σφάλμα')]
        [string]$Stage,
        [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$Message,
        [Parameter()][ValidateSet('Verbose', 'Debug', 'Warning')]
        [string]$Level = 'Verbose',
        [Parameter()][PSCustomObject]$Configuration
    )
    if (-not $Configuration) {
        $Configuration = New-BridgeConfiguration
    }
    $prefix = "[Bridge:$Stage]"
    $output = "$prefix $Message"
    # Console logging
    switch ($Level) {
        'Verbose' { Write-Verbose $output }
        'Debug' { Write-Debug $output }
        'Warning' { Write-Warning $output }
    }
    # File logging — uses configured log directory
    $logDir = $Configuration.Defaults.LogDirectory
    if (-not (Test-Path $logDir)) {
        $newItemSplat = @{
            Path     = $logDir
            ItemType = 'Directory'
            Force    = $true
        }
        [void](New-Item @newItemSplat)
    }
    $dateStr = (Get-Date).ToString('yyyy-MM-dd')
    $timeStr = (Get-Date).ToString('HH:mm:ss')

    $joinPathSplat = @{
        Path      = $logDir
        ChildPath = "BridgeWatcher-$dateStr.log"
    }
    $logPath = Join-Path @joinPathSplat
    $logLine = "[$timeStr] [$Stage] $Message"

    $isPester = [bool](Get-PSCallStack | Where-Object { $_.ScriptName -like '*Tests.ps1' -or $_.ScriptName -like '*Pester*' })

    if ($isPester -or $logDir -like 'TestDrive:\*') {
        try {
            $addContentSplat = @{
                Path        = $logPath
                Value       = $logLine
                Encoding    = 'utf8BOM'
                ErrorAction = 'Stop'
            }
            Add-Content @addContentSplat
        }
        catch {
            Write-Warning "Failed to write to log file '$logPath': $($_.Exception.Message)"
        }
    } else {
        try {
            if ($script:LogStream -and $script:LogStreamPath -eq $logPath) {
                $script:LogStream.WriteLine($logLine)
                $script:LogStream.Flush()
            } else {
                if ($script:LogStream) {
                    try { $script:LogStream.Close() } catch { Write-Verbose $_.Exception.Message }
                    $script:LogStream = $null
                }
                $utf8WithBOM = New-Object System.Text.UTF8Encoding $true
                $script:LogStream = New-Object System.IO.StreamWriter($logPath, $true, $utf8WithBOM)
                $script:LogStream.AutoFlush = $true
                $script:LogStreamPath = $logPath

                $script:LogStream.WriteLine($logLine)
            }
        }
        catch {
            Write-Warning "Failed to write to log file '$logPath': $($_.Exception.Message)"
        }
    }
}