# Exchange Metrics Collector - Outputs Prometheus-compatible metrics
# Requires Exchange Management Shell and appropriate permissions

param(
    [switch]$InstallScheduledTask,
    [int]$TaskIntervalMinutes = 5
)

if ($InstallScheduledTask) {
    $taskName = "ExchangeMetricsExporter"
    $existingTask = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue

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

        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 Exchange 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"
    }
}

$StartTime = Get-Date
$Hostname = $env:COMPUTERNAME

# Try to load Exchange Management Shell if not already loaded
if (-not (Get-Command Get-TransportServer -ErrorAction SilentlyContinue)) {
    try {
        Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn -ErrorAction SilentlyContinue
    } catch {
        # Try loading as module for newer Exchange versions
        try {
            $ExchangeInstallPath = (Get-ItemProperty HKLM:\SOFTWARE\Microsoft\ExchangeServer\v15\Setup -ErrorAction SilentlyContinue).MsiInstallPath
            if ($ExchangeInstallPath) {
                . "$ExchangeInstallPath\bin\RemoteExchange.ps1" -ErrorAction SilentlyContinue
                Connect-ExchangeServer -auto -ErrorAction SilentlyContinue
            }
        } catch {}
    }
}

# Helper function to safely get counter values
function Get-SafeCounter {
    param([string]$CounterPath)
    try {
        $counter = Get-Counter -Counter $CounterPath -ErrorAction SilentlyContinue
        if ($counter -and $counter.CounterSamples) {
            return [math]::Max(0, [math]::Round($counter.CounterSamples[0].CookedValue, 2))
        }
    } catch {}
    return 0
}

# Helper function to safely count event log entries
function Get-EventLogCount {
    param(
        [string]$LogName,
        [string]$Source,
        [int]$EventId,
        [int]$Hours = 24
    )
    try {
        $startDate = (Get-Date).AddHours(-$Hours)
        $filter = @{
            LogName = $LogName
            StartTime = $startDate
        }
        if ($Source) { $filter['ProviderName'] = $Source }
        if ($EventId) { $filter['Id'] = $EventId }
        
        return (Get-WinEvent -FilterHashtable $filter -ErrorAction SilentlyContinue | Measure-Object).Count
    } catch {
        return 0
    }
}

# ============================================================================
# TRANSPORT QUEUES
# ============================================================================
Write-Output "# HELP exchange_queue_length Number of messages in each transport queue"
Write-Output "# TYPE exchange_queue_length gauge"

try {
    $queues = Get-Queue -ErrorAction SilentlyContinue
    if ($queues) {
        foreach ($queue in $queues) {
            $queueType = $queue.DeliveryType -replace '\s+', '_'
            $queueStatus = $queue.Status
            $nextHop = ($queue.NextHopDomain -replace '["\s]', '') -replace '\.', '_'
            Write-Output "exchange_queue_length{queue_type=`"$queueType`",status=`"$queueStatus`",next_hop=`"$nextHop`",hostname=`"$Hostname`"} $($queue.MessageCount)"
        }
    }
} catch {}

# Queue totals by status
Write-Output "# HELP exchange_queue_total_messages Total messages in queues by status"
Write-Output "# TYPE exchange_queue_total_messages gauge"
try {
    $queueStats = Get-Queue -ErrorAction SilentlyContinue | Group-Object Status
    foreach ($stat in $queueStats) {
        $total = ($stat.Group | Measure-Object -Property MessageCount -Sum).Sum
        Write-Output "exchange_queue_total_messages{status=`"$($stat.Name)`",hostname=`"$Hostname`"} $total"
    }
} catch {}

# Submission queue (messages waiting to be categorized)
Write-Output "# HELP exchange_submission_queue_length Messages in submission queue"
Write-Output "# TYPE exchange_submission_queue_length gauge"
$submissionQueue = Get-SafeCounter "\MSExchangeTransport Queues(_total)\Submission Queue Length"
Write-Output "exchange_submission_queue_length{hostname=`"$Hostname`"} $submissionQueue"

# Poison queue (messages that failed repeatedly)
Write-Output "# HELP exchange_poison_queue_length Messages in poison queue"
Write-Output "# TYPE exchange_poison_queue_length gauge"
$poisonQueue = Get-SafeCounter "\MSExchangeTransport Queues(_total)\Poison Queue Length"
Write-Output "exchange_poison_queue_length{hostname=`"$Hostname`"} $poisonQueue"

# Unreachable queue
Write-Output "# HELP exchange_unreachable_queue_length Messages in unreachable queue"
Write-Output "# TYPE exchange_unreachable_queue_length gauge"
$unreachableQueue = Get-SafeCounter "\MSExchangeTransport Queues(_total)\Unreachable Queue Length"
Write-Output "exchange_unreachable_queue_length{hostname=`"$Hostname`"} $unreachableQueue"

# Active mailbox delivery queue
Write-Output "# HELP exchange_active_mailbox_queue_length Active mailbox delivery queue"
Write-Output "# TYPE exchange_active_mailbox_queue_length gauge"
$activeMailbox = Get-SafeCounter "\MSExchangeTransport Queues(_total)\Active Mailbox Delivery Queue Length"
Write-Output "exchange_active_mailbox_queue_length{hostname=`"$Hostname`"} $activeMailbox"

# Active non-SMTP delivery queue
Write-Output "# HELP exchange_active_nonsmtp_queue_length Active non-SMTP delivery queue"
Write-Output "# TYPE exchange_active_nonsmtp_queue_length gauge"
$activeNonSmtp = Get-SafeCounter "\MSExchangeTransport Queues(_total)\Active Non-Smtp Delivery Queue Length"
Write-Output "exchange_active_nonsmtp_queue_length{hostname=`"$Hostname`"} $activeNonSmtp"

# Retry mailbox delivery queue
Write-Output "# HELP exchange_retry_mailbox_queue_length Retry mailbox delivery queue"
Write-Output "# TYPE exchange_retry_mailbox_queue_length gauge"
$retryMailbox = Get-SafeCounter "\MSExchangeTransport Queues(_total)\Retry Mailbox Delivery Queue Length"
Write-Output "exchange_retry_mailbox_queue_length{hostname=`"$Hostname`"} $retryMailbox"

# ============================================================================
# MESSAGE THROUGHPUT
# ============================================================================
Write-Output "# HELP exchange_messages_received_total Total messages received"
Write-Output "# TYPE exchange_messages_received_total counter"
$messagesReceived = Get-SafeCounter "\MSExchangeTransport SmtpReceive(_total)\Messages Received Total"
Write-Output "exchange_messages_received_total{hostname=`"$Hostname`"} $messagesReceived"

Write-Output "# HELP exchange_messages_sent_total Total messages sent"
Write-Output "# TYPE exchange_messages_sent_total counter"
$messagesSent = Get-SafeCounter "\MSExchangeTransport SmtpSend(_total)\Messages Sent Total"
Write-Output "exchange_messages_sent_total{hostname=`"$Hostname`"} $messagesSent"

Write-Output "# HELP exchange_messages_received_per_second Messages received per second"
Write-Output "# TYPE exchange_messages_received_per_second gauge"
$messagesReceivedSec = Get-SafeCounter "\MSExchangeTransport SmtpReceive(_total)\Messages Received/sec"
Write-Output "exchange_messages_received_per_second{hostname=`"$Hostname`"} $messagesReceivedSec"

Write-Output "# HELP exchange_messages_sent_per_second Messages sent per second"
Write-Output "# TYPE exchange_messages_sent_per_second gauge"
$messagesSentSec = Get-SafeCounter "\MSExchangeTransport SmtpSend(_total)\Messages Sent/sec"
Write-Output "exchange_messages_sent_per_second{hostname=`"$Hostname`"} $messagesSentSec"

# Bytes transferred
Write-Output "# HELP exchange_bytes_received_total Total bytes received"
Write-Output "# TYPE exchange_bytes_received_total counter"
$bytesReceived = Get-SafeCounter "\MSExchangeTransport SmtpReceive(_total)\Bytes Received Total"
Write-Output "exchange_bytes_received_total{hostname=`"$Hostname`"} $bytesReceived"

Write-Output "# HELP exchange_bytes_sent_total Total bytes sent"
Write-Output "# TYPE exchange_bytes_sent_total counter"
$bytesSent = Get-SafeCounter "\MSExchangeTransport SmtpSend(_total)\Bytes Sent Total"
Write-Output "exchange_bytes_sent_total{hostname=`"$Hostname`"} $bytesSent"

# ============================================================================
# SMTP CONNECTIONS
# ============================================================================
Write-Output "# HELP exchange_smtp_connections_current Current SMTP connections"
Write-Output "# TYPE exchange_smtp_connections_current gauge"
$smtpInbound = Get-SafeCounter "\MSExchangeTransport SmtpReceive(_total)\Connections Current"
$smtpOutbound = Get-SafeCounter "\MSExchangeTransport SmtpSend(_total)\Connections Current"
Write-Output "exchange_smtp_connections_current{direction=`"inbound`",hostname=`"$Hostname`"} $smtpInbound"
Write-Output "exchange_smtp_connections_current{direction=`"outbound`",hostname=`"$Hostname`"} $smtpOutbound"

Write-Output "# HELP exchange_smtp_connections_total Total SMTP connections"
Write-Output "# TYPE exchange_smtp_connections_total counter"
$smtpInboundTotal = Get-SafeCounter "\MSExchangeTransport SmtpReceive(_total)\Connections Total"
$smtpOutboundTotal = Get-SafeCounter "\MSExchangeTransport SmtpSend(_total)\Connections Total"
Write-Output "exchange_smtp_connections_total{direction=`"inbound`",hostname=`"$Hostname`"} $smtpInboundTotal"
Write-Output "exchange_smtp_connections_total{direction=`"outbound`",hostname=`"$Hostname`"} $smtpOutboundTotal"

# SMTP connection failures
Write-Output "# HELP exchange_smtp_connection_failures_total SMTP connection failures"
Write-Output "# TYPE exchange_smtp_connection_failures_total counter"
$smtpSendFailures = Get-SafeCounter "\MSExchangeTransport SmtpSend(_total)\Connection Failures"
Write-Output "exchange_smtp_connection_failures_total{hostname=`"$Hostname`"} $smtpSendFailures"

# ============================================================================
# MESSAGE TRACKING (from logs)
# ============================================================================
Write-Output "# HELP exchange_tracked_messages_total Messages by event type (last 24h)"
Write-Output "# TYPE exchange_tracked_messages_total counter"

try {
    $trackingStart = (Get-Date).AddHours(-24)
    $eventTypes = @('RECEIVE', 'SEND', 'DELIVER', 'FAIL', 'DSN', 'DEFER', 'EXPAND', 'REDIRECT', 'RESOLVE', 'DROP', 'BADMAIL', 'POISONMESSAGE')
    
    foreach ($eventType in $eventTypes) {
        try {
            $count = (Get-MessageTrackingLog -Start $trackingStart -EventId $eventType -ResultSize Unlimited -ErrorAction SilentlyContinue | Measure-Object).Count
            Write-Output "exchange_tracked_messages_total{event_type=`"$eventType`",hostname=`"$Hostname`"} $count"
        } catch {
            Write-Output "exchange_tracked_messages_total{event_type=`"$eventType`",hostname=`"$Hostname`"} 0"
        }
    }
} catch {}

# Message delivery latency from tracking logs
Write-Output "# HELP exchange_message_latency_seconds Message delivery latency statistics"
Write-Output "# TYPE exchange_message_latency_seconds gauge"
try {
    $deliveries = Get-MessageTrackingLog -Start (Get-Date).AddHours(-1) -EventId DELIVER -ResultSize 1000 -ErrorAction SilentlyContinue
    if ($deliveries -and $deliveries.Count -gt 0) {
        $latencies = $deliveries | ForEach-Object {
            $received = Get-MessageTrackingLog -MessageId $_.MessageId -EventId RECEIVE -ResultSize 1 -ErrorAction SilentlyContinue
            if ($received) {
                ($_.Timestamp - $received.Timestamp).TotalSeconds
            }
        } | Where-Object { $_ -gt 0 }
        
        if ($latencies -and $latencies.Count -gt 0) {
            $avgLatency = ($latencies | Measure-Object -Average).Average
            $maxLatency = ($latencies | Measure-Object -Maximum).Maximum
            Write-Output "exchange_message_latency_seconds{stat=`"avg`",hostname=`"$Hostname`"} $([math]::Round($avgLatency, 2))"
            Write-Output "exchange_message_latency_seconds{stat=`"max`",hostname=`"$Hostname`"} $([math]::Round($maxLatency, 2))"
        } else {
            Write-Output "exchange_message_latency_seconds{stat=`"avg`",hostname=`"$Hostname`"} 0"
            Write-Output "exchange_message_latency_seconds{stat=`"max`",hostname=`"$Hostname`"} 0"
        }
    }
} catch {
    Write-Output "exchange_message_latency_seconds{stat=`"avg`",hostname=`"$Hostname`"} 0"
    Write-Output "exchange_message_latency_seconds{stat=`"max`",hostname=`"$Hostname`"} 0"
}

# ============================================================================
# MAILBOX STATISTICS
# ============================================================================
Write-Output "# HELP exchange_mailbox_count Total number of mailboxes"
Write-Output "# TYPE exchange_mailbox_count gauge"
try {
    $mailboxCount = (Get-Mailbox -ResultSize Unlimited -ErrorAction SilentlyContinue | Measure-Object).Count
    Write-Output "exchange_mailbox_count{hostname=`"$Hostname`"} $mailboxCount"
} catch {
    Write-Output "exchange_mailbox_count{hostname=`"$Hostname`"} 0"
}

# Mailbox database sizes and stats
Write-Output "# HELP exchange_database_size_bytes Mailbox database size in bytes"
Write-Output "# TYPE exchange_database_size_bytes gauge"
Write-Output "# HELP exchange_database_available_space_bytes Available space in mailbox database"
Write-Output "# TYPE exchange_database_available_space_bytes gauge"
Write-Output "# HELP exchange_database_mailbox_count Mailboxes per database"
Write-Output "# TYPE exchange_database_mailbox_count gauge"

try {
    $databases = Get-MailboxDatabase -Status -ErrorAction SilentlyContinue
    foreach ($db in $databases) {
        $dbName = $db.Name -replace '\s+', '_'
        $dbSize = if ($db.DatabaseSize) { $db.DatabaseSize.ToBytes() } else { 0 }
        $dbAvailable = if ($db.AvailableNewMailboxSpace) { $db.AvailableNewMailboxSpace.ToBytes() } else { 0 }
        Write-Output "exchange_database_size_bytes{database=`"$dbName`",hostname=`"$Hostname`"} $dbSize"
        Write-Output "exchange_database_available_space_bytes{database=`"$dbName`",hostname=`"$Hostname`"} $dbAvailable"
        
        $mbxInDb = (Get-Mailbox -Database $db.Name -ResultSize Unlimited -ErrorAction SilentlyContinue | Measure-Object).Count
        Write-Output "exchange_database_mailbox_count{database=`"$dbName`",hostname=`"$Hostname`"} $mbxInDb"
    }
} catch {}

# Database mount status
Write-Output "# HELP exchange_database_mounted Database mount status (1=mounted, 0=dismounted)"
Write-Output "# TYPE exchange_database_mounted gauge"
try {
    $databases = Get-MailboxDatabase -Status -ErrorAction SilentlyContinue
    foreach ($db in $databases) {
        $dbName = $db.Name -replace '\s+', '_'
        $mounted = if ($db.Mounted) { 1 } else { 0 }
        Write-Output "exchange_database_mounted{database=`"$dbName`",hostname=`"$Hostname`"} $mounted"
    }
} catch {}

# ============================================================================
# CONTENT FILTERING / ANTI-SPAM
# ============================================================================
Write-Output "# HELP exchange_antispam_messages_total Anti-spam filter results"
Write-Output "# TYPE exchange_antispam_messages_total counter"

# Content Filter Agent
$cfBlocked = Get-SafeCounter "\MSExchange Content Filter Agent\Messages that Triggered the Content Filter"
$cfQuarantined = Get-SafeCounter "\MSExchange Content Filter Agent\Messages Quarantined"
$cfRejected = Get-SafeCounter "\MSExchange Content Filter Agent\Messages Rejected"
$cfDeleted = Get-SafeCounter "\MSExchange Content Filter Agent\Messages Deleted"
Write-Output "exchange_antispam_messages_total{filter=`"content`",action=`"triggered`",hostname=`"$Hostname`"} $cfBlocked"
Write-Output "exchange_antispam_messages_total{filter=`"content`",action=`"quarantined`",hostname=`"$Hostname`"} $cfQuarantined"
Write-Output "exchange_antispam_messages_total{filter=`"content`",action=`"rejected`",hostname=`"$Hostname`"} $cfRejected"
Write-Output "exchange_antispam_messages_total{filter=`"content`",action=`"deleted`",hostname=`"$Hostname`"} $cfDeleted"

# Sender Filter Agent
$sfBlocked = Get-SafeCounter "\MSExchange Sender Filter Agent\Messages Evaluated by Sender Filter"
$sfRejected = Get-SafeCounter "\MSExchange Sender Filter Agent\Messages Rejected by Sender Filter"
Write-Output "exchange_antispam_messages_total{filter=`"sender`",action=`"evaluated`",hostname=`"$Hostname`"} $sfBlocked"
Write-Output "exchange_antispam_messages_total{filter=`"sender`",action=`"rejected`",hostname=`"$Hostname`"} $sfRejected"

# Sender ID Agent
$sidPass = Get-SafeCounter "\MSExchange Sender Id Agent\Messages That Passed Sender ID Validation"
$sidFail = Get-SafeCounter "\MSExchange Sender Id Agent\Messages That Failed Sender ID Validation"
$sidNeutral = Get-SafeCounter "\MSExchange Sender Id Agent\Messages with Neutral Sender ID Validation Result"
Write-Output "exchange_antispam_messages_total{filter=`"sender_id`",action=`"pass`",hostname=`"$Hostname`"} $sidPass"
Write-Output "exchange_antispam_messages_total{filter=`"sender_id`",action=`"fail`",hostname=`"$Hostname`"} $sidFail"
Write-Output "exchange_antispam_messages_total{filter=`"sender_id`",action=`"neutral`",hostname=`"$Hostname`"} $sidNeutral"

# Recipient Filter Agent
$rfBlocked = Get-SafeCounter "\MSExchange Recipient Filter Agent\Recipients Rejected by Recipient Validation"
Write-Output "exchange_antispam_messages_total{filter=`"recipient`",action=`"rejected`",hostname=`"$Hostname`"} $rfBlocked"

# Connection Filter Agent (IP Block List)
$connBlocked = Get-SafeCounter "\MSExchange Connection Filtering Agent\Connections on IP Block List"
$connAllowed = Get-SafeCounter "\MSExchange Connection Filtering Agent\Connections on IP Allow List"
Write-Output "exchange_antispam_messages_total{filter=`"connection`",action=`"blocked`",hostname=`"$Hostname`"} $connBlocked"
Write-Output "exchange_antispam_messages_total{filter=`"connection`",action=`"allowed`",hostname=`"$Hostname`"} $connAllowed"

# ============================================================================
# SPAM CONFIDENCE LEVEL (SCL) DISTRIBUTION
# ============================================================================
Write-Output "# HELP exchange_scl_distribution Messages by SCL rating"
Write-Output "# TYPE exchange_scl_distribution counter"
for ($scl = -1; $scl -le 9; $scl++) {
    $sclCount = Get-SafeCounter "\MSExchange Content Filter Agent\Messages with SCL Rating $scl"
    Write-Output "exchange_scl_distribution{scl=`"$scl`",hostname=`"$Hostname`"} $sclCount"
}

# ============================================================================
# AUTHENTICATION
# ============================================================================
Write-Output "# HELP exchange_auth_total Authentication attempts"
Write-Output "# TYPE exchange_auth_total counter"

# SMTP AUTH
$smtpAuthSuccess = Get-SafeCounter "\MSExchangeTransport SmtpReceive(_total)\Auth Succeeded"
$smtpAuthFail = Get-SafeCounter "\MSExchangeTransport SmtpReceive(_total)\Auth Failed"
Write-Output "exchange_auth_total{type=`"smtp`",result=`"success`",hostname=`"$Hostname`"} $smtpAuthSuccess"
Write-Output "exchange_auth_total{type=`"smtp`",result=`"failed`",hostname=`"$Hostname`"} $smtpAuthFail"

# OWA Authentication (from event logs)
$owaAuthSuccess = Get-EventLogCount -LogName "MSExchange Management" -Source "MSExchange OWA" -Hours 24
Write-Output "exchange_auth_total{type=`"owa`",result=`"success`",hostname=`"$Hostname`"} $owaAuthSuccess"

# ActiveSync Authentication
$easAuthFail = Get-EventLogCount -LogName "Application" -Source "MSExchange ActiveSync" -EventId 1053 -Hours 24
Write-Output "exchange_auth_total{type=`"activesync`",result=`"failed`",hostname=`"$Hostname`"} $easAuthFail"

# ============================================================================
# TLS/ENCRYPTION
# ============================================================================
Write-Output "# HELP exchange_tls_connections_total TLS connection statistics"
Write-Output "# TYPE exchange_tls_connections_total counter"

$tlsInbound = Get-SafeCounter "\MSExchangeTransport SmtpReceive(_total)\TLS Sessions Started"
$tlsOutbound = Get-SafeCounter "\MSExchangeTransport SmtpSend(_total)\TLS Sessions Started"
Write-Output "exchange_tls_connections_total{direction=`"inbound`",hostname=`"$Hostname`"} $tlsInbound"
Write-Output "exchange_tls_connections_total{direction=`"outbound`",hostname=`"$Hostname`"} $tlsOutbound"

Write-Output "# HELP exchange_tls_negotiation_failures TLS negotiation failures"
Write-Output "# TYPE exchange_tls_negotiation_failures counter"
$tlsInboundFail = Get-SafeCounter "\MSExchangeTransport SmtpReceive(_total)\TLS Negotiations Failed"
$tlsOutboundFail = Get-SafeCounter "\MSExchangeTransport SmtpSend(_total)\TLS Negotiations Failed"
Write-Output "exchange_tls_negotiation_failures{direction=`"inbound`",hostname=`"$Hostname`"} $tlsInboundFail"
Write-Output "exchange_tls_negotiation_failures{direction=`"outbound`",hostname=`"$Hostname`"} $tlsOutboundFail"

# ============================================================================
# CLIENT ACCESS (OWA, EWS, ActiveSync, Outlook Anywhere)
# ============================================================================
Write-Output "# HELP exchange_owa_requests_total OWA requests"
Write-Output "# TYPE exchange_owa_requests_total counter"
$owaRequests = Get-SafeCounter "\MSExchange OWA\Requests/sec"
$owaRequestsTotal = Get-SafeCounter "\MSExchange OWA\Total Requests"
Write-Output "exchange_owa_requests_total{hostname=`"$Hostname`"} $owaRequestsTotal"

Write-Output "# HELP exchange_owa_requests_per_second OWA requests per second"
Write-Output "# TYPE exchange_owa_requests_per_second gauge"
Write-Output "exchange_owa_requests_per_second{hostname=`"$Hostname`"} $owaRequests"

Write-Output "# HELP exchange_owa_current_users Current OWA users"
Write-Output "# TYPE exchange_owa_current_users gauge"
$owaUsers = Get-SafeCounter "\MSExchange OWA\Current Unique Users"
Write-Output "exchange_owa_current_users{hostname=`"$Hostname`"} $owaUsers"

# EWS
Write-Output "# HELP exchange_ews_requests_per_second EWS requests per second"
Write-Output "# TYPE exchange_ews_requests_per_second gauge"
$ewsRequests = Get-SafeCounter "\MSExchangeWS\Requests/sec"
Write-Output "exchange_ews_requests_per_second{hostname=`"$Hostname`"} $ewsRequests"

Write-Output "# HELP exchange_ews_current_connections Current EWS connections"
Write-Output "# TYPE exchange_ews_current_connections gauge"
$ewsConns = Get-SafeCounter "\MSExchangeWS\Current Connections"
Write-Output "exchange_ews_current_connections{hostname=`"$Hostname`"} $ewsConns"

# ActiveSync
Write-Output "# HELP exchange_activesync_requests_per_second ActiveSync requests per second"
Write-Output "# TYPE exchange_activesync_requests_per_second gauge"
$easRequests = Get-SafeCounter "\MSExchange ActiveSync\Requests/sec"
Write-Output "exchange_activesync_requests_per_second{hostname=`"$Hostname`"} $easRequests"

Write-Output "# HELP exchange_activesync_current_requests Current ActiveSync requests"
Write-Output "# TYPE exchange_activesync_current_requests gauge"
$easCurrent = Get-SafeCounter "\MSExchange ActiveSync\Current Requests"
Write-Output "exchange_activesync_current_requests{hostname=`"$Hostname`"} $easCurrent"

Write-Output "# HELP exchange_activesync_sync_commands ActiveSync sync commands per second"
Write-Output "# TYPE exchange_activesync_sync_commands gauge"
$easSync = Get-SafeCounter "\MSExchange ActiveSync\Sync Commands/sec"
Write-Output "exchange_activesync_sync_commands{hostname=`"$Hostname`"} $easSync"

# Outlook Anywhere (RPC over HTTP)
Write-Output "# HELP exchange_rpc_requests_per_second RPC requests per second"
Write-Output "# TYPE exchange_rpc_requests_per_second gauge"
$rpcRequests = Get-SafeCounter "\MSExchange RpcClientAccess\RPC Requests"
Write-Output "exchange_rpc_requests_per_second{hostname=`"$Hostname`"} $rpcRequests"

Write-Output "# HELP exchange_rpc_active_users Active RPC users"
Write-Output "# TYPE exchange_rpc_active_users gauge"
$rpcUsers = Get-SafeCounter "\MSExchange RpcClientAccess\Active User Count"
Write-Output "exchange_rpc_active_users{hostname=`"$Hostname`"} $rpcUsers"

Write-Output "# HELP exchange_rpc_connection_count RPC connection count"
Write-Output "# TYPE exchange_rpc_connection_count gauge"
$rpcConns = Get-SafeCounter "\MSExchange RpcClientAccess\Connection Count"
Write-Output "exchange_rpc_connection_count{hostname=`"$Hostname`"} $rpcConns"

# Outlook MAPI/HTTP
Write-Output "# HELP exchange_mapi_requests_per_second MAPI over HTTP requests per second"
Write-Output "# TYPE exchange_mapi_requests_per_second gauge"
$mapiRequests = Get-SafeCounter "\MSExchange MapiHttp Emsmdb\Requests/sec"
Write-Output "exchange_mapi_requests_per_second{hostname=`"$Hostname`"} $mapiRequests"

Write-Output "# HELP exchange_mapi_current_connections Current MAPI connections"
Write-Output "# TYPE exchange_mapi_current_connections gauge"
$mapiConns = Get-SafeCounter "\MSExchange MapiHttp Emsmdb\Current Unique Users"
Write-Output "exchange_mapi_current_connections{hostname=`"$Hostname`"} $mapiConns"

# ============================================================================
# POP3/IMAP
# ============================================================================
Write-Output "# HELP exchange_pop3_connections Current POP3 connections"
Write-Output "# TYPE exchange_pop3_connections gauge"
$pop3Conns = Get-SafeCounter "\MSExchangePop3\Connections Current"
Write-Output "exchange_pop3_connections{hostname=`"$Hostname`"} $pop3Conns"

Write-Output "# HELP exchange_pop3_connections_total Total POP3 connections"
Write-Output "# TYPE exchange_pop3_connections_total counter"
$pop3Total = Get-SafeCounter "\MSExchangePop3\Connections Total"
Write-Output "exchange_pop3_connections_total{hostname=`"$Hostname`"} $pop3Total"

Write-Output "# HELP exchange_imap4_connections Current IMAP4 connections"
Write-Output "# TYPE exchange_imap4_connections gauge"
$imap4Conns = Get-SafeCounter "\MSExchangeImap4\Connections Current"
Write-Output "exchange_imap4_connections{hostname=`"$Hostname`"} $imap4Conns"

Write-Output "# HELP exchange_imap4_connections_total Total IMAP4 connections"
Write-Output "# TYPE exchange_imap4_connections_total counter"
$imap4Total = Get-SafeCounter "\MSExchangeImap4\Connections Total"
Write-Output "exchange_imap4_connections_total{hostname=`"$Hostname`"} $imap4Total"

# ============================================================================
# STORE (Information Store)
# ============================================================================
Write-Output "# HELP exchange_store_rpc_requests RPC requests to Information Store"
Write-Output "# TYPE exchange_store_rpc_requests gauge"
$storeRpc = Get-SafeCounter "\MSExchangeIS Store(_total)\RPC Requests"
Write-Output "exchange_store_rpc_requests{hostname=`"$Hostname`"} $storeRpc"

Write-Output "# HELP exchange_store_rpc_latency_avg Average RPC latency (ms)"
Write-Output "# TYPE exchange_store_rpc_latency_avg gauge"
$storeLatency = Get-SafeCounter "\MSExchangeIS Store(_total)\RPC Average Latency"
Write-Output "exchange_store_rpc_latency_avg{hostname=`"$Hostname`"} $storeLatency"

Write-Output "# HELP exchange_store_messages_queued Messages queued for submission"
Write-Output "# TYPE exchange_store_messages_queued gauge"
$storeQueued = Get-SafeCounter "\MSExchangeIS Store(_total)\Messages Queued For Submission"
Write-Output "exchange_store_messages_queued{hostname=`"$Hostname`"} $storeQueued"

# ============================================================================
# TRANSPORT DUMPSTER / SAFETY NET
# ============================================================================
Write-Output "# HELP exchange_safetynet_messages Messages in Safety Net"
Write-Output "# TYPE exchange_safetynet_messages gauge"
$safetynetMsgs = Get-SafeCounter "\MSExchangeTransport Safety Net\Safety Net Total Messages"
Write-Output "exchange_safetynet_messages{hostname=`"$Hostname`"} $safetynetMsgs"

Write-Output "# HELP exchange_safetynet_size_bytes Safety Net size in bytes"
Write-Output "# TYPE exchange_safetynet_size_bytes gauge"
$safetynetSize = Get-SafeCounter "\MSExchangeTransport Safety Net\Safety Net Total Size"
Write-Output "exchange_safetynet_size_bytes{hostname=`"$Hostname`"} $safetynetSize"

# ============================================================================
# DSN (DELIVERY STATUS NOTIFICATIONS)
# ============================================================================
Write-Output "# HELP exchange_dsn_total DSN messages generated"
Write-Output "# TYPE exchange_dsn_total counter"
$dsnGenerated = Get-SafeCounter "\MSExchangeTransport DSN(_total)\Delivery Status Notifications Generated"
Write-Output "exchange_dsn_total{type=`"generated`",hostname=`"$Hostname`"} $dsnGenerated"

$dsnFailure = Get-SafeCounter "\MSExchangeTransport DSN(_total)\Failure DSNs Total"
$dsnDelay = Get-SafeCounter "\MSExchangeTransport DSN(_total)\Delay DSNs Total"
$dsnRelayed = Get-SafeCounter "\MSExchangeTransport DSN(_total)\Relayed DSNs Total"
$dsnExpanded = Get-SafeCounter "\MSExchangeTransport DSN(_total)\Expanded DSNs Total"
Write-Output "exchange_dsn_total{type=`"failure`",hostname=`"$Hostname`"} $dsnFailure"
Write-Output "exchange_dsn_total{type=`"delay`",hostname=`"$Hostname`"} $dsnDelay"
Write-Output "exchange_dsn_total{type=`"relayed`",hostname=`"$Hostname`"} $dsnRelayed"
Write-Output "exchange_dsn_total{type=`"expanded`",hostname=`"$Hostname`"} $dsnExpanded"

# ============================================================================
# RESOURCE HEALTH
# ============================================================================
Write-Output "# HELP exchange_resource_health Resource health status"
Write-Output "# TYPE exchange_resource_health gauge"

# Back Pressure indicators
$backPressure = Get-SafeCounter "\MSExchangeTransport Queues(_total)\Messages Submitted Recently"
Write-Output "exchange_resource_health{resource=`"back_pressure_messages`",hostname=`"$Hostname`"} $backPressure"

# Database log generation
$dbLogGen = Get-SafeCounter "\MSExchange Database ==> Instances(_total)\Log Generation Checkpoint Depth"
Write-Output "exchange_resource_health{resource=`"log_checkpoint_depth`",hostname=`"$Hostname`"} $dbLogGen"

# ============================================================================
# SERVICE STATUS
# ============================================================================
Write-Output "# HELP exchange_service_status Exchange service status (1=running, 0=stopped)"
Write-Output "# TYPE exchange_service_status gauge"

$services = @(
    'MSExchangeTransport',
    'MSExchangeIS',
    'MSExchangeMailboxAssistants',
    'MSExchangeDelivery',
    'MSExchangeSubmission',
    'MSExchangeFrontEndTransport',
    'MSExchangeEdgeSync',
    'MSExchangeServiceHost',
    'MSExchangeRPC',
    'MSExchangeMailboxReplication',
    'MSExchangeADTopology',
    'MSExchangeAntispamUpdate',
    'MSExchangeThrottling',
    'MSExchangeHM',
    'MSExchangeDiagnostics',
    'MSExchangePop3',
    'MSExchangeImap4',
    'MSExchangeUM',
    'MSExchangeUMCR'
)

foreach ($svc in $services) {
    $service = Get-Service -Name $svc -ErrorAction SilentlyContinue
    if ($service) {
        $status = if ($service.Status -eq 'Running') { 1 } else { 0 }
        Write-Output "exchange_service_status{service=`"$svc`",hostname=`"$Hostname`"} $status"
    }
}

# ============================================================================
# SERVER HEALTH (Exchange 2013+)
# ============================================================================
Write-Output "# HELP exchange_server_health Server component health (1=healthy, 0=unhealthy)"
Write-Output "# TYPE exchange_server_health gauge"
try {
    $health = Get-ServerComponentState -Identity $Hostname -ErrorAction SilentlyContinue
    foreach ($component in $health) {
        $state = if ($component.State -eq 'Active') { 1 } else { 0 }
        $compName = $component.Component -replace '\s+', '_'
        Write-Output "exchange_server_health{component=`"$compName`",hostname=`"$Hostname`"} $state"
    }
} catch {}

# ============================================================================
# HEALTH MANAGER MONITORS
# ============================================================================
Write-Output "# HELP exchange_health_monitor Health monitor status (1=healthy, 0=unhealthy)"
Write-Output "# TYPE exchange_health_monitor gauge"
try {
    $monitors = Get-ServerHealth -Identity $Hostname -ErrorAction SilentlyContinue | 
        Where-Object { $_.AlertValue -ne "Healthy" } |
        Select-Object Name, AlertValue -Unique
    
    $unhealthyCount = ($monitors | Measure-Object).Count
    Write-Output "exchange_health_monitor{status=`"unhealthy_count`",hostname=`"$Hostname`"} $unhealthyCount"
} catch {
    Write-Output "exchange_health_monitor{status=`"unhealthy_count`",hostname=`"$Hostname`"} 0"
}

# ============================================================================
# DAG (DATABASE AVAILABILITY GROUP) STATUS
# ============================================================================
Write-Output "# HELP exchange_dag_copy_status DAG database copy status (1=mounted/healthy, 0=failed)"
Write-Output "# TYPE exchange_dag_copy_status gauge"
try {
    $dagStatus = Get-MailboxDatabaseCopyStatus -ErrorAction SilentlyContinue
    foreach ($copy in $dagStatus) {
        $dbName = $copy.DatabaseName -replace '\s+', '_'
        $status = switch ($copy.Status) {
            'Mounted' { 1 }
            'Healthy' { 1 }
            default { 0 }
        }
        Write-Output "exchange_dag_copy_status{database=`"$dbName`",server=`"$($copy.MailboxServer)`",status=`"$($copy.Status)`",hostname=`"$Hostname`"} $status"
        
        # Copy queue length
        if ($copy.CopyQueueLength -ne $null) {
            Write-Output "exchange_dag_copy_queue_length{database=`"$dbName`",server=`"$($copy.MailboxServer)`",hostname=`"$Hostname`"} $($copy.CopyQueueLength)"
        }
        
        # Replay queue length
        if ($copy.ReplayQueueLength -ne $null) {
            Write-Output "exchange_dag_replay_queue_length{database=`"$dbName`",server=`"$($copy.MailboxServer)`",hostname=`"$Hostname`"} $($copy.ReplayQueueLength)"
        }
    }
} catch {}

# ============================================================================
# CERTIFICATE EXPIRY
# ============================================================================
Write-Output "# HELP exchange_cert_expiry_seconds Seconds until certificate expires"
Write-Output "# TYPE exchange_cert_expiry_seconds gauge"
try {
    $certs = Get-ExchangeCertificate -ErrorAction SilentlyContinue | Where-Object { $_.NotAfter -gt (Get-Date) }
    foreach ($cert in $certs) {
        $services = ($cert.Services -join '_') -replace ',', '_'
        $thumbprint = $cert.Thumbprint.Substring(0, 8)
        $expirySeconds = [math]::Round(($cert.NotAfter - (Get-Date)).TotalSeconds)
        Write-Output "exchange_cert_expiry_seconds{thumbprint=`"$thumbprint`",services=`"$services`",hostname=`"$Hostname`"} $expirySeconds"
    }
} catch {}

# ============================================================================
# TOP SENDERS/RECIPIENTS (Last 24 hours)
# ============================================================================
Write-Output "# HELP exchange_top_senders_total Top senders by message count (24h)"
Write-Output "# TYPE exchange_top_senders_total counter"
try {
    $topSenders = Get-MessageTrackingLog -Start (Get-Date).AddHours(-24) -EventId RECEIVE -ResultSize 10000 -ErrorAction SilentlyContinue |
        Group-Object Sender |
        Sort-Object Count -Descending |
        Select-Object -First 20
    foreach ($sender in $topSenders) {
        $senderAddr = $sender.Name -replace '"', ''
        Write-Output "exchange_top_senders_total{sender=`"$senderAddr`",hostname=`"$Hostname`"} $($sender.Count)"
    }
} catch {}

Write-Output "# HELP exchange_top_recipients_total Top recipients by message count (24h)"
Write-Output "# TYPE exchange_top_recipients_total counter"
try {
    $topRecipients = Get-MessageTrackingLog -Start (Get-Date).AddHours(-24) -EventId DELIVER -ResultSize 10000 -ErrorAction SilentlyContinue |
        ForEach-Object { $_.Recipients } |
        Group-Object |
        Sort-Object Count -Descending |
        Select-Object -First 20
    foreach ($recipient in $topRecipients) {
        $recipientAddr = $recipient.Name -replace '"', ''
        Write-Output "exchange_top_recipients_total{recipient=`"$recipientAddr`",hostname=`"$Hostname`"} $($recipient.Count)"
    }
} catch {}

# ============================================================================
# MESSAGE SIZE STATISTICS
# ============================================================================
Write-Output "# HELP exchange_message_size_bytes Message size statistics"
Write-Output "# TYPE exchange_message_size_bytes gauge"
try {
    $messages = Get-MessageTrackingLog -Start (Get-Date).AddHours(-1) -EventId RECEIVE -ResultSize 1000 -ErrorAction SilentlyContinue
    if ($messages -and $messages.Count -gt 0) {
        $sizes = $messages | ForEach-Object { $_.TotalBytes } | Where-Object { $_ -gt 0 }
        if ($sizes -and $sizes.Count -gt 0) {
            $avgSize = [math]::Round(($sizes | Measure-Object -Average).Average)
            $maxSize = ($sizes | Measure-Object -Maximum).Maximum
            $totalSize = ($sizes | Measure-Object -Sum).Sum
            Write-Output "exchange_message_size_bytes{stat=`"avg`",hostname=`"$Hostname`"} $avgSize"
            Write-Output "exchange_message_size_bytes{stat=`"max`",hostname=`"$Hostname`"} $maxSize"
            Write-Output "exchange_message_size_bytes{stat=`"total_1h`",hostname=`"$Hostname`"} $totalSize"
        }
    }
} catch {}

# ============================================================================
# EDGE TRANSPORT (if applicable)
# ============================================================================
Write-Output "# HELP exchange_edge_sync_status EdgeSync status"
Write-Output "# TYPE exchange_edge_sync_status gauge"
try {
    $edgeSync = Test-EdgeSynchronization -ErrorAction SilentlyContinue
    if ($edgeSync) {
        $syncStatus = if ($edgeSync.SyncStatus -eq 'Normal') { 1 } else { 0 }
        Write-Output "exchange_edge_sync_status{hostname=`"$Hostname`"} $syncStatus"
    }
} catch {}

# ============================================================================
# TRANSPORT AGENTS
# ============================================================================
Write-Output "# HELP exchange_transport_agent_enabled Transport agent status (1=enabled, 0=disabled)"
Write-Output "# TYPE exchange_transport_agent_enabled gauge"
try {
    $agents = Get-TransportAgent -ErrorAction SilentlyContinue
    foreach ($agent in $agents) {
        $agentName = $agent.Identity -replace '\s+', '_' -replace '[^\w_]', ''
        $enabled = if ($agent.Enabled) { 1 } else { 0 }
        Write-Output "exchange_transport_agent_enabled{agent=`"$agentName`",hostname=`"$Hostname`"} $enabled"
    }
} catch {}

# ============================================================================
# PUBLIC FOLDER STATISTICS
# ============================================================================
Write-Output "# HELP exchange_public_folder_count Number of public folders"
Write-Output "# TYPE exchange_public_folder_count gauge"
try {
    $pfCount = (Get-PublicFolder -Recurse -ResultSize Unlimited -ErrorAction SilentlyContinue | Measure-Object).Count
    Write-Output "exchange_public_folder_count{hostname=`"$Hostname`"} $pfCount"
} catch {
    Write-Output "exchange_public_folder_count{hostname=`"$Hostname`"} 0"
}

# ============================================================================
# OUTLOOK WEB APP ERRORS (from Event Log)
# ============================================================================
Write-Output "# HELP exchange_owa_errors_total OWA errors from event log (24h)"
Write-Output "# TYPE exchange_owa_errors_total counter"
$owaErrors = Get-EventLogCount -LogName "Application" -Source "MSExchange OWA" -Hours 24
Write-Output "exchange_owa_errors_total{hostname=`"$Hostname`"} $owaErrors"

# ============================================================================
# TRANSPORT ERRORS (from Event Log)
# ============================================================================
Write-Output "# HELP exchange_transport_errors_total Transport errors from event log (24h)"
Write-Output "# TYPE exchange_transport_errors_total counter"
$transportErrors = Get-EventLogCount -LogName "Application" -Source "MSExchangeTransport" -Hours 24
Write-Output "exchange_transport_errors_total{hostname=`"$Hostname`"} $transportErrors"

# ============================================================================
# INFORMATION STORE ERRORS
# ============================================================================
Write-Output "# HELP exchange_store_errors_total Information Store errors from event log (24h)"
Write-Output "# TYPE exchange_store_errors_total counter"
$storeErrors = Get-EventLogCount -LogName "Application" -Source "MSExchangeIS" -Hours 24
Write-Output "exchange_store_errors_total{hostname=`"$Hostname`"} $storeErrors"

# ============================================================================
# REPLICATION HEALTH
# ============================================================================
Write-Output "# HELP exchange_replication_health Replication health check results"
Write-Output "# TYPE exchange_replication_health gauge"
try {
    $replHealth = Test-ReplicationHealth -ErrorAction SilentlyContinue
    foreach ($check in $replHealth) {
        $checkName = $check.Check -replace '\s+', '_'
        $result = if ($check.Result -eq 'Passed') { 1 } else { 0 }
        Write-Output "exchange_replication_health{check=`"$checkName`",hostname=`"$Hostname`"} $result"
    }
} catch {}

# ============================================================================
# MAIL FLOW TEST
# ============================================================================
Write-Output "# HELP exchange_mailflow_test_latency_seconds Mail flow test latency"
Write-Output "# TYPE exchange_mailflow_test_latency_seconds gauge"
try {
    $mailflow = Test-Mailflow -ErrorAction SilentlyContinue
    if ($mailflow -and $mailflow.TestMailflowResult -eq 'Success') {
        $latency = $mailflow.MessageLatencyTime.TotalSeconds
        Write-Output "exchange_mailflow_test_latency_seconds{hostname=`"$Hostname`"} $([math]::Round($latency, 2))"
        Write-Output "exchange_mailflow_test_success{hostname=`"$Hostname`"} 1"
    } else {
        Write-Output "exchange_mailflow_test_success{hostname=`"$Hostname`"} 0"
    }
} catch {
    Write-Output "exchange_mailflow_test_success{hostname=`"$Hostname`"} 0"
}

# ============================================================================
# DOMAIN STATISTICS (Send/Receive by Domain)
# ============================================================================
Write-Output "# HELP exchange_domain_messages_total Messages by remote domain (24h)"
Write-Output "# TYPE exchange_domain_messages_total counter"
try {
    $domainStats = Get-MessageTrackingLog -Start (Get-Date).AddHours(-24) -EventId SEND -ResultSize 10000 -ErrorAction SilentlyContinue |
        ForEach-Object { 
            $_.Recipients | ForEach-Object { 
                if ($_ -match '@(.+)$') { $matches[1] }
            }
        } |
        Group-Object |
        Sort-Object Count -Descending |
        Select-Object -First 20
    foreach ($domain in $domainStats) {
        if ($domain.Name) {
            Write-Output "exchange_domain_messages_total{domain=`"$($domain.Name)`",direction=`"outbound`",hostname=`"$Hostname`"} $($domain.Count)"
        }
    }
} catch {}

# ============================================================================
# RETENTION POLICY TAGS (Compliance)
# ============================================================================
Write-Output "# HELP exchange_retention_policy_count Number of retention policies"
Write-Output "# TYPE exchange_retention_policy_count gauge"
try {
    $retPolicies = (Get-RetentionPolicy -ErrorAction SilentlyContinue | Measure-Object).Count
    Write-Output "exchange_retention_policy_count{hostname=`"$Hostname`"} $retPolicies"
} catch {
    Write-Output "exchange_retention_policy_count{hostname=`"$Hostname`"} 0"
}

# ============================================================================
# JOURNAL RULES
# ============================================================================
Write-Output "# HELP exchange_journal_rule_count Number of journal rules"
Write-Output "# TYPE exchange_journal_rule_count gauge"
try {
    $journalRules = (Get-JournalRule -ErrorAction SilentlyContinue | Measure-Object).Count
    Write-Output "exchange_journal_rule_count{hostname=`"$Hostname`"} $journalRules"
} catch {
    Write-Output "exchange_journal_rule_count{hostname=`"$Hostname`"} 0"
}

# ============================================================================
# TRANSPORT RULES
# ============================================================================
Write-Output "# HELP exchange_transport_rule_count Number of transport rules"
Write-Output "# TYPE exchange_transport_rule_count gauge"
try {
    $transportRules = (Get-TransportRule -ErrorAction SilentlyContinue | Measure-Object).Count
    Write-Output "exchange_transport_rule_count{hostname=`"$Hostname`"} $transportRules"
} catch {
    Write-Output "exchange_transport_rule_count{hostname=`"$Hostname`"} 0"
}

# ============================================================================
# ACCEPTED DOMAINS
# ============================================================================
Write-Output "# HELP exchange_accepted_domain_count Number of accepted domains"
Write-Output "# TYPE exchange_accepted_domain_count gauge"
try {
    $acceptedDomains = (Get-AcceptedDomain -ErrorAction SilentlyContinue | Measure-Object).Count
    Write-Output "exchange_accepted_domain_count{hostname=`"$Hostname`"} $acceptedDomains"
} catch {
    Write-Output "exchange_accepted_domain_count{hostname=`"$Hostname`"} 0"
}

# ============================================================================
# SEND/RECEIVE CONNECTORS
# ============================================================================
Write-Output "# HELP exchange_send_connector_enabled Send connector status"
Write-Output "# TYPE exchange_send_connector_enabled gauge"
try {
    $sendConnectors = Get-SendConnector -ErrorAction SilentlyContinue
    foreach ($conn in $sendConnectors) {
        $connName = $conn.Name -replace '\s+', '_' -replace '[^\w_]', ''
        $enabled = if ($conn.Enabled) { 1 } else { 0 }
        Write-Output "exchange_send_connector_enabled{connector=`"$connName`",hostname=`"$Hostname`"} $enabled"
    }
} catch {}

Write-Output "# HELP exchange_receive_connector_enabled Receive connector status"
Write-Output "# TYPE exchange_receive_connector_enabled gauge"
try {
    $receiveConnectors = Get-ReceiveConnector -ErrorAction SilentlyContinue
    foreach ($conn in $receiveConnectors) {
        $connName = $conn.Name -replace '\s+', '_' -replace '[^\w_]', ''
        $enabled = if ($conn.Enabled) { 1 } else { 0 }
        Write-Output "exchange_receive_connector_enabled{connector=`"$connName`",hostname=`"$Hostname`"} $enabled"
    }
} catch {}

# ============================================================================
# EXECUTION TIME
# ============================================================================
# ============================================================================
# AUTODISCOVER
# ============================================================================
Write-Output "# HELP exchange_autodiscover_requests_per_second Autodiscover requests per second"
Write-Output "# TYPE exchange_autodiscover_requests_per_second gauge"
$autodiscoverReq = Get-SafeCounter "\MSExchange Autodiscover\Requests/sec"
Write-Output "exchange_autodiscover_requests_per_second{hostname=`"$Hostname`"} $autodiscoverReq"

Write-Output "# HELP exchange_autodiscover_errors_total Autodiscover errors"
Write-Output "# TYPE exchange_autodiscover_errors_total counter"
$autodiscoverErrors = Get-SafeCounter "\MSExchange Autodiscover\Error Responses/sec"
Write-Output "exchange_autodiscover_errors_total{hostname=`"$Hostname`"} $autodiscoverErrors"

# ============================================================================
# ADDRESS BOOK SERVICE
# ============================================================================
Write-Output "# HELP exchange_addressbook_requests_per_second Address Book requests per second"
Write-Output "# TYPE exchange_addressbook_requests_per_second gauge"
$abRequests = Get-SafeCounter "\MSExchange AddressBook(_total)\Referral RPC Requests/sec"
Write-Output "exchange_addressbook_requests_per_second{hostname=`"$Hostname`"} $abRequests"

Write-Output "# HELP exchange_addressbook_latency_avg Average Address Book latency (ms)"
Write-Output "# TYPE exchange_addressbook_latency_avg gauge"
$abLatency = Get-SafeCounter "\MSExchange AddressBook(_total)\RPC Averaged Latency"
Write-Output "exchange_addressbook_latency_avg{hostname=`"$Hostname`"} $abLatency"

# ============================================================================
# SEARCH / CONTENT INDEXING
# ============================================================================
Write-Output "# HELP exchange_search_mailboxes_left Mailboxes left to crawl"
Write-Output "# TYPE exchange_search_mailboxes_left gauge"
$searchLeft = Get-SafeCounter "\MSExchange Search Indexes(_total)\Mailboxes Left to Crawl"
Write-Output "exchange_search_mailboxes_left{hostname=`"$Hostname`"} $searchLeft"

Write-Output "# HELP exchange_search_documents_indexed Documents indexed per second"
Write-Output "# TYPE exchange_search_documents_indexed gauge"
$searchDocs = Get-SafeCounter "\MSExchange Search Indexes(_total)\Average Document Indexing Time"
Write-Output "exchange_search_documents_indexed{hostname=`"$Hostname`"} $searchDocs"

Write-Output "# HELP exchange_search_index_status Search index catalog status"
Write-Output "# TYPE exchange_search_index_status gauge"
try {
    $catalogStatus = Get-MailboxDatabaseCopyStatus -ErrorAction SilentlyContinue | 
        Select-Object DatabaseName, ContentIndexState
    foreach ($cat in $catalogStatus) {
        $dbName = $cat.DatabaseName -replace '\s+', '_'
        $healthy = if ($cat.ContentIndexState -eq 'Healthy') { 1 } else { 0 }
        Write-Output "exchange_search_index_status{database=`"$dbName`",state=`"$($cat.ContentIndexState)`",hostname=`"$Hostname`"} $healthy"
    }
} catch {}

# ============================================================================
# THROTTLING POLICY
# ============================================================================
Write-Output "# HELP exchange_throttling_rejected_total Requests rejected by throttling"
Write-Output "# TYPE exchange_throttling_rejected_total counter"
$throttleRejected = Get-SafeCounter "\MSExchange Throttling\Requests Rejected due to Budget Usage"
Write-Output "exchange_throttling_rejected_total{hostname=`"$Hostname`"} $throttleRejected"

Write-Output "# HELP exchange_throttling_delayed_total Requests delayed by throttling"
Write-Output "# TYPE exchange_throttling_delayed_total counter"
$throttleDelayed = Get-SafeCounter "\MSExchange Throttling\Requests Submitted to Delayed Execution"
Write-Output "exchange_throttling_delayed_total{hostname=`"$Hostname`"} $throttleDelayed"

# ============================================================================
# MAILBOX ASSISTANTS
# ============================================================================
Write-Output "# HELP exchange_mailbox_assistant_events Events processed by mailbox assistants"
Write-Output "# TYPE exchange_mailbox_assistant_events gauge"
$assistantEvents = Get-SafeCounter "\MSExchange Assistants - Per Assistant(_total)\Events Polled"
Write-Output "exchange_mailbox_assistant_events{hostname=`"$Hostname`"} $assistantEvents"

Write-Output "# HELP exchange_calendar_assistant_requests Calendar assistant requests"
Write-Output "# TYPE exchange_calendar_assistant_requests gauge"
$calendarAssist = Get-SafeCounter "\MSExchange Calendar Attendant\Requests"
Write-Output "exchange_calendar_assistant_requests{hostname=`"$Hostname`"} $calendarAssist"

# ============================================================================
# MAILBOX REPLICATION SERVICE (MRS)
# ============================================================================
Write-Output "# HELP exchange_mrs_active_moves Active mailbox moves"
Write-Output "# TYPE exchange_mrs_active_moves gauge"
try {
    $activeMoves = (Get-MoveRequest -MoveStatus InProgress -ErrorAction SilentlyContinue | Measure-Object).Count
    Write-Output "exchange_mrs_active_moves{hostname=`"$Hostname`"} $activeMoves"
} catch {
    Write-Output "exchange_mrs_active_moves{hostname=`"$Hostname`"} 0"
}

Write-Output "# HELP exchange_mrs_queued_moves Queued mailbox moves"
Write-Output "# TYPE exchange_mrs_queued_moves gauge"
try {
    $queuedMoves = (Get-MoveRequest -MoveStatus Queued -ErrorAction SilentlyContinue | Measure-Object).Count
    Write-Output "exchange_mrs_queued_moves{hostname=`"$Hostname`"} $queuedMoves"
} catch {
    Write-Output "exchange_mrs_queued_moves{hostname=`"$Hostname`"} 0"
}

Write-Output "# HELP exchange_mrs_failed_moves Failed mailbox moves"
Write-Output "# TYPE exchange_mrs_failed_moves gauge"
try {
    $failedMoves = (Get-MoveRequest -MoveStatus Failed -ErrorAction SilentlyContinue | Measure-Object).Count
    Write-Output "exchange_mrs_failed_moves{hostname=`"$Hostname`"} $failedMoves"
} catch {
    Write-Output "exchange_mrs_failed_moves{hostname=`"$Hostname`"} 0"
}

# ============================================================================
# TRANSPORT PIPELINE LATENCY
# ============================================================================
Write-Output "# HELP exchange_transport_latency_seconds Transport component latency"
Write-Output "# TYPE exchange_transport_latency_seconds gauge"
$latencySmtpReceive = Get-SafeCounter "\MSExchangeTransport SmtpReceive(_total)\Average bytes/message"
$latencyCategor = Get-SafeCounter "\MSExchangeTransport Resolver(_total)\Ambiguous Recipients Rate"
$latencyDelivery = Get-SafeCounter "\MSExchangeTransport Delivery Failures\Average Delivery Attempts Per Message"
Write-Output "exchange_transport_latency_seconds{component=`"smtp_receive`",hostname=`"$Hostname`"} $latencySmtpReceive"
Write-Output "exchange_transport_latency_seconds{component=`"categorizer`",hostname=`"$Hostname`"} $latencyCategor"
Write-Output "exchange_transport_latency_seconds{component=`"delivery`",hostname=`"$Hostname`"} $latencyDelivery"

# ============================================================================
# PROCESS MEMORY/CPU
# ============================================================================
Write-Output "# HELP exchange_process_memory_bytes Memory usage by Exchange processes"
Write-Output "# TYPE exchange_process_memory_bytes gauge"
Write-Output "# HELP exchange_process_cpu_percent CPU usage by Exchange processes"
Write-Output "# TYPE exchange_process_cpu_percent gauge"

$exchangeProcesses = @(
    'EdgeTransport',
    'Microsoft.Exchange.Store.Worker',
    'MSExchangeTransport',
    'MSExchangeHMWorker',
    'MSExchangeMailboxAssistants',
    'MSExchangeDelivery',
    'MSExchangeSubmission',
    'w3wp'  # IIS worker processes
)

foreach ($procName in $exchangeProcesses) {
    try {
        $procs = Get-Process -Name $procName -ErrorAction SilentlyContinue
        if ($procs) {
            $totalMem = ($procs | Measure-Object -Property WorkingSet64 -Sum).Sum
            $procNameClean = $procName -replace '\.', '_'
            Write-Output "exchange_process_memory_bytes{process=`"$procNameClean`",hostname=`"$Hostname`"} $totalMem"
        }
    } catch {}
}

# ============================================================================
# ARCHIVE MAILBOX STATISTICS
# ============================================================================
Write-Output "# HELP exchange_archive_mailbox_count Number of archive mailboxes"
Write-Output "# TYPE exchange_archive_mailbox_count gauge"
try {
    $archiveCount = (Get-Mailbox -Archive -ResultSize Unlimited -ErrorAction SilentlyContinue | Measure-Object).Count
    Write-Output "exchange_archive_mailbox_count{hostname=`"$Hostname`"} $archiveCount"
} catch {
    Write-Output "exchange_archive_mailbox_count{hostname=`"$Hostname`"} 0"
}

# ============================================================================
# LITIGATION HOLD
# ============================================================================
Write-Output "# HELP exchange_litigation_hold_count Mailboxes on litigation hold"
Write-Output "# TYPE exchange_litigation_hold_count gauge"
try {
    $litigationCount = (Get-Mailbox -ResultSize Unlimited -ErrorAction SilentlyContinue | 
        Where-Object { $_.LitigationHoldEnabled -eq $true } | Measure-Object).Count
    Write-Output "exchange_litigation_hold_count{hostname=`"$Hostname`"} $litigationCount"
} catch {
    Write-Output "exchange_litigation_hold_count{hostname=`"$Hostname`"} 0"
}

# ============================================================================
# SHARED MAILBOXES
# ============================================================================
Write-Output "# HELP exchange_shared_mailbox_count Number of shared mailboxes"
Write-Output "# TYPE exchange_shared_mailbox_count gauge"
try {
    $sharedCount = (Get-Mailbox -RecipientTypeDetails SharedMailbox -ResultSize Unlimited -ErrorAction SilentlyContinue | Measure-Object).Count
    Write-Output "exchange_shared_mailbox_count{hostname=`"$Hostname`"} $sharedCount"
} catch {
    Write-Output "exchange_shared_mailbox_count{hostname=`"$Hostname`"} 0"
}

# ============================================================================
# ROOM/RESOURCE MAILBOXES
# ============================================================================
Write-Output "# HELP exchange_resource_mailbox_count Number of resource mailboxes"
Write-Output "# TYPE exchange_resource_mailbox_count gauge"
try {
    $roomCount = (Get-Mailbox -RecipientTypeDetails RoomMailbox -ResultSize Unlimited -ErrorAction SilentlyContinue | Measure-Object).Count
    $equipmentCount = (Get-Mailbox -RecipientTypeDetails EquipmentMailbox -ResultSize Unlimited -ErrorAction SilentlyContinue | Measure-Object).Count
    Write-Output "exchange_resource_mailbox_count{type=`"room`",hostname=`"$Hostname`"} $roomCount"
    Write-Output "exchange_resource_mailbox_count{type=`"equipment`",hostname=`"$Hostname`"} $equipmentCount"
} catch {}

# ============================================================================
# DISTRIBUTION GROUPS
# ============================================================================
Write-Output "# HELP exchange_distribution_group_count Number of distribution groups"
Write-Output "# TYPE exchange_distribution_group_count gauge"
try {
    $dgCount = (Get-DistributionGroup -ResultSize Unlimited -ErrorAction SilentlyContinue | Measure-Object).Count
    Write-Output "exchange_distribution_group_count{hostname=`"$Hostname`"} $dgCount"
} catch {
    Write-Output "exchange_distribution_group_count{hostname=`"$Hostname`"} 0"
}

# ============================================================================
# DYNAMIC DISTRIBUTION GROUPS
# ============================================================================
Write-Output "# HELP exchange_dynamic_distribution_group_count Number of dynamic distribution groups"
Write-Output "# TYPE exchange_dynamic_distribution_group_count gauge"
try {
    $ddgCount = (Get-DynamicDistributionGroup -ResultSize Unlimited -ErrorAction SilentlyContinue | Measure-Object).Count
    Write-Output "exchange_dynamic_distribution_group_count{hostname=`"$Hostname`"} $ddgCount"
} catch {
    Write-Output "exchange_dynamic_distribution_group_count{hostname=`"$Hostname`"} 0"
}

# ============================================================================
# REMOTE DOMAINS
# ============================================================================
Write-Output "# HELP exchange_remote_domain_count Number of remote domains configured"
Write-Output "# TYPE exchange_remote_domain_count gauge"
try {
    $remoteDomains = (Get-RemoteDomain -ErrorAction SilentlyContinue | Measure-Object).Count
    Write-Output "exchange_remote_domain_count{hostname=`"$Hostname`"} $remoteDomains"
} catch {
    Write-Output "exchange_remote_domain_count{hostname=`"$Hostname`"} 0"
}

# ============================================================================
# UNIFIED MESSAGING (if enabled)
# ============================================================================
Write-Output "# HELP exchange_um_calls_current Current UM calls"
Write-Output "# TYPE exchange_um_calls_current gauge"
$umCalls = Get-SafeCounter "\MSExchangeUMAvailability\Current Calls"
Write-Output "exchange_um_calls_current{hostname=`"$Hostname`"} $umCalls"

Write-Output "# HELP exchange_um_calls_total Total UM calls"
Write-Output "# TYPE exchange_um_calls_total counter"
$umCallsTotal = Get-SafeCounter "\MSExchangeUMAvailability\Total Calls"
Write-Output "exchange_um_calls_total{hostname=`"$Hostname`"} $umCallsTotal"

# ============================================================================
# SUBMISSION QUEUE STATISTICS (detailed)
# ============================================================================
Write-Output "# HELP exchange_submission_queue_items_expired Items expired from submission queue"
Write-Output "# TYPE exchange_submission_queue_items_expired counter"
$subExpired = Get-SafeCounter "\MSExchangeTransport Queues(_total)\Items Expired from Submission Queue"
Write-Output "exchange_submission_queue_items_expired{hostname=`"$Hostname`"} $subExpired"

Write-Output "# HELP exchange_submission_queue_items_submitted Items submitted to queue"
Write-Output "# TYPE exchange_submission_queue_items_submitted counter"
$subSubmitted = Get-SafeCounter "\MSExchangeTransport Queues(_total)\Items Submitted Total"
Write-Output "exchange_submission_queue_items_submitted{hostname=`"$Hostname`"} $subSubmitted"

# ============================================================================
# EXTERNAL RELAY STATISTICS
# ============================================================================
Write-Output "# HELP exchange_external_relay_messages External relay message count"
Write-Output "# TYPE exchange_external_relay_messages counter"
try {
    $externalRelay = Get-MessageTrackingLog -Start (Get-Date).AddHours(-24) -EventId SEND -ResultSize 5000 -ErrorAction SilentlyContinue |
        Where-Object { $_.ConnectorId -notmatch 'Internal' } |
        Measure-Object
    Write-Output "exchange_external_relay_messages{hostname=`"$Hostname`"} $($externalRelay.Count)"
} catch {
    Write-Output "exchange_external_relay_messages{hostname=`"$Hostname`"} 0"
}

# ============================================================================
# SPAM/MALWARE QUARANTINE
# ============================================================================
Write-Output "# HELP exchange_quarantine_messages Messages in quarantine"
Write-Output "# TYPE exchange_quarantine_messages gauge"
try {
    $quarantineCount = (Get-QuarantineMessage -ErrorAction SilentlyContinue | Measure-Object).Count
    Write-Output "exchange_quarantine_messages{hostname=`"$Hostname`"} $quarantineCount"
} catch {
    Write-Output "exchange_quarantine_messages{hostname=`"$Hostname`"} 0"
}

# ============================================================================
# HYBRID CONFIGURATION (for O365 hybrid)
# ============================================================================
Write-Output "# HELP exchange_hybrid_configured Hybrid configuration status"
Write-Output "# TYPE exchange_hybrid_configured gauge"
try {
    $hybrid = Get-HybridConfiguration -ErrorAction SilentlyContinue
    $isHybrid = if ($hybrid) { 1 } else { 0 }
    Write-Output "exchange_hybrid_configured{hostname=`"$Hostname`"} $isHybrid"
} catch {
    Write-Output "exchange_hybrid_configured{hostname=`"$Hostname`"} 0"
}

# ============================================================================
# OAUTH TOKENS (for modern auth)
# ============================================================================
Write-Output "# HELP exchange_oauth_token_requests OAuth token requests"
Write-Output "# TYPE exchange_oauth_token_requests counter"
$oauthRequests = Get-SafeCounter "\MSExchange OAuth\Inbound: Token Requests"
Write-Output "exchange_oauth_token_requests{hostname=`"$Hostname`"} $oauthRequests"

# ============================================================================
# LAST FULL BACKUP
# ============================================================================
Write-Output "# HELP exchange_database_last_backup_seconds Seconds since last full backup"
Write-Output "# TYPE exchange_database_last_backup_seconds gauge"
try {
    $databases = Get-MailboxDatabase -Status -ErrorAction SilentlyContinue
    foreach ($db in $databases) {
        $dbName = $db.Name -replace '\s+', '_'
        if ($db.LastFullBackup) {
            $backupAge = [math]::Round(((Get-Date) - $db.LastFullBackup).TotalSeconds)
            Write-Output "exchange_database_last_backup_seconds{database=`"$dbName`",hostname=`"$Hostname`"} $backupAge"
        } else {
            Write-Output "exchange_database_last_backup_seconds{database=`"$dbName`",hostname=`"$Hostname`"} -1"
        }
    }
} catch {}

# ============================================================================
# LOG GENERATION RATE
# ============================================================================
Write-Output "# HELP exchange_log_generation_rate Log generation checkpoint depth"
Write-Output "# TYPE exchange_log_generation_rate gauge"
try {
    $databases = Get-MailboxDatabase -Status -ErrorAction SilentlyContinue
    foreach ($db in $databases) {
        $dbName = $db.Name -replace '\s+', '_'
        $counterPath = "\MSExchange Database ==> Instances($dbName)\Log Generation Checkpoint Depth"
        $logDepth = Get-SafeCounter $counterPath
        Write-Output "exchange_log_generation_rate{database=`"$dbName`",hostname=`"$Hostname`"} $logDepth"
    }
} catch {}

# ============================================================================
# AVERAGE MAILBOX SIZE
# ============================================================================
Write-Output "# HELP exchange_average_mailbox_size_bytes Average mailbox size"
Write-Output "# TYPE exchange_average_mailbox_size_bytes gauge"
try {
    $stats = Get-MailboxStatistics -Server $Hostname -ErrorAction SilentlyContinue | 
        Where-Object { $_.TotalItemSize -ne $null }
    if ($stats -and $stats.Count -gt 0) {
        $avgSize = ($stats | ForEach-Object { $_.TotalItemSize.Value.ToBytes() } | Measure-Object -Average).Average
        Write-Output "exchange_average_mailbox_size_bytes{hostname=`"$Hostname`"} $([math]::Round($avgSize))"
    }
} catch {
    Write-Output "exchange_average_mailbox_size_bytes{hostname=`"$Hostname`"} 0"
}

# ============================================================================
# LARGEST MAILBOXES (top 10)
# ============================================================================
Write-Output "# HELP exchange_largest_mailbox_bytes Largest mailboxes by size"
Write-Output "# TYPE exchange_largest_mailbox_bytes gauge"
try {
    $topMailboxes = Get-MailboxStatistics -Server $Hostname -ErrorAction SilentlyContinue | 
        Where-Object { $_.TotalItemSize -ne $null } |
        Sort-Object TotalItemSize -Descending |
        Select-Object -First 10
    foreach ($mbx in $topMailboxes) {
        $mbxName = $mbx.DisplayName -replace '["\s]', '_' -replace '[^\w_]', ''
        $size = $mbx.TotalItemSize.Value.ToBytes()
        Write-Output "exchange_largest_mailbox_bytes{mailbox=`"$mbxName`",hostname=`"$Hostname`"} $size"
    }
} catch {}

# ============================================================================
# EXECUTION TIME
# ============================================================================
$EndTime = Get-Date
$Duration = ($EndTime - $StartTime).TotalSeconds

Write-Output "# HELP exchange_collector_duration_seconds Time taken to collect metrics"
Write-Output "# TYPE exchange_collector_duration_seconds gauge"
Write-Output "exchange_collector_duration_seconds{hostname=`"$Hostname`"} $([math]::Round($Duration, 2))"

Write-Output "# HELP exchange_collector_last_run_timestamp Unix timestamp of last collection"
Write-Output "# TYPE exchange_collector_last_run_timestamp gauge"
Write-Output "exchange_collector_last_run_timestamp{hostname=`"$Hostname`"} $([math]::Round((Get-Date -UFormat %s), 0))"
