Public/Get-GlookoCGMStatsExtended.ps1

function Get-GlookoCGMStatsExtended {
    <#
    .SYNOPSIS
        Analyzes CGM data with extended statistics including very low and very high categories.
    
    .DESCRIPTION
        This function provides detailed analysis of Continuous Glucose Monitoring (CGM) data
        with customizable ranges. By default, uses three categories (low, in range, high).
        When UseVeryLowHigh switch is enabled, adds very low and very high categories.
        Results are grouped by date and include both counts and percentages. Supports date range filtering.
        
        Default ranges without UseVeryLowHigh (mmol/L):
        - Low: < 4.0 mmol/L (< 70 mg/dL)
        - In Range: 4.0-10.0 mmol/L (70-180 mg/dL)
        - High: > 10.0 mmol/L (> 180 mg/dL)
        
        Default ranges with UseVeryLowHigh (mmol/L):
        - Very Low: < 3.0 mmol/L (< 54 mg/dL)
        - Low: 3.0-3.9 mmol/L (54-70 mg/dL)
        - In Range: 4.0-10.0 mmol/L (70-180 mg/dL)
        - High: 10.1-13.9 mmol/L (181-250 mg/dL)
        - Very High: >= 14.0 mmol/L (>= 250 mg/dL)
    
    .PARAMETER InputObject
        CGM data to analyze. Accepts output from Get-GlookoDataset or Import-GlookoCSV.
        Expected to have 'Timestamp' and a glucose value column.
    
    .PARAMETER UseVeryLowHigh
        Switch to enable very low and very high categories. When enabled, uses five categories
        instead of three. When disabled, uses same categories as Get-GlookoCGMStats.
    
    .PARAMETER VeryLowThreshold
        Threshold for very low readings in mmol/L. Default is 3.0. Only used when UseVeryLowHigh is enabled.
        Readings below this are considered very low.
    
    .PARAMETER LowThreshold
        Lower threshold for target range in mmol/L. Default is 4.0.
        When UseVeryLowHigh is disabled: readings below this are considered low.
        When UseVeryLowHigh is enabled: readings between VeryLowThreshold and LowThreshold are considered low.
    
    .PARAMETER HighThreshold
        Upper threshold for target range in mmol/L. Default is 10.0.
        Readings between LowThreshold and HighThreshold are considered in range.
    
    .PARAMETER VeryHighThreshold
        Threshold for very high readings in mmol/L. Default is 14.0. Only used when UseVeryLowHigh is enabled.
        Readings above this are considered very high.
    
    .PARAMETER GlucoseColumn
        Name of the column containing glucose values.
        Default is 'CGM Glucose Value (mmol/l)'.
    
    .PARAMETER StartDate
        Start date for filtering data (inclusive). Format: yyyy-MM-dd
    
    .PARAMETER EndDate
        End date for filtering data (inclusive). Format: yyyy-MM-dd
    
    .PARAMETER Days
        Number of days to include in analysis (from most recent date backwards).
    
    .EXAMPLE
        Import-GlookoZip -Path "export.zip" | Get-GlookoDataset -Name "cgm" | Get-GlookoCGMStatsExtended
        Analyzes CGM data with three categories (low, in range, high) grouped by date.
    
    .EXAMPLE
        $cgmData | Get-GlookoCGMStatsExtended -UseVeryLowHigh
        Analyzes CGM data with five categories including very low and very high.
    
    .EXAMPLE
        $cgmData = Import-GlookoCSV -Path "cgm.csv"
        Get-GlookoCGMStatsExtended -InputObject $cgmData.Data -Days 7
        Analyzes the last 7 days of CGM data.
    
    .EXAMPLE
        Get-GlookoCGMStatsExtended -InputObject $cgmData -StartDate "2025-10-20" -EndDate "2025-10-27"
        Analyzes CGM data for a specific date range.
    
    .EXAMPLE
        Get-GlookoCGMStatsExtended -InputObject $cgmData -UseVeryLowHigh -VeryLowThreshold 2.8 -VeryHighThreshold 15.0
        Analyzes with custom thresholds for very low and very high categories.
    
    .OUTPUTS
        PSCustomObject
        Returns objects with Date and detailed statistics for each glucose category.
        
        Without UseVeryLowHigh: Low, InRange, High (same as Get-GlookoCGMStats)
        With UseVeryLowHigh: VeryLow, Low, InRange, High, VeryHigh
    #>

    
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [AllowEmptyCollection()]
        [array]$InputObject,
        
        [Parameter(Mandatory = $false)]
        [switch]$UseVeryLowHigh,
        
        [Parameter(Mandatory = $false)]
        [ValidateRange(0, 50)]
        [double]$VeryLowThreshold = 3.0,
        
        [Parameter(Mandatory = $false)]
        [ValidateRange(0, 50)]
        [double]$LowThreshold = 4.0,
        
        [Parameter(Mandatory = $false)]
        [ValidateRange(0, 50)]
        [double]$HighThreshold = 10.0,
        
        [Parameter(Mandatory = $false)]
        [ValidateRange(0, 50)]
        [double]$VeryHighThreshold = 14.0,
        
        [Parameter(Mandatory = $false)]
        [string]$GlucoseColumn = 'CGM Glucose Value (mmol/l)',
        
        [Parameter(Mandatory = $false)]
        [datetime]$StartDate,
        
        [Parameter(Mandatory = $false)]
        [datetime]$EndDate,
        
        [Parameter(Mandatory = $false)]
        [ValidateRange(1, 365)]
        [int]$Days
    )
    
    begin {
        Write-Verbose "Starting Get-GlookoCGMStatsExtended function"
        if ($UseVeryLowHigh) {
            Write-Verbose "Thresholds: VeryLow<$VeryLowThreshold, Low=$VeryLowThreshold-$LowThreshold, InRange=$LowThreshold-$HighThreshold, High=$HighThreshold-$VeryHighThreshold, VeryHigh>=$VeryHighThreshold mmol/L"
        } else {
            Write-Verbose "Thresholds: Low<$LowThreshold, InRange=$LowThreshold-$HighThreshold, High>$HighThreshold mmol/L"
        }
        $allData = @()
    }
    
    process {
        foreach ($item in $InputObject) {
            $allData += $item
        }
    }
    
    end {
        try {
            if ($allData.Count -eq 0) {
                Write-Warning "No CGM data provided to analyze"
                return @()
            }
            
            Write-Verbose "Processing $($allData.Count) CGM reading(s)"
            
            # Verify the glucose column exists
            $firstRecord = $allData | Select-Object -First 1
            if (-not ($firstRecord.PSObject.Properties.Name -contains $GlucoseColumn)) {
                Write-Error "Glucose column '$GlucoseColumn' not found in data. Available columns: $($firstRecord.PSObject.Properties.Name -join ', ')"
                return @()
            }
            
            # Filter by date if specified
            $filteredData = $allData
            
            if ($PSBoundParameters.ContainsKey('StartDate') -or $PSBoundParameters.ContainsKey('EndDate') -or $PSBoundParameters.ContainsKey('Days')) {
                Write-Verbose "Applying date filters..."
                
                # Convert timestamps to datetime for filtering
                $dataWithDates = $filteredData | Where-Object { $_.Timestamp } | ForEach-Object {
                    $_ | Add-Member -NotePropertyName 'ParsedDate' -NotePropertyValue ([datetime]$_.Timestamp) -PassThru -Force
                }
                
                if ($PSBoundParameters.ContainsKey('Days')) {
                    $maxDate = ($dataWithDates | Measure-Object -Property ParsedDate -Maximum).Maximum
                    $minDate = $maxDate.AddDays(-$Days + 1).Date
                    Write-Verbose "Filtering to last $Days days: $($minDate.ToString('yyyy-MM-dd')) to $($maxDate.ToString('yyyy-MM-dd'))"
                    $filteredData = $dataWithDates | Where-Object { $_.ParsedDate -ge $minDate }
                } else {
                    if ($PSBoundParameters.ContainsKey('StartDate')) {
                        Write-Verbose "Filtering from $($StartDate.ToString('yyyy-MM-dd'))"
                        $dataWithDates = $dataWithDates | Where-Object { $_.ParsedDate.Date -ge $StartDate.Date }
                    }
                    if ($PSBoundParameters.ContainsKey('EndDate')) {
                        Write-Verbose "Filtering to $($EndDate.ToString('yyyy-MM-dd'))"
                        $dataWithDates = $dataWithDates | Where-Object { $_.ParsedDate.Date -le $EndDate.Date }
                    }
                    $filteredData = $dataWithDates
                }
                
                Write-Verbose "After filtering: $($filteredData.Count) reading(s)"
            }
            
            if ($filteredData.Count -eq 0) {
                Write-Warning "No data remaining after date filtering"
                return @()
            }
            
            # Group data by date
            $groupedByDate = $filteredData | Group-Object -Property { 
                if ($_.Timestamp) {
                    ([datetime]$_.Timestamp).ToString('yyyy-MM-dd')
                } else {
                    'Unknown'
                }
            }
            
            Write-Verbose "Found data for $($groupedByDate.Count) date(s)"
            
            $results = foreach ($dateGroup in $groupedByDate) {
                $date = $dateGroup.Name
                $readings = $dateGroup.Group
                
                Write-Verbose "Analyzing $($readings.Count) reading(s) for $date"
                
                if ($UseVeryLowHigh) {
                    # Count readings in five categories
                    $veryLowCount = 0
                    $lowCount = 0
                    $inRangeCount = 0
                    $highCount = 0
                    $veryHighCount = 0
                    
                    foreach ($reading in $readings) {
                        $glucoseValue = [double]$reading.$GlucoseColumn
                        
                        if ($glucoseValue -lt $VeryLowThreshold) {
                            $veryLowCount++
                        } elseif ($glucoseValue -lt $LowThreshold) {
                            $lowCount++
                        } elseif ($glucoseValue -le $HighThreshold) {
                            $inRangeCount++
                        } elseif ($glucoseValue -lt $VeryHighThreshold) {
                            $highCount++
                        } else {
                            $veryHighCount++
                        }
                    }
                    
                    $totalCount = $readings.Count
                    
                    # Calculate percentages
                    $veryLowPercent = if ($totalCount -gt 0) { [math]::Round(($veryLowCount / $totalCount) * 100, 1) } else { 0 }
                    $lowPercent = if ($totalCount -gt 0) { [math]::Round(($lowCount / $totalCount) * 100, 1) } else { 0 }
                    $inRangePercent = if ($totalCount -gt 0) { [math]::Round(($inRangeCount / $totalCount) * 100, 1) } else { 0 }
                    $highPercent = if ($totalCount -gt 0) { [math]::Round(($highCount / $totalCount) * 100, 1) } else { 0 }
                    $veryHighPercent = if ($totalCount -gt 0) { [math]::Round(($veryHighCount / $totalCount) * 100, 1) } else { 0 }
                    
                    # Create result object with five categories
                    [PSCustomObject]@{
                        Date = $date
                        TotalReadings = $totalCount
                        VeryLow = $veryLowCount
                        VeryLowPercent = $veryLowPercent
                        Low = $lowCount
                        LowPercent = $lowPercent
                        InRange = $inRangeCount
                        InRangePercent = $inRangePercent
                        High = $highCount
                        HighPercent = $highPercent
                        VeryHigh = $veryHighCount
                        VeryHighPercent = $veryHighPercent
                        Ranges = "VeryLow<$VeryLowThreshold, Low=$VeryLowThreshold-$LowThreshold, InRange=$LowThreshold-$HighThreshold, High=$HighThreshold-$VeryHighThreshold, VeryHigh>=$VeryHighThreshold mmol/L"
                    }
                } else {
                    # Count readings in three categories (same as basic function)
                    $lowCount = 0
                    $inRangeCount = 0
                    $highCount = 0
                    
                    foreach ($reading in $readings) {
                        $glucoseValue = [double]$reading.$GlucoseColumn
                        
                        if ($glucoseValue -lt $LowThreshold) {
                            $lowCount++
                        } elseif ($glucoseValue -le $HighThreshold) {
                            $inRangeCount++
                        } else {
                            $highCount++
                        }
                    }
                    
                    $totalCount = $readings.Count
                    
                    # Calculate percentages
                    $lowPercent = if ($totalCount -gt 0) { [math]::Round(($lowCount / $totalCount) * 100, 1) } else { 0 }
                    $inRangePercent = if ($totalCount -gt 0) { [math]::Round(($inRangeCount / $totalCount) * 100, 1) } else { 0 }
                    $highPercent = if ($totalCount -gt 0) { [math]::Round(($highCount / $totalCount) * 100, 1) } else { 0 }
                    
                    # Create result object with three categories
                    [PSCustomObject]@{
                        Date = $date
                        TotalReadings = $totalCount
                        Low = $lowCount
                        LowPercent = $lowPercent
                        InRange = $inRangeCount
                        InRangePercent = $inRangePercent
                        High = $highCount
                        HighPercent = $highPercent
                        TargetRange = "$LowThreshold-$HighThreshold mmol/L"
                    }
                }
            }
            
            Write-Verbose "Completed analysis for $($results.Count) date(s)"
            return $results
        }
        catch {
            Write-Error "Error analyzing CGM data: $($_.Exception.Message)"
            throw
        }
        finally {
            Write-Verbose "Get-GlookoCGMStatsExtended function completed"
        }
    }
}

# Export the function if this script is being dot-sourced
Export-ModuleMember -Function Get-GlookoCGMStatsExtended