<#
.SYNOPSIS
    Windows Event Log Prometheus Metrics Exporter
.DESCRIPTION
    Prometheus exporter for Windows Event Log critical and error events. Scans
    System, Application, and Security event logs for errors, warnings, and
    critical events. Exports counts, rates, and recent event details as
    Prometheus-compatible text format for windows_exporter textfile collector.
.PARAMETER Mode
    Output mode: 'stdout' (default), 'textfile', or 'http'
.PARAMETER Port
    HTTP port for http mode (default: 9199)
.PARAMETER TextfileDir
    Directory for textfile collector output (default: C:\ProgramData\node_exporter)
.PARAMETER OutputFile
    Custom output file path
.PARAMETER InstallScheduledTask
    Switch to create a scheduled task for auto-start on system boot
.PARAMETER TaskIntervalMinutes
    Interval in minutes for the scheduled task (default: 5)
.NOTES
    Author:  Phil Connor
    Contact: contact@mylinux.work
    Website: https://mylinux.work
    License: MIT
    Version: 1.0

    Metrics Exported:
      Core Status:
        - windows_eventlog_up
        - windows_eventlog_exporter_info{version}

      System Log:
        - windows_eventlog_system_critical_total_1h
        - windows_eventlog_system_critical_total_6h
        - windows_eventlog_system_critical_total_24h
        - windows_eventlog_system_error_total_1h
        - windows_eventlog_system_error_total_6h
        - windows_eventlog_system_error_total_24h
        - windows_eventlog_system_warning_total_1h
        - windows_eventlog_system_warning_total_6h
        - windows_eventlog_system_warning_total_24h
        - windows_eventlog_system_top_source_count{source}
        - windows_eventlog_system_events_per_hour

      Application Log:
        - windows_eventlog_application_critical_total_1h
        - windows_eventlog_application_critical_total_6h
        - windows_eventlog_application_critical_total_24h
        - windows_eventlog_application_error_total_1h
        - windows_eventlog_application_error_total_6h
        - windows_eventlog_application_error_total_24h
        - windows_eventlog_application_warning_total_1h
        - windows_eventlog_application_warning_total_6h
        - windows_eventlog_application_warning_total_24h
        - windows_eventlog_application_top_source_count{source}
        - windows_eventlog_application_events_per_hour
        - windows_eventlog_application_crash_total_24h
        - windows_eventlog_application_dotnet_error_total_24h

      Security Log (requires admin):
        - windows_eventlog_security_failed_logon_total_24h
        - windows_eventlog_security_account_lockout_total_24h
        - windows_eventlog_security_privilege_escalation_total_24h
        - windows_eventlog_security_audit_policy_change_total_24h

      Service Failures:
        - windows_eventlog_service_stopped_autostart{service}
        - windows_eventlog_service_crash_total_24h

      Disk Events:
        - windows_eventlog_disk_error_total_24h{source}

      System Stability:
        - windows_eventlog_unexpected_shutdown_total_24h
        - windows_eventlog_bugcheck_total_24h

      Summary:
        - windows_eventlog_total_critical_error_all_logs
        - windows_eventlog_newest_event_timestamp{log}

      Exporter:
        - windows_eventlog_exporter_duration_seconds
        - windows_eventlog_exporter_last_run_timestamp
#>

param(
    [ValidateSet('stdout', 'textfile', 'http')]
    [string]$Mode = 'stdout',

    [int]$Port = 9199,

    [string]$TextfileDir = 'C:\ProgramData\node_exporter',

    [string]$OutputFile,

    [switch]$InstallScheduledTask,

    [int]$TaskIntervalMinutes = 5
)

# Create a scheduled task to run this script every $TaskIntervalMinutes minutes
# The task will run as SYSTEM and will be set to run at startup
if ($InstallScheduledTask) {
    $taskName = "WindowsEventLogExporter"
    $existingTask = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue

    if (-not $existingTask) {
        $taskAction = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-NoProfile -ExecutionPolicy Bypass -File `"$($MyInvocation.MyCommand.Path)`" -Mode textfile"

        if (-not $TaskIntervalMinutes -or $TaskIntervalMinutes -le 0) {
            throw "TaskIntervalMinutes must be a positive integer"
        }

        $taskTrigger = New-ScheduledTaskTrigger -Once -At (Get-Date).AddMinutes(1) -RepetitionInterval (New-TimeSpan -Minutes $TaskIntervalMinutes) -RepetitionDuration (New-TimeSpan -Days 365)
        $taskPrincipal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest

        try {
            Write-Host "Creating scheduled task: $taskName"
            Register-ScheduledTask -TaskName $taskName -Action $taskAction -Trigger $taskTrigger -Principal $taskPrincipal -Description "Exports Windows event log metrics for Prometheus every $TaskIntervalMinutes minutes"

            $createdTask = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
            if (-not $createdTask) {
                throw "Failed to verify scheduled task creation"
            }
            Write-Host "Successfully created scheduled task: $taskName" -ForegroundColor Green
        } catch {
            Write-Error "Failed to create auto-start task: $($_.Exception.Message)"
            throw
        }
    } else {
        Write-Host "Scheduled task '$taskName' already exists, skipping creation"
    }
}

$ErrorActionPreference = 'SilentlyContinue'

# ============================================================================
# HELPER FUNCTIONS
# ============================================================================

function Get-UnixTimestamp {
    [int][double]::Parse((Get-Date -UFormat '%s'))
}

function Format-MetricValue {
    param([double]$Value, [int]$Decimals = 2)
    [math]::Round($Value, $Decimals)
}

function Get-EventCount {
    param(
        [string]$LogName,
        [int[]]$Level,
        [int[]]$Id,
        [string[]]$ProviderName,
        [int]$Hours = 24
    )

    $filter = @{
        LogName   = $LogName
        StartTime = (Get-Date).AddHours(-$Hours)
    }
    if ($Level)        { $filter['Level'] = $Level }
    if ($Id)           { $filter['Id'] = $Id }
    if ($ProviderName) { $filter['ProviderName'] = $ProviderName }

    try {
        $events = Get-WinEvent -FilterHashtable $filter -ErrorAction Stop
        return @($events).Count
    } catch {
        return 0
    }
}

function Get-EventCountToTimestamp {
    param(
        [string]$LogName,
        [int[]]$Level
    )

    try {
        $events = Get-WinEvent -FilterHashtable @{
            LogName = $LogName
            Level   = $Level
        } -MaxEvents 1 -ErrorAction Stop

        if ($events -and $events.Count -gt 0) {
            return [int][double]::Parse($events[0].TimeCreated.ToUniversalTime().ToString('yyyyMMddHHmmss').Substring(0,10))
        }
    } catch {}

    try {
        $events = Get-WinEvent -FilterHashtable @{
            LogName = $LogName
        } -MaxEvents 1 -ErrorAction Stop

        if ($events -and $events.Count -gt 0) {
            $epoch = [datetime]'1970-01-01'
            return [int]($events[0].TimeCreated.ToUniversalTime() - $epoch).TotalSeconds
        }
    } catch {}

    return 0
}

# ============================================================================
# SYSTEM LOG EVENTS
# ============================================================================

function Get-SystemLogMetrics {
    $sb = [System.Text.StringBuilder]::new()

    try {
        # Critical events (Level 1)
        foreach ($window in @(@{Suffix='1h'; Hours=1}, @{Suffix='6h'; Hours=6}, @{Suffix='24h'; Hours=24})) {
            $count = Get-EventCount -LogName 'System' -Level 1 -Hours $window.Hours
            [void]$sb.AppendLine("# HELP windows_eventlog_system_critical_total_$($window.Suffix) System log critical events in last $($window.Suffix)")
            [void]$sb.AppendLine("# TYPE windows_eventlog_system_critical_total_$($window.Suffix) gauge")
            [void]$sb.AppendLine("windows_eventlog_system_critical_total_$($window.Suffix) $count")
            [void]$sb.AppendLine('')
        }

        # Error events (Level 2)
        foreach ($window in @(@{Suffix='1h'; Hours=1}, @{Suffix='6h'; Hours=6}, @{Suffix='24h'; Hours=24})) {
            $count = Get-EventCount -LogName 'System' -Level 2 -Hours $window.Hours
            [void]$sb.AppendLine("# HELP windows_eventlog_system_error_total_$($window.Suffix) System log error events in last $($window.Suffix)")
            [void]$sb.AppendLine("# TYPE windows_eventlog_system_error_total_$($window.Suffix) gauge")
            [void]$sb.AppendLine("windows_eventlog_system_error_total_$($window.Suffix) $count")
            [void]$sb.AppendLine('')
        }

        # Warning events (Level 3)
        foreach ($window in @(@{Suffix='1h'; Hours=1}, @{Suffix='6h'; Hours=6}, @{Suffix='24h'; Hours=24})) {
            $count = Get-EventCount -LogName 'System' -Level 3 -Hours $window.Hours
            [void]$sb.AppendLine("# HELP windows_eventlog_system_warning_total_$($window.Suffix) System log warning events in last $($window.Suffix)")
            [void]$sb.AppendLine("# TYPE windows_eventlog_system_warning_total_$($window.Suffix) gauge")
            [void]$sb.AppendLine("windows_eventlog_system_warning_total_$($window.Suffix) $count")
            [void]$sb.AppendLine('')
        }

        # Top event sources
        try {
            $systemEvents = Get-WinEvent -FilterHashtable @{
                LogName   = 'System'
                Level     = 1, 2
                StartTime = (Get-Date).AddHours(-24)
            } -ErrorAction Stop

            $topSources = $systemEvents | Group-Object ProviderName |
                Sort-Object Count -Descending |
                Select-Object -First 5

            [void]$sb.AppendLine('# HELP windows_eventlog_system_top_source_count Event count for top sources in System log')
            [void]$sb.AppendLine('# TYPE windows_eventlog_system_top_source_count gauge')
            foreach ($source in $topSources) {
                $sourceName = $source.Name -replace '"', ''
                [void]$sb.AppendLine("windows_eventlog_system_top_source_count{source=`"$sourceName`"} $($source.Count)")
            }
            [void]$sb.AppendLine('')
        } catch {
            [void]$sb.AppendLine('# HELP windows_eventlog_system_top_source_count Event count for top sources in System log')
            [void]$sb.AppendLine('# TYPE windows_eventlog_system_top_source_count gauge')
            [void]$sb.AppendLine('')
        }

        # Events per hour
        $total24h = Get-EventCount -LogName 'System' -Level 1, 2 -Hours 24
        $perHour = Format-MetricValue ($total24h / 24)
        [void]$sb.AppendLine('# HELP windows_eventlog_system_events_per_hour System log error/critical event rate per hour')
        [void]$sb.AppendLine('# TYPE windows_eventlog_system_events_per_hour gauge')
        [void]$sb.AppendLine("windows_eventlog_system_events_per_hour $perHour")
        [void]$sb.AppendLine('')
    }
    catch {
        Write-Warning "Failed to collect system log metrics: $_"
    }

    $sb.ToString()
}

# ============================================================================
# APPLICATION LOG EVENTS
# ============================================================================

function Get-ApplicationLogMetrics {
    $sb = [System.Text.StringBuilder]::new()

    try {
        # Critical events (Level 1)
        foreach ($window in @(@{Suffix='1h'; Hours=1}, @{Suffix='6h'; Hours=6}, @{Suffix='24h'; Hours=24})) {
            $count = Get-EventCount -LogName 'Application' -Level 1 -Hours $window.Hours
            [void]$sb.AppendLine("# HELP windows_eventlog_application_critical_total_$($window.Suffix) Application log critical events in last $($window.Suffix)")
            [void]$sb.AppendLine("# TYPE windows_eventlog_application_critical_total_$($window.Suffix) gauge")
            [void]$sb.AppendLine("windows_eventlog_application_critical_total_$($window.Suffix) $count")
            [void]$sb.AppendLine('')
        }

        # Error events (Level 2)
        foreach ($window in @(@{Suffix='1h'; Hours=1}, @{Suffix='6h'; Hours=6}, @{Suffix='24h'; Hours=24})) {
            $count = Get-EventCount -LogName 'Application' -Level 2 -Hours $window.Hours
            [void]$sb.AppendLine("# HELP windows_eventlog_application_error_total_$($window.Suffix) Application log error events in last $($window.Suffix)")
            [void]$sb.AppendLine("# TYPE windows_eventlog_application_error_total_$($window.Suffix) gauge")
            [void]$sb.AppendLine("windows_eventlog_application_error_total_$($window.Suffix) $count")
            [void]$sb.AppendLine('')
        }

        # Warning events (Level 3)
        foreach ($window in @(@{Suffix='1h'; Hours=1}, @{Suffix='6h'; Hours=6}, @{Suffix='24h'; Hours=24})) {
            $count = Get-EventCount -LogName 'Application' -Level 3 -Hours $window.Hours
            [void]$sb.AppendLine("# HELP windows_eventlog_application_warning_total_$($window.Suffix) Application log warning events in last $($window.Suffix)")
            [void]$sb.AppendLine("# TYPE windows_eventlog_application_warning_total_$($window.Suffix) gauge")
            [void]$sb.AppendLine("windows_eventlog_application_warning_total_$($window.Suffix) $count")
            [void]$sb.AppendLine('')
        }

        # Top event sources
        try {
            $appEvents = Get-WinEvent -FilterHashtable @{
                LogName   = 'Application'
                Level     = 1, 2
                StartTime = (Get-Date).AddHours(-24)
            } -ErrorAction Stop

            $topSources = $appEvents | Group-Object ProviderName |
                Sort-Object Count -Descending |
                Select-Object -First 5

            [void]$sb.AppendLine('# HELP windows_eventlog_application_top_source_count Event count for top sources in Application log')
            [void]$sb.AppendLine('# TYPE windows_eventlog_application_top_source_count gauge')
            foreach ($source in $topSources) {
                $sourceName = $source.Name -replace '"', ''
                [void]$sb.AppendLine("windows_eventlog_application_top_source_count{source=`"$sourceName`"} $($source.Count)")
            }
            [void]$sb.AppendLine('')
        } catch {
            [void]$sb.AppendLine('# HELP windows_eventlog_application_top_source_count Event count for top sources in Application log')
            [void]$sb.AppendLine('# TYPE windows_eventlog_application_top_source_count gauge')
            [void]$sb.AppendLine('')
        }

        # Events per hour
        $total24h = Get-EventCount -LogName 'Application' -Level 1, 2 -Hours 24
        $perHour = Format-MetricValue ($total24h / 24)
        [void]$sb.AppendLine('# HELP windows_eventlog_application_events_per_hour Application log error/critical event rate per hour')
        [void]$sb.AppendLine('# TYPE windows_eventlog_application_events_per_hour gauge')
        [void]$sb.AppendLine("windows_eventlog_application_events_per_hour $perHour")
        [void]$sb.AppendLine('')

        # Application crash events (WER)
        $crashCount = Get-EventCount -LogName 'Application' -ProviderName 'Application Error', 'Windows Error Reporting' -Hours 24
        [void]$sb.AppendLine('# HELP windows_eventlog_application_crash_total_24h Application crash events (WER) in last 24 hours')
        [void]$sb.AppendLine('# TYPE windows_eventlog_application_crash_total_24h gauge')
        [void]$sb.AppendLine("windows_eventlog_application_crash_total_24h $crashCount")
        [void]$sb.AppendLine('')

        # .NET runtime errors
        $dotnetCount = Get-EventCount -LogName 'Application' -ProviderName '.NET Runtime' -Level 1, 2 -Hours 24
        [void]$sb.AppendLine('# HELP windows_eventlog_application_dotnet_error_total_24h .NET runtime errors in last 24 hours')
        [void]$sb.AppendLine('# TYPE windows_eventlog_application_dotnet_error_total_24h gauge')
        [void]$sb.AppendLine("windows_eventlog_application_dotnet_error_total_24h $dotnetCount")
        [void]$sb.AppendLine('')
    }
    catch {
        Write-Warning "Failed to collect application log metrics: $_"
    }

    $sb.ToString()
}

# ============================================================================
# SECURITY LOG EVENTS (requires admin)
# ============================================================================

function Get-SecurityLogMetrics {
    $sb = [System.Text.StringBuilder]::new()

    try {
        # Failed logon attempts (Event ID 4625)
        $failedLogons = Get-EventCount -LogName 'Security' -Id 4625 -Hours 24
        [void]$sb.AppendLine('# HELP windows_eventlog_security_failed_logon_total_24h Failed logon attempts (4625) in last 24 hours')
        [void]$sb.AppendLine('# TYPE windows_eventlog_security_failed_logon_total_24h gauge')
        [void]$sb.AppendLine("windows_eventlog_security_failed_logon_total_24h $failedLogons")
        [void]$sb.AppendLine('')

        # Account lockouts (Event ID 4740)
        $lockouts = Get-EventCount -LogName 'Security' -Id 4740 -Hours 24
        [void]$sb.AppendLine('# HELP windows_eventlog_security_account_lockout_total_24h Account lockout events (4740) in last 24 hours')
        [void]$sb.AppendLine('# TYPE windows_eventlog_security_account_lockout_total_24h gauge')
        [void]$sb.AppendLine("windows_eventlog_security_account_lockout_total_24h $lockouts")
        [void]$sb.AppendLine('')

        # Privilege escalation (Event IDs 4672, 4673)
        $privEsc = Get-EventCount -LogName 'Security' -Id 4672, 4673 -Hours 24
        [void]$sb.AppendLine('# HELP windows_eventlog_security_privilege_escalation_total_24h Privilege escalation events (4672/4673) in last 24 hours')
        [void]$sb.AppendLine('# TYPE windows_eventlog_security_privilege_escalation_total_24h gauge')
        [void]$sb.AppendLine("windows_eventlog_security_privilege_escalation_total_24h $privEsc")
        [void]$sb.AppendLine('')

        # Audit policy changes (Event ID 4719)
        $auditChanges = Get-EventCount -LogName 'Security' -Id 4719 -Hours 24
        [void]$sb.AppendLine('# HELP windows_eventlog_security_audit_policy_change_total_24h Audit policy change events (4719) in last 24 hours')
        [void]$sb.AppendLine('# TYPE windows_eventlog_security_audit_policy_change_total_24h gauge')
        [void]$sb.AppendLine("windows_eventlog_security_audit_policy_change_total_24h $auditChanges")
        [void]$sb.AppendLine('')
    }
    catch {
        Write-Warning "Failed to collect security log metrics (admin required): $_"
    }

    $sb.ToString()
}

# ============================================================================
# SERVICE FAILURES
# ============================================================================

function Get-ServiceFailureMetrics {
    $sb = [System.Text.StringBuilder]::new()

    try {
        # Auto-start services that are stopped
        $stoppedAutoStart = Get-Service | Where-Object {
            $_.StartType -eq 'Automatic' -and $_.Status -ne 'Running'
        }

        [void]$sb.AppendLine('# HELP windows_eventlog_service_stopped_autostart Auto-start services currently in stopped state')
        [void]$sb.AppendLine('# TYPE windows_eventlog_service_stopped_autostart gauge')
        if ($stoppedAutoStart) {
            foreach ($svc in $stoppedAutoStart) {
                $svcName = $svc.Name -replace '"', ''
                [void]$sb.AppendLine("windows_eventlog_service_stopped_autostart{service=`"$svcName`"} 1")
            }
        }
        [void]$sb.AppendLine('')

        # Service crash events (SCM Event IDs 7031, 7034)
        $crashCount = Get-EventCount -LogName 'System' -ProviderName 'Service Control Manager' -Id 7031, 7034 -Hours 24
        [void]$sb.AppendLine('# HELP windows_eventlog_service_crash_total_24h Service crash events in last 24 hours')
        [void]$sb.AppendLine('# TYPE windows_eventlog_service_crash_total_24h gauge')
        [void]$sb.AppendLine("windows_eventlog_service_crash_total_24h $crashCount")
        [void]$sb.AppendLine('')
    }
    catch {
        Write-Warning "Failed to collect service failure metrics: $_"
    }

    $sb.ToString()
}

# ============================================================================
# DISK EVENTS
# ============================================================================

function Get-DiskEventMetrics {
    $sb = [System.Text.StringBuilder]::new()

    try {
        $diskSources = @('disk', 'ntfs', 'volmgr', 'Ntfs', 'Disk', 'volsnap')

        [void]$sb.AppendLine('# HELP windows_eventlog_disk_error_total_24h Disk errors in last 24 hours by source')
        [void]$sb.AppendLine('# TYPE windows_eventlog_disk_error_total_24h gauge')

        foreach ($source in $diskSources) {
            $count = Get-EventCount -LogName 'System' -ProviderName $source -Level 1, 2, 3 -Hours 24
            if ($count -gt 0) {
                [void]$sb.AppendLine("windows_eventlog_disk_error_total_24h{source=`"$source`"} $count")
            }
        }
        [void]$sb.AppendLine('')
    }
    catch {
        Write-Warning "Failed to collect disk event metrics: $_"
    }

    $sb.ToString()
}

# ============================================================================
# SYSTEM STABILITY
# ============================================================================

function Get-SystemStabilityMetrics {
    $sb = [System.Text.StringBuilder]::new()

    try {
        # Unexpected shutdown events (Event ID 6008)
        $unexpectedShutdowns = Get-EventCount -LogName 'System' -Id 6008 -Hours 24
        [void]$sb.AppendLine('# HELP windows_eventlog_unexpected_shutdown_total_24h Unexpected shutdown events (6008) in last 24 hours')
        [void]$sb.AppendLine('# TYPE windows_eventlog_unexpected_shutdown_total_24h gauge')
        [void]$sb.AppendLine("windows_eventlog_unexpected_shutdown_total_24h $unexpectedShutdowns")
        [void]$sb.AppendLine('')

        # Bugcheck events
        $bugchecks = 0
        try {
            $bugcheckEvents = Get-WinEvent -FilterHashtable @{
                LogName   = 'System'
                ProviderName = 'Microsoft-Windows-WER-SystemErrorReporting', 'BugCheck'
                StartTime = (Get-Date).AddHours(-24)
            } -ErrorAction Stop
            $bugchecks = @($bugcheckEvents).Count
        } catch {}
        [void]$sb.AppendLine('# HELP windows_eventlog_bugcheck_total_24h Bugcheck events in last 24 hours')
        [void]$sb.AppendLine('# TYPE windows_eventlog_bugcheck_total_24h gauge')
        [void]$sb.AppendLine("windows_eventlog_bugcheck_total_24h $bugchecks")
        [void]$sb.AppendLine('')
    }
    catch {
        Write-Warning "Failed to collect system stability metrics: $_"
    }

    $sb.ToString()
}

# ============================================================================
# SUMMARY
# ============================================================================

function Get-SummaryMetrics {
    $sb = [System.Text.StringBuilder]::new()

    try {
        # Total critical+error across all logs
        $sysCritErr = Get-EventCount -LogName 'System' -Level 1, 2 -Hours 24
        $appCritErr = Get-EventCount -LogName 'Application' -Level 1, 2 -Hours 24
        $total = $sysCritErr + $appCritErr

        [void]$sb.AppendLine('# HELP windows_eventlog_total_critical_error_all_logs Total critical and error events across all logs (24h)')
        [void]$sb.AppendLine('# TYPE windows_eventlog_total_critical_error_all_logs gauge')
        [void]$sb.AppendLine("windows_eventlog_total_critical_error_all_logs $total")
        [void]$sb.AppendLine('')

        # Newest event timestamp per log
        [void]$sb.AppendLine('# HELP windows_eventlog_newest_event_timestamp Unix timestamp of newest event per log')
        [void]$sb.AppendLine('# TYPE windows_eventlog_newest_event_timestamp gauge')

        foreach ($logName in @('System', 'Application', 'Security')) {
            try {
                $newestEvent = Get-WinEvent -LogName $logName -MaxEvents 1 -ErrorAction Stop
                if ($newestEvent) {
                    $epoch = [datetime]'1970-01-01'
                    $ts = [int]($newestEvent.TimeCreated.ToUniversalTime() - $epoch).TotalSeconds
                    [void]$sb.AppendLine("windows_eventlog_newest_event_timestamp{log=`"$logName`"} $ts")
                }
            } catch {}
        }
        [void]$sb.AppendLine('')
    }
    catch {
        Write-Warning "Failed to collect summary metrics: $_"
    }

    $sb.ToString()
}

# ============================================================================
# COLLECT ALL METRICS
# ============================================================================

function Get-AllMetrics {
    $scriptStart = Get-Date
    $sb = [System.Text.StringBuilder]::new()

    # Exporter up
    [void]$sb.AppendLine('# HELP windows_eventlog_up Exporter status (1=up, 0=down)')
    [void]$sb.AppendLine('# TYPE windows_eventlog_up gauge')
    [void]$sb.AppendLine('windows_eventlog_up 1')
    [void]$sb.AppendLine('')

    # Exporter info
    [void]$sb.AppendLine('# HELP windows_eventlog_exporter_info Exporter version information')
    [void]$sb.AppendLine('# TYPE windows_eventlog_exporter_info gauge')
    [void]$sb.AppendLine('windows_eventlog_exporter_info{version="1.0"} 1')
    [void]$sb.AppendLine('')

    # Collect all sections
    [void]$sb.Append((Get-SystemLogMetrics))
    [void]$sb.Append((Get-ApplicationLogMetrics))
    [void]$sb.Append((Get-SecurityLogMetrics))
    [void]$sb.Append((Get-ServiceFailureMetrics))
    [void]$sb.Append((Get-DiskEventMetrics))
    [void]$sb.Append((Get-SystemStabilityMetrics))
    [void]$sb.Append((Get-SummaryMetrics))

    # Exporter runtime
    $scriptEnd = Get-Date
    $duration = Format-MetricValue ($scriptEnd - $scriptStart).TotalSeconds
    $timestamp = Get-UnixTimestamp

    [void]$sb.AppendLine('# HELP windows_eventlog_exporter_duration_seconds Time to generate all metrics')
    [void]$sb.AppendLine('# TYPE windows_eventlog_exporter_duration_seconds gauge')
    [void]$sb.AppendLine("windows_eventlog_exporter_duration_seconds $duration")
    [void]$sb.AppendLine('')
    [void]$sb.AppendLine('# HELP windows_eventlog_exporter_last_run_timestamp Unix timestamp of last successful run')
    [void]$sb.AppendLine('# TYPE windows_eventlog_exporter_last_run_timestamp gauge')
    [void]$sb.AppendLine("windows_eventlog_exporter_last_run_timestamp $timestamp")
    [void]$sb.AppendLine('')

    $sb.ToString()
}

# ============================================================================
# HTTP SERVER MODE
# ============================================================================

function Start-HttpServer {
    param([int]$ListenPort)

    $prefix = "http://+:$ListenPort/"
    $listener = [System.Net.HttpListener]::new()
    $listener.Prefixes.Add($prefix)

    try {
        $listener.Start()
        Write-Host "Starting Windows event log exporter on port $ListenPort..." -ForegroundColor Green
        Write-Host "Metrics available at http://localhost:$ListenPort/metrics"

        while ($listener.IsListening) {
            $context  = $listener.GetContext()
            $request  = $context.Request
            $response = $context.Response

            if ($request.Url.AbsolutePath -eq '/metrics') {
                $metrics = Get-AllMetrics
                $buffer  = [System.Text.Encoding]::UTF8.GetBytes($metrics)
                $response.ContentType = 'text/plain; version=0.0.4; charset=utf-8'
            }
            else {
                $html = @"
<!DOCTYPE html>
<html>
<head><title>Windows Event Log Exporter v1.0</title></head>
<body>
<h1>Windows Event Log Exporter v1.0</h1>
<p><a href="/metrics">Metrics</a></p>
<h2>Sections</h2>
<ul>
<li>System log events (critical, error, warning counts)</li>
<li>Application log events (crashes, .NET runtime errors)</li>
<li>Security log events (failed logons, lockouts, privilege escalation)</li>
<li>Service failures (stopped auto-start services, crash events)</li>
<li>Disk events (disk, ntfs, volmgr errors)</li>
<li>System stability (unexpected shutdowns, bugchecks)</li>
<li>Summary (total events, newest timestamps per log)</li>
</ul>
</body>
</html>
"@
                $buffer = [System.Text.Encoding]::UTF8.GetBytes($html)
                $response.ContentType = 'text/html; charset=utf-8'
            }

            $response.ContentLength64 = $buffer.Length
            $response.OutputStream.Write($buffer, 0, $buffer.Length)
            $response.OutputStream.Close()
        }
    }
    catch {
        Write-Error "HTTP server error: $_"
        Write-Error "If access denied, run: netsh http add urlacl url=http://+:$ListenPort/ user=Everyone"
    }
    finally {
        if ($listener.IsListening) {
            $listener.Stop()
        }
    }
}

# ============================================================================
# MAIN EXECUTION
# ============================================================================

switch ($Mode) {
    'http' {
        Start-HttpServer -ListenPort $Port
    }
    'textfile' {
        if (-not $OutputFile) {
            $OutputFile = Join-Path $TextfileDir 'windows_eventlog.prom'
        }

        $outputDir = Split-Path $OutputFile -Parent
        if (-not (Test-Path $outputDir)) {
            New-Item -Path $outputDir -ItemType Directory -Force | Out-Null
        }

        $tempFile = Join-Path $outputDir ".windows_eventlog_metrics.$PID.tmp"

        try {
            $metrics = Get-AllMetrics
            $metrics | Out-File -FilePath $tempFile -Encoding utf8 -NoNewline

            $lineCount = ($metrics -split "`n").Count
            if ($lineCount -lt 10) {
                Remove-Item -Path $tempFile -Force -ErrorAction SilentlyContinue
                Write-Error "Metrics file too small ($lineCount lines), keeping previous"
                exit 1
            }

            Move-Item -Path $tempFile -Destination $OutputFile -Force
            Write-Host "Metrics written to $OutputFile ($lineCount lines)" -ForegroundColor Green
        }
        catch {
            Remove-Item -Path $tempFile -Force -ErrorAction SilentlyContinue
            Write-Error "Failed to generate metrics: $_"
            exit 1
        }
    }
    default {
        Get-AllMetrics | Write-Output
    }
}
