Core/Logger.ps1

# Module-level variables for logger state
$script:LogFilePath = $null
$script:LogToFile = $false
$script:LogToConsole = $true

function Initialize-Logger {
    <#
    .SYNOPSIS
        Initializes the logging system.

    .DESCRIPTION
        Sets up logging configuration including file path and output options.

    .PARAMETER LogPath
        Path to the log file. If not specified, file logging is disabled.

    .PARAMETER LogToConsole
        Whether to write logs to console. Default is $true.

    .PARAMETER LogToFile
        Whether to write logs to file. Default is $true if LogPath is provided.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false)]
        [string]$LogPath,

        [Parameter(Mandatory = $false)]
        [bool]$LogToConsole = $true,

        [Parameter(Mandatory = $false)]
        [bool]$LogToFile = $true
    )

    $script:LogToConsole = $LogToConsole

    if ($LogPath) {
        $script:LogFilePath = $LogPath
        $script:LogToFile = $LogToFile

        # Create log directory if it doesn't exist
        $logDir = Split-Path -Path $LogPath -Parent
        if ($logDir -and -not (Test-Path $logDir)) {
            New-Item -Path $logDir -ItemType Directory -Force | Out-Null
        }

        # Create or clear log file
        if ($script:LogToFile) {
            try {
                # Write header to log file
                $header = @"
=================================================================
Microsoft Secure Score Assessment Toolkit - Log File
Generated: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')
=================================================================

"@

                $header | Out-File -FilePath $LogPath -Encoding UTF8 -Force
            }
            catch {
                Write-Warning "Failed to initialize log file: $_"
                $script:LogToFile = $false
            }
        }
    }
    else {
        $script:LogToFile = $false
    }
}

function Write-Log {
    <#
    .SYNOPSIS
        Writes a log message.

    .DESCRIPTION
        Writes a log message to console and/or file based on configuration.

    .PARAMETER Message
        The message to log. Can be empty for blank lines.

    .PARAMETER Level
        Log level: Info, Success, Warning, or Error.

    .PARAMETER NoConsole
        Suppress console output for this message only.

    .PARAMETER NoFile
        Suppress file output for this message only.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false)]
        [string]$Message = "",

        [Parameter(Mandatory = $false)]
        [ValidateSet("Info", "Success", "Warning", "Error", "Debug")]
        [string]$Level = "Info",

        [Parameter(Mandatory = $false)]
        [switch]$NoConsole,

        [Parameter(Mandatory = $false)]
        [switch]$NoFile
    )

    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    $logMessage = "[$timestamp] [$Level] $Message"

    # Console output
    if ($script:LogToConsole -and -not $NoConsole) {
        $color = switch ($Level) {
            "Info" { "Cyan" }
            "Success" { "Green" }
            "Warning" { "Yellow" }
            "Error" { "Red" }
            "Debug" { "Gray" }
            default { "White" }
        }
        Write-Host $logMessage -ForegroundColor $color
    }

    # File output
    if ($script:LogToFile -and $script:LogFilePath -and -not $NoFile) {
        try {
            $logMessage | Out-File -FilePath $script:LogFilePath -Append -Encoding UTF8
        }
        catch {
            Write-Warning "Failed to write to log file: $_"
        }
    }
}

function Write-LogSection {
    <#
    .SYNOPSIS
        Writes a section header to the log.

    .DESCRIPTION
        Writes a formatted section header with separator lines.

    .PARAMETER Title
        Section title.

    .PARAMETER Level
        Log level for the section header.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Title,

        [Parameter(Mandatory = $false)]
        [ValidateSet("Info", "Success", "Warning", "Error")]
        [string]$Level = "Info"
    )

    $separator = "=" * 60
    Write-Log -Level $Level
    Write-Log -Message $separator -Level $Level
    Write-Log -Message $Title -Level $Level
    Write-Log -Message $separator -Level $Level
    Write-Log -Level $Level
}

function Write-LogProgress {
    <#
    .SYNOPSIS
        Writes progress information to the log.

    .DESCRIPTION
        Logs progress without cluttering the file (file logging only every Nth call).

    .PARAMETER Activity
        Activity description.

    .PARAMETER Current
        Current item number.

    .PARAMETER Total
        Total number of items.

    .PARAMETER FileLogInterval
        Write to file every N items. Default is 50.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Activity,

        [Parameter(Mandatory = $true)]
        [int]$Current,

        [Parameter(Mandatory = $true)]
        [int]$Total,

        [Parameter(Mandatory = $false)]
        [int]$FileLogInterval = 50
    )

    $percentComplete = [math]::Round(($Current / $Total) * 100, 1)

    # Always show progress bar in console
    Write-Progress -Activity $Activity `
        -Status "Processing item $Current of $Total - $percentComplete% complete" `
        -PercentComplete $percentComplete

    # Log to file only at intervals to avoid spam
    if ($Current % $FileLogInterval -eq 0 -or $Current -eq $Total) {
        Write-Log -Message "$Activity - $Current / $Total ($percentComplete%)" -Level Info -NoConsole
    }
}

function Get-LogPath {
    <#
    .SYNOPSIS
        Gets the current log file path.

    .OUTPUTS
        String path to log file, or $null if not set.
    #>

    [CmdletBinding()]
    param()

    return $script:LogFilePath
}

function Close-Logger {
    <#
    .SYNOPSIS
        Closes the logger and writes a footer.

    .DESCRIPTION
        Writes a closing message to the log file and resets logger state.
    #>

    [CmdletBinding()]
    param()

    if ($script:LogToFile -and $script:LogFilePath) {
        $footer = @"

=================================================================
Log session ended: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')
=================================================================
"@

        try {
            $footer | Out-File -FilePath $script:LogFilePath -Append -Encoding UTF8
        }
        catch {
            Write-Warning "Failed to write log footer: $_"
        }
    }

    # Clear progress bar
    Write-Progress -Activity "Processing" -Completed

    $script:LogFilePath = $null
    $script:LogToFile = $false
}

Export-ModuleMember -Function @(
    'Initialize-Logger',
    'Write-Log',
    'Write-LogSection',
    'Write-LogProgress',
    'Get-LogPath',
    'Close-Logger'
)