Private/Cache.ps1

# InTUI Caching Layer
# Provides local caching for Graph API responses to improve performance

function Initialize-InTUICache {
    <#
    .SYNOPSIS
        Initializes the cache directory structure.
    #>

    [CmdletBinding()]
    param()

    if (-not (Test-Path $script:CachePath)) {
        try {
            New-Item -Path $script:CachePath -ItemType Directory -Force | Out-Null
            Write-InTUILog -Message "Cache directory created" -Context @{ Path = $script:CachePath }
        }
        catch {
            Write-InTUILog -Level 'WARN' -Message "Failed to create cache directory: $($_.Exception.Message)"
            $script:CacheEnabled = $false
        }
    }
}

function Get-InTUICacheKey {
    <#
    .SYNOPSIS
        Generates a cache key from URI and parameters.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$Uri,

        [Parameter()]
        [string]$Method = 'GET',

        [Parameter()]
        [switch]$Beta
    )

    $keySource = "$Method|$Uri|$Beta"
    $bytes = [System.Text.Encoding]::UTF8.GetBytes($keySource)
    $hash = [System.Security.Cryptography.SHA256]::Create().ComputeHash($bytes)
    $hashString = [BitConverter]::ToString($hash) -replace '-', ''

    return $hashString.Substring(0, 32)
}

function Get-InTUICachedResponse {
    <#
    .SYNOPSIS
        Retrieves a cached response if valid.
    .DESCRIPTION
        Checks the cache for a valid response. Returns $null if cache miss or expired.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$Uri,

        [Parameter()]
        [string]$Method = 'GET',

        [Parameter()]
        [switch]$Beta
    )

    if (-not $script:CacheEnabled) {
        return $null
    }

    # Only cache GET requests
    if ($Method -ne 'GET') {
        return $null
    }

    $cacheKey = Get-InTUICacheKey -Uri $Uri -Method $Method -Beta:$Beta
    $cacheFile = Join-Path $script:CachePath "$cacheKey.json"

    if (-not (Test-Path $cacheFile)) {
        return $null
    }

    try {
        $cached = Get-Content $cacheFile -Raw | ConvertFrom-Json

        $cachedTime = [DateTime]::Parse($cached.Timestamp)
        $age = ([DateTime]::UtcNow - $cachedTime).TotalSeconds

        if ($age -gt $script:CacheTTL) {
            Write-InTUILog -Message "Cache expired" -Context @{ Uri = $Uri; Age = [math]::Round($age) }
            Remove-Item $cacheFile -Force -ErrorAction SilentlyContinue
            return $null
        }

        Write-InTUILog -Message "Cache hit" -Context @{ Uri = $Uri; Age = [math]::Round($age) }
        return $cached.Data
    }
    catch {
        Write-InTUILog -Level 'WARN' -Message "Failed to read cache: $($_.Exception.Message)"
        return $null
    }
}

function Set-InTUICachedResponse {
    <#
    .SYNOPSIS
        Stores a response in the cache.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$Uri,

        [Parameter(Mandatory)]
        $Data,

        [Parameter()]
        [string]$Method = 'GET',

        [Parameter()]
        [switch]$Beta
    )

    if (-not $script:CacheEnabled) {
        return
    }

    # Only cache GET requests
    if ($Method -ne 'GET') {
        return
    }

    Initialize-InTUICache

    $cacheKey = Get-InTUICacheKey -Uri $Uri -Method $Method -Beta:$Beta
    $cacheFile = Join-Path $script:CachePath "$cacheKey.json"

    try {
        $cacheEntry = @{
            Uri       = $Uri
            Method    = $Method
            Beta      = [bool]$Beta
            Timestamp = [DateTime]::UtcNow.ToString('o')
            Data      = $Data
        }

        $cacheEntry | ConvertTo-Json -Depth 20 | Set-Content $cacheFile -Encoding UTF8
        Write-InTUILog -Message "Cache write" -Context @{ Uri = $Uri; Key = $cacheKey }
    }
    catch {
        Write-InTUILog -Level 'WARN' -Message "Failed to write cache: $($_.Exception.Message)"
    }
}

function Clear-InTUICache {
    <#
    .SYNOPSIS
        Clears cached responses.
    .PARAMETER ExpiredOnly
        Only remove expired entries.
    #>

    [CmdletBinding()]
    param(
        [Parameter()]
        [switch]$ExpiredOnly
    )

    if (-not (Test-Path $script:CachePath)) {
        Write-InTUILog -Message "Cache directory does not exist"
        return 0
    }

    $cacheFiles = Get-ChildItem -Path $script:CachePath -Filter '*.json' -ErrorAction SilentlyContinue
    $removedCount = 0

    foreach ($file in $cacheFiles) {
        $shouldRemove = $true

        if ($ExpiredOnly) {
            try {
                $cached = Get-Content $file.FullName -Raw | ConvertFrom-Json
                $cachedTime = [DateTime]::Parse($cached.Timestamp)
                $age = ([DateTime]::UtcNow - $cachedTime).TotalSeconds

                if ($age -le $script:CacheTTL) {
                    $shouldRemove = $false
                }
            }
            catch {
                # If we can't read it, remove it
                $shouldRemove = $true
            }
        }

        if ($shouldRemove) {
            Remove-Item $file.FullName -Force -ErrorAction SilentlyContinue
            $removedCount++
        }
    }

    Write-InTUILog -Message "Cache cleared" -Context @{ RemovedCount = $removedCount; ExpiredOnly = [bool]$ExpiredOnly }
    return $removedCount
}

function Get-InTUICacheStats {
    <#
    .SYNOPSIS
        Returns cache statistics.
    #>

    [CmdletBinding()]
    param()

    if (-not (Test-Path $script:CachePath)) {
        return @{
            Enabled    = $script:CacheEnabled
            TTL        = $script:CacheTTL
            EntryCount = 0
            TotalSize  = 0
            ValidCount = 0
            ExpiredCount = 0
        }
    }

    $cacheFiles = Get-ChildItem -Path $script:CachePath -Filter '*.json' -ErrorAction SilentlyContinue
    $totalSize = 0
    $validCount = 0
    $expiredCount = 0

    foreach ($file in $cacheFiles) {
        $totalSize += $file.Length

        try {
            $cached = Get-Content $file.FullName -Raw | ConvertFrom-Json
            $cachedTime = [DateTime]::Parse($cached.Timestamp)
            $age = ([DateTime]::UtcNow - $cachedTime).TotalSeconds

            if ($age -le $script:CacheTTL) {
                $validCount++
            }
            else {
                $expiredCount++
            }
        }
        catch {
            $expiredCount++
        }
    }

    return @{
        Enabled      = $script:CacheEnabled
        TTL          = $script:CacheTTL
        EntryCount   = $cacheFiles.Count
        TotalSize    = $totalSize
        ValidCount   = $validCount
        ExpiredCount = $expiredCount
    }
}