src/private/Write-CustomLog.ps1

#Requires -Version 7.0

<#
.SYNOPSIS
    Internal logging function for AitherZero module
.DESCRIPTION
    Provides structured logging with multiple targets - internal use only.
    Public interface is Write-AitherLog.
#>


# Script-level variables for logging state
if (-not (Get-Variable -Name 'Script:AitherLogPath' -Scope Script -ErrorAction SilentlyContinue)) {
    # Use Get-AitherModuleRoot if available, otherwise calculate from PSScriptRoot
    # PSScriptRoot is .../AitherZero/Private, so we need to go up 2 levels to get project root
    $projectRoot = if (Get-Command Get-AitherModuleRoot -ErrorAction SilentlyContinue) {
        Get-AitherModuleRoot
    }
    elseif ($script:ProjectRoot) {
        $script:ProjectRoot
    }
    elseif ($env:AITHERZERO_ROOT) {
        $env:AITHERZERO_ROOT
    }
    else {
        # Fallback: calculate from PSScriptRoot (Private -> AitherZero -> Project)
        Split-Path (Split-Path $PSScriptRoot -Parent) -Parent
    }

    $script:AitherLogPath = Join-Path $projectRoot "AitherZero/library/logs"
    $script:AitherLogLevel = "Information"
    $script:AitherLogTargets = @("File")
    $script:AitherLogBuffer = @()
    $script:AitherBufferSize = 100
}

function Write-CustomLog {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ValidateSet('Trace', 'Debug', 'Information', 'Warning', 'Error', 'Critical')]
        [string]$Level,

        [Parameter(Mandatory)]
        [string]$Message,

        [string]$Source = "General",

        [hashtable]$Data = @{},

        [System.Exception]$Exception,

        [string[]]$Targets
    )

    # Log level hierarchy
    $logLevels = @{
        'Trace'       = 0
        'Debug'       = 1
        'Information' = 2
        'Warning'     = 3
        'Error'       = 4
        'Critical'    = 5
    }

    # Check if we should log this based on configured level
    $currentLevel = if ($script:AitherLogLevel -and $logLevels.ContainsKey($script:AitherLogLevel)) {
        $logLevels[$script:AitherLogLevel]
    }
    else {
        2 # Default to Information
    }

    $messageLevel = if ($Level -and $logLevels.ContainsKey($Level)) {
        $logLevels[$Level]
    }
    else {
        2 # Default to Information
    }

    if ($messageLevel -lt $currentLevel) {
        return
    }

    # Determine active targets
    $activeTargets = if ($Targets) { $Targets } else { $script:AitherLogTargets }

    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss.fff"

    # Create structured log entry
    $logEntry = [PSCustomObject]@{
        Timestamp = $timestamp
        Level     = $Level
        Source    = $Source
        Message   = $Message
        Data      = $Data
        Exception = if ($Exception) { $Exception.ToString() } else { $null }
        ProcessId = $PID
        ThreadId  = [System.Threading.Thread]::CurrentThread.ManagedThreadId
        User      = if ($IsWindows) {
            try { [System.Security.Principal.WindowsIdentity]::GetCurrent().Name } catch { $env:USERNAME }
        }
        else { $env:USER }
        Computer  = if ($env:COMPUTERNAME) { $env:COMPUTERNAME } else { $env:HOSTNAME }
    }

    # Write to console
    if ($activeTargets -contains 'Console') {
        $color = switch ($Level) {
            'Trace' { 'DarkGray' }
            'Debug' { 'Gray' }
            'Information' { 'White' }
            'Warning' { 'Yellow' }
            'Error' { 'Red' }
            'Critical' { 'Magenta' }
            default { 'White' }
        }

        $prefix = switch ($Level) {
            'Trace' { '[TRACE]' }
            'Debug' { '[DEBUG]' }
            'Information' { '[INFO]' }
            'Warning' { '[WARN]' }
            'Error' { '[ERROR]' }
            'Critical' { '[CRIT]' }
            default { '[INFO]' }
        }

        Write-Host "$prefix [$timestamp] $Source`: $Message" -ForegroundColor $color

        if ($Exception) {
            Write-Host " Exception: $($Exception.ToString())" -ForegroundColor $color
        }
    }

    # Write to file
    if ($activeTargets -contains 'File') {
        try {
            if (-not (Test-Path $script:AitherLogPath)) {
                New-Item -Path $script:AitherLogPath -ItemType Directory -Force | Out-Null
            }

            $logFile = Join-Path $script:AitherLogPath "aitherzero-$(Get-Date -Format 'yyyy-MM-dd').log"
            $logLine = "[$timestamp] [$Level] [$Source] $Message"

            if ($Data.Count -gt 0) {
                $dataStr = ($Data.GetEnumerator() | ForEach-Object { "$($_.Key)=$($_.Value)" }) -join '; '
                $logLine += " | Data: $dataStr"
            }

            if ($Exception) {
                $logLine += "`n Exception: $($Exception.ToString())"
            }

            Add-Content -Path $logFile -Value $logLine -ErrorAction SilentlyContinue
        }
        catch {
            # Fail silently for logging errors
        }
    }

    # Add to buffer
    $script:AitherLogBuffer += $logEntry
    if ($script:AitherLogBuffer.Count -ge $script:AitherBufferSize) {
        # Clear buffer (simplified - just reset)
        $script:AitherLogBuffer = @()
    }
}