Functions/Public/Export-MetricsToCSV.ps1

function Export-MetricsToCSV {
    <#
    .SYNOPSIS
        Exports metrics data to a CSV file with dynamic headers.

    .DESCRIPTION
        The Export-MetricsToCSV function takes a PSObject containing metrics and writes
        them to a CSV file. It automatically generates headers from the object properties
        and includes a timestamp. Supports both appending to existing files and overwriting.

    .PARAMETER Metrics
        A PSObject containing the metrics to export. Each property becomes a column.

    .PARAMETER Path
        The path to the CSV file.

    .PARAMETER DateFormat
        The date format for the timestamp column. Default is ISO 8601 format.

    .PARAMETER DateColumnName
        The name of the date column. Default is "Date".

    .PARAMETER Overwrite
        If specified, overwrites the file instead of appending.

    .PARAMETER NoHeader
        If specified, doesn't write headers (useful when appending to existing file
        with different structure - use with caution).

    .PARAMETER PassThru
        If specified, returns the CSV line that was written.

    .INPUTS
        System.Management.Automation.PSObject. You can pipe metrics objects.

    .OUTPUTS
        None by default. The CSV line string if -PassThru is specified.

    .EXAMPLE
        $metrics = [PSCustomObject]@{
            TotalDevices = 1500
            ActiveDevices = 1200
            InactiveDevices = 300
        }
        Export-MetricsToCSV -Metrics $metrics -Path "C:\Data\device-metrics.csv"

        Creates/appends to CSV with columns: Date, TotalDevices, ActiveDevices, InactiveDevices

    .EXAMPLE
        $metrics | Export-MetricsToCSV -Path $metricsFile -Overwrite

        Overwrites the file with new metrics.

    .EXAMPLE
        Export-MetricsToCSV -Metrics $stats -Path $logFile -DateFormat "yyyy-MM-dd"

        Uses date-only format for timestamp.

    .NOTES
        Author: Sune Alexandersen Narud
        Version: 1.0.0
        Date: February 2026

        This is a generalized version of Log-MetricsToCSV and Log-Statistics functions.

    .LINK
        Test-FileLock
    #>


    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
        [ValidateNotNull()]
        [PSObject]$Metrics,

        [Parameter(Mandatory = $true, Position = 1)]
        [ValidateNotNullOrEmpty()]
        [string]$Path,

        [Parameter()]
        [string]$DateFormat = 'yyyy-MM-ddTHH:mm:ssZ',

        [Parameter()]
        [string]$DateColumnName = 'Date',

        [Parameter()]
        [switch]$Overwrite,

        [Parameter()]
        [switch]$NoHeader,

        [Parameter()]
        [switch]$PassThru
    )

    process {
        try {
            # Extract property names and values from the metrics object
            $properties = $Metrics.PSObject.Properties | Where-Object { $_.MemberType -eq 'NoteProperty' }
            
            if ($properties.Count -eq 0) {
                Write-Warning "No properties found in metrics object."
                return
            }

            $headers = @($DateColumnName) + ($properties | ForEach-Object { $_.Name })
            $values = @(Get-Date -Format $DateFormat) + ($properties | ForEach-Object { $_.Value })

            # Escape values that contain commas or quotes
            $escapedValues = $values | ForEach-Object {
                $val = "$_"
                if ($val -match '[,"\n]') {
                    '"' + ($val -replace '"', '""') + '"'
                }
                else {
                    $val
                }
            }

            $headerLine = $headers -join ','
            $csvLine = $escapedValues -join ','

            # Check if file needs headers
            $writeHeaders = $false
            if (-not (Test-Path $Path) -or $Overwrite) {
                $writeHeaders = $true
            }

            # Check file lock if file exists
            if (Test-Path $Path) {
                if (Get-Command -Name 'Test-FileLock' -ErrorAction SilentlyContinue) {
                    $isLocked = Test-FileLock -FilePath $Path -Silent
                    if ($isLocked) {
                        Write-Warning "File '$Path' is locked. Waiting for it to become available..."
                        Test-FileLock -FilePath $Path  # Interactive wait
                    }
                }
            }

            # Write to file
            if ($PSCmdlet.ShouldProcess($Path, "Export metrics")) {
                if ($Overwrite) {
                    if (-not $NoHeader) {
                        $headerLine | Out-File -FilePath $Path -Encoding UTF8
                    }
                    $csvLine | Out-File -FilePath $Path -Append -Encoding UTF8
                }
                else {
                    if ($writeHeaders -and -not $NoHeader) {
                        $headerLine | Out-File -FilePath $Path -Encoding UTF8
                    }
                    $csvLine | Out-File -FilePath $Path -Append -Encoding UTF8
                }
            }

            if ($PassThru) {
                return $csvLine
            }
        }
        catch {
            Write-Error "Failed to export metrics to CSV: $_"
        }
    }
}