internal/functions/generators/New-SldgAIStructuredValue.ps1

function New-SldgAIStructuredValue {
    <#
    .SYNOPSIS
        Uses AI to generate a single structured data value.
    .DESCRIPTION
        When ContextColumn/ContextValue are provided, generates structure-varied data
        keyed by the context value (e.g., different JSON schemas per report type).
        Results are cached per (table, column, type, context) for reuse.
    #>

    [CmdletBinding()]
    param (
        [string]$Type,
        [string]$ColumnName,
        [string]$TableName,
        [string]$SchemaHint,
        [int]$MaxLength = 4000,
        [string]$AIGenerationHint,
        [string]$ContextColumn,
        [string]$ContextValue,
        [string[]]$ValueExamples
    )

    # Build cache key — include context value when present for per-variant caching
    # Escape pipe characters in components to prevent key collision across different table/column combinations
    $safeTable = $TableName.Replace('|', '||')
    $safeColumn = $ColumnName.Replace('|', '||')
    $safeType = $Type.Replace('|', '||')
    $contextSuffix = if ($ContextColumn -and $ContextValue) { "|ctx:$($ContextValue.Replace('|', '||'))" } else { '' }
    $cacheKey = "StructuredData|$safeTable|$safeColumn|$safeType$contextSuffix"

    # Return from template cache if we have previously generated values
    if ($script:SldgState.AIValueCache.ContainsKey($cacheKey)) {
        $cached = $script:SldgState.AIValueCache[$cacheKey]
        if ($cached -and $cached.Count -gt 0) {
            return ($cached | Get-Random)
        }
    }

    $format = if ($Type -eq 'Json') { 'JSON' } else { 'XML' }
    $hintText = if ($SchemaHint) { "`nSchema hint (from view definition): $SchemaHint" } else { '' }
    $aiHintText = if ($AIGenerationHint) { "`nGeneration context: $AIGenerationHint" } else { '' }
    $contextText = if ($ContextColumn -and $ContextValue) {
        # Sanitize ContextValue to mitigate prompt injection from DB data
        $safeContextValue = Remove-SldgUnsafeChar -Text $ContextValue -Mode General -MaxLength 500
        "`nContext: The column '$ContextColumn' for this row has the value '$safeContextValue'. Generate $format content that is appropriate for this specific $ContextColumn value. The structure and fields should reflect what '$safeContextValue' means in business terms."
    } else { '' }
    $examplesText = if ($ValueExamples -and $ValueExamples.Count -gt 0) {
        $exList = ($ValueExamples | ForEach-Object { " - $_" }) -join "`n"
        "`nExample values (use as structure reference):`n$exList"
    } else { '' }

    # Use contextual prompt when we have context, standard otherwise
    $promptPurpose = if ($ContextColumn -and $ContextValue) { 'structured-value-contextual' } else { 'structured-value' }

    $promptVars = @{
        Format     = $format
        TableName  = $TableName
        ColumnName = $ColumnName
        MaxLength  = $MaxLength
        SchemaHint = $hintText
    }
    # Contextual prompt uses additional variables
    if ($ContextColumn -and $ContextValue) {
        $promptVars['AIGenerationHint'] = $aiHintText
        $promptVars['ContextColumn'] = $ContextColumn
        $promptVars['ContextValue'] = $ContextValue
        $promptVars['ContextText'] = $contextText
        $promptVars['ExamplesText'] = $examplesText
    }

    $systemPrompt = Resolve-SldgPromptTemplate -Purpose $promptPurpose -Variables $promptVars

    # Fall back to standard prompt if contextual template not found
    if (-not $systemPrompt -and $promptPurpose -eq 'structured-value-contextual') {
        $systemPrompt = Resolve-SldgPromptTemplate -Purpose 'structured-value' -Variables $promptVars
    }

    if (-not $systemPrompt) {
        Write-PSFMessage -Level Warning -Message ($script:strings.'Prompt.ResolveFailed' -f $promptPurpose)
        return $null
    }

    # Append AI hint and context to the standard prompt when not using contextual template
    if ($promptPurpose -eq 'structured-value') {
        $systemPrompt += $aiHintText + $contextText + $examplesText
    }

    $safeTableName = Remove-SldgUnsafeChar -Text $TableName -Mode Identifier
    $safeColumnName = Remove-SldgUnsafeChar -Text $ColumnName -Mode Identifier
    $contextLabel = if ($ContextValue) { " (context: $ContextColumn=$ContextValue)" } else { '' }
    $userMessage = "Generate 10 realistic $format values for column '$safeColumnName' in table '$safeTableName'$contextLabel."

    Write-PSFMessage -Level Verbose -Message ($script:strings.'StructuredData.AIGenerating' -f $Type, $TableName, $ColumnName)

    try {
        $response = Invoke-SldgAIRequest -SystemPrompt $systemPrompt -UserMessage $userMessage -Purpose $promptPurpose

        if (-not $response) { return $null }

        $jsonText = $response
        if ($jsonText -match '```(?:json)?\s*\n?([\s\S]*?)\n?```') {
            $jsonText = $Matches[1]
        }
        elseif ($jsonText -match '(\[[\s\S]*?\])') {
            $jsonText = $Matches[1]
        }

        $parsed = $jsonText | ConvertFrom-Json -ErrorAction Stop

        if ($parsed -and $parsed.Count -gt 0) {
            # Validate and filter results
            $valid = @(foreach ($item in $parsed) {
                $str = if ($item -is [string]) { $item } else { $item | ConvertTo-Json -Depth 10 -Compress }
                if ($str.Length -le $MaxLength) { $str }
            })

            if ($valid.Count -gt 0) {
                # Cache for reuse (keyed by context for variant caching)
                Invoke-SldgCacheEviction -Cache $script:SldgState.AIValueCache -CacheName 'AIValueCache'
                $script:SldgState.AIValueCache[$cacheKey] = $valid
                $script:SldgState.CacheTimestamps["AIValueCache$($script:CacheKeySeparator)$cacheKey"] = [datetime]::UtcNow
                Write-PSFMessage -Level Verbose -Message ($script:strings.'StructuredData.AIGenerated' -f $Type, $valid.Count, $TableName, $ColumnName)
                return ($valid | Get-Random)
            }
        }
    }
    catch {
        Write-PSFMessage -Level Warning -Message ($script:strings.'StructuredData.AIFailed' -f $Type, $TableName, $ColumnName, $_)
    }

    # Return type-appropriate fallback instead of $null to avoid NOT NULL constraint violations
    $fallback = if ($Type -eq 'Xml') { '<root />' } else { '{}' }
    return $fallback
}