private/uitool/Show-UiFilterBuilder.ps1

function Show-UiFilterBuilder {
    <#
    .SYNOPSIS
        Shows a filter pattern builder popup and returns the selected pattern.
    .PARAMETER CurrentValue
        The current filter value to pre-populate.
    .PARAMETER Mode
        The type of filter syntax: File, AD, WMI, or Generic.
    .PARAMETER ThemeColors
        Optional theme colors hashtable. If not provided, uses active theme.
    #>

    [CmdletBinding()]
    param(
        [string]$CurrentValue,
        
        [ValidateSet('File', 'AD', 'WMI', 'Generic')]
        [string]$Mode = 'Generic',
        
        [hashtable]$ThemeColors
    )
    
    $colors = $ThemeColors
    if (!$colors -or !$colors.WindowBg) {
        $colors = Get-ThemeColors
    }
    if (!$colors -or !$colors.WindowBg) {
        $themeName = [PsUi.ModuleContext]::ActiveTheme
        if (!$themeName) { $themeName = 'Light' }
        $colors = Initialize-UITheme -Theme $themeName
    }
    
    # Define presets and syntax help based on mode
    switch ($Mode) {
        'File' {
            $presets = [ordered]@{
                'All Files'      = '*.*'
                'PowerShell'     = '*.ps1'
                'Scripts'        = '*.ps1, *.psm1, *.psd1'
                'Text Files'     = '*.txt'
                'Log Files'      = '*.log'
                'Config Files'   = '*.json, *.xml, *.yaml, *.yml, *.ini, *.config'
                'Documents'      = '*.docx, *.doc, *.pdf, *.xlsx, *.xls'
                'Images'         = '*.png, *.jpg, *.jpeg, *.gif, *.bmp, *.ico'
                'Code Files'     = '*.cs, *.js, *.ts, *.py, *.java, *.cpp, *.h'
                'Web Files'      = '*.html, *.htm, *.css, *.js'
                'Archives'       = '*.zip, *.7z, *.rar, *.tar, *.gz'
                'Executables'    = '*.exe, *.dll, *.msi'
            }
            $syntaxHelp = @(
                @{ Symbol = '*';  Desc = 'Any characters' }
                @{ Symbol = '?';  Desc = 'Single character' }
            )
            $exampleText  = 'e.g. *.txt, file*.log, *report*'
            $syntaxTitle  = 'Wildcards'
            $popupTitle   = 'File Filter Builder'
            $popupHeight  = 380
        }
        
        'AD' {
            $presets = [ordered]@{
                'All Users'           = '*'
                'Enabled Users'       = 'Enabled -eq $true'
                'Disabled Users'      = 'Enabled -eq $false'
                'Name Starts With'    = 'Name -like "A*"'
                'Name Contains'       = 'Name -like "*john*"'
                'Email Domain'        = 'EmailAddress -like "*@contoso.com"'
                'Department'          = 'Department -eq "IT"'
                'Title Contains'      = 'Title -like "*Manager*"'
                'Created Recently'    = 'Created -ge "2024-01-01"'
                'Password Expired'    = 'PasswordExpired -eq $true'
                'Locked Out'          = 'LockedOut -eq $true'
                'In OU'               = 'DistinguishedName -like "*OU=Sales,*"'
            }
            $syntaxHelp = @(
                @{ Symbol = '-eq';   Desc = 'Equals' }
                @{ Symbol = '-ne';   Desc = 'Not equal' }
                @{ Symbol = '-like'; Desc = 'Wildcard match (* for any)' }
                @{ Symbol = '-gt';   Desc = 'Greater than' }
                @{ Symbol = '-lt';   Desc = 'Less than' }
                @{ Symbol = '-and';  Desc = 'Both must match' }
                @{ Symbol = '-or';   Desc = 'Either can match' }
            )
            $exampleText  = 'e.g. Name -like "John*", Enabled -eq $true'
            $syntaxTitle  = 'Filter Operators'
            $popupTitle   = 'AD Filter Builder'
            $popupHeight  = 480
        }
        
        'WMI' {
            $presets = [ordered]@{
                'All Instances'      = '*'
                'Name Equals'        = "Name = 'value'"
                'Name Like'          = "Name LIKE '%pattern%'"
                'Status Running'     = "State = 'Running'"
                'Status Stopped'     = "State = 'Stopped'"
                'DriveType Fixed'    = 'DriveType = 3'
                'DriveType Removable'= 'DriveType = 2'
                'Not Null'           = 'PropertyName IS NOT NULL'
            }
            $syntaxHelp = @(
                @{ Symbol = '=';       Desc = 'Equals' }
                @{ Symbol = '<>';      Desc = 'Not equal' }
                @{ Symbol = 'LIKE';    Desc = 'Wildcard match (% for any)' }
                @{ Symbol = 'AND';     Desc = 'Both must match' }
                @{ Symbol = 'OR';      Desc = 'Either can match' }
                @{ Symbol = 'IS NULL'; Desc = 'Null check' }
            )
            $exampleText  = "e.g. Name = 'notepad.exe', State = 'Running'"
            $syntaxTitle  = 'WQL Operators'
            $popupTitle   = 'WMI Filter Builder'
            $popupHeight  = 460
        }
        
        default {
            # Generic mode - PowerShell-style operators
            $presets = [ordered]@{
                'All'                = '*'
                'Equals'             = 'Property -eq "value"'
                'Not Equals'         = 'Property -ne "value"'
                'Like (Wildcard)'    = 'Property -like "*pattern*"'
                'Greater Than'       = 'Property -gt 100'
                'Less Than'          = 'Property -lt 100'
                'Contains'           = 'Property -contains "value"'
                'Multiple Conditions'= '(Prop1 -eq "a") -and (Prop2 -gt 10)'
            }
            $syntaxHelp = @(
                @{ Symbol = '-eq';       Desc = 'Equals' }
                @{ Symbol = '-ne';       Desc = 'Not equal' }
                @{ Symbol = '-like';     Desc = 'Wildcard (* and ?)' }
                @{ Symbol = '-match';    Desc = 'Regex' }
                @{ Symbol = '-gt / -lt'; Desc = 'Greater / less than' }
                @{ Symbol = '-and/-or';  Desc = 'Combine filters' }
            )
            $exampleText  = 'e.g. Name -eq "Test", Value -gt 100'
            $syntaxTitle  = 'Filter Operators'
            $popupTitle   = 'Filter Builder'
            $popupHeight  = 440
        }
    }
    
    $popup = [System.Windows.Window]@{
        Title                 = $popupTitle
        Width                 = 420
        Height                = $popupHeight + 32
        WindowStartupLocation = 'CenterScreen'
        ResizeMode            = 'NoResize'
        WindowStyle           = 'None'
        AllowsTransparency    = $true
        FontFamily            = [System.Windows.Media.FontFamily]::new('Segoe UI')
        Background            = [System.Windows.Media.Brushes]::Transparent
        Foreground            = ConvertTo-UiBrush $colors.WindowFg
        Opacity               = 0
    }
    
    $mainBorder = [System.Windows.Controls.Border]@{
        Margin          = [System.Windows.Thickness]::new(16)
        BorderBrush     = ConvertTo-UiBrush $colors.Border
        BorderThickness = [System.Windows.Thickness]::new(1)
        Background      = ConvertTo-UiBrush $colors.WindowBg
    }

    $shadow = [System.Windows.Media.Effects.DropShadowEffect]@{
        BlurRadius  = 16
        ShadowDepth = 4
        Opacity     = 0.35
        Color       = [System.Windows.Media.Colors]::Black
        Direction   = 270
    }
    $mainBorder.Effect = $shadow
    $popup.Content = $mainBorder
    
    $outerPanel = [System.Windows.Controls.DockPanel]@{
        LastChildFill = $true
    }
    $mainBorder.Child = $outerPanel
    
    # Custom titlebar (no padding so close button extends to edge)
    $titleBar = [System.Windows.Controls.Border]@{
        Background = ConvertTo-UiBrush $colors.HeaderBackground
        Height     = 36
    }
    [System.Windows.Controls.DockPanel]::SetDock($titleBar, 'Top')
    
    $titleGrid = [System.Windows.Controls.Grid]::new()
    $titleBar.Child = $titleGrid
    
    # Title text with left margin for visual spacing
    $titleText = [System.Windows.Controls.TextBlock]@{
        Text              = $popupTitle
        FontSize          = 14
        FontWeight        = 'SemiBold'
        Foreground        = ConvertTo-UiBrush $colors.HeaderForeground
        VerticalAlignment = 'Center'
        Margin            = [System.Windows.Thickness]::new(12, 0, 0, 0)
    }
    [void]$titleGrid.Children.Add($titleText)
    
    # Close button - foreground is set inside the template to avoid local value precedence issues
    $closeBtn = [System.Windows.Controls.Button]@{
        Content             = [PsUi.ModuleContext]::GetIcon('Close')
        FontFamily          = [System.Windows.Media.FontFamily]::new('Segoe MDL2 Assets')
        FontSize            = 12
        Width               = 36
        Height              = 36
        HorizontalAlignment = 'Right'
        Background          = [System.Windows.Media.Brushes]::Transparent
        BorderThickness     = [System.Windows.Thickness]::new(0)
        Cursor              = [System.Windows.Input.Cursors]::Hand
    }
    $closeBtn.OverridesDefaultStyle = $true
    
    # Apply hover template (red background, white foreground on hover)
    $closeBtnTemplate = @'
<ControlTemplate xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 TargetType="Button">
    <Border x:Name="border" Background="Transparent">
        <ContentPresenter x:Name="content" HorizontalAlignment="Center" VerticalAlignment="Center"
                          TextElement.Foreground="{DynamicResource HeaderForegroundBrush}"/>
    </Border>
    <ControlTemplate.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
            <Setter TargetName="border" Property="Background" Value="#E81123"/>
            <Setter TargetName="content" Property="TextElement.Foreground" Value="White"/>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>
'@

    $closeBtn.Template = [System.Windows.Markup.XamlReader]::Parse($closeBtnTemplate)
    $closeBtn.Add_Click({ $popup.DialogResult = $false; $popup.Close() }.GetNewClosure())
    [void]$titleGrid.Children.Add($closeBtn)
    
    # Enable window dragging
    $titleBar.Add_MouseLeftButtonDown({ $popup.DragMove() }.GetNewClosure())
    [void]$outerPanel.Children.Add($titleBar)
    
    $mainPanel = [System.Windows.Controls.StackPanel]::new()
    $mainPanel.Margin = [System.Windows.Thickness]::new(16)
    
    # Presets section
    $presetsLabel = [System.Windows.Controls.TextBlock]::new()
    $presetsLabel.Text = 'Quick Presets'
    $presetsLabel.FontWeight = 'SemiBold'
    $presetsLabel.FontSize = 14
    $presetsLabel.Margin = [System.Windows.Thickness]::new(0, 0, 0, 8)
    $presetsLabel.Foreground = ConvertTo-UiBrush $colors.WindowFg
    [void]$mainPanel.Children.Add($presetsLabel)
    
    $presetsCombo = [System.Windows.Controls.ComboBox]::new()
    $presetsCombo.Height = 32
    $presetsCombo.Margin = [System.Windows.Thickness]::new(0, 0, 0, 16)
    Set-ComboBoxStyle -ComboBox $presetsCombo
    
    [void]$presetsCombo.Items.Add('(Select a preset...)')
    foreach ($key in $presets.Keys) {
        [void]$presetsCombo.Items.Add($key)
    }
    $presetsCombo.SelectedIndex = 0
    [void]$mainPanel.Children.Add($presetsCombo)
    
    # Custom pattern section
    $customLabel = [System.Windows.Controls.TextBlock]::new()
    $customLabel.Text = 'Custom Filter'
    $customLabel.FontWeight = 'SemiBold'
    $customLabel.FontSize = 14
    $customLabel.Margin = [System.Windows.Thickness]::new(0, 0, 0, 8)
    $customLabel.Foreground = ConvertTo-UiBrush $colors.WindowFg
    [void]$mainPanel.Children.Add($customLabel)
    
    $patternBox = [System.Windows.Controls.TextBox]::new()
    $patternBox.Height = 32
    $patternBox.Margin = [System.Windows.Thickness]::new(0, 0, 0, 8)
    $patternBox.VerticalContentAlignment = 'Center'
    $patternBox.Text = if ($CurrentValue) { $CurrentValue } else { '' }
    Set-TextBoxStyle -TextBox $patternBox
    [void]$mainPanel.Children.Add($patternBox)
    
    # Help text
    $helpText = [System.Windows.Controls.TextBlock]::new()
    $helpText.Text = $exampleText
    $helpText.FontSize = 11
    $helpText.Opacity = 0.7
    $helpText.Margin = [System.Windows.Thickness]::new(0, 0, 0, 16)
    $helpText.Foreground = ConvertTo-UiBrush $colors.WindowFg
    [void]$mainPanel.Children.Add($helpText)
    
    # Syntax reference
    $refLabel = [System.Windows.Controls.TextBlock]::new()
    $refLabel.Text = $syntaxTitle
    $refLabel.FontWeight = 'SemiBold'
    $refLabel.FontSize = 14
    $refLabel.Margin = [System.Windows.Thickness]::new(0, 0, 0, 8)
    $refLabel.Foreground = ConvertTo-UiBrush $colors.WindowFg
    [void]$mainPanel.Children.Add($refLabel)
    
    # Build syntax reference grid
    $refGrid = [System.Windows.Controls.Grid]::new()
    $refGrid.Margin = [System.Windows.Thickness]::new(0, 0, 0, 20)
    $col1 = [System.Windows.Controls.ColumnDefinition]::new()
    $col1.Width = [System.Windows.GridLength]::new(80)
    $col2 = [System.Windows.Controls.ColumnDefinition]::new()
    [void]$refGrid.ColumnDefinitions.Add($col1)
    [void]$refGrid.ColumnDefinitions.Add($col2)
    
    $row = 0
    foreach ($item in $syntaxHelp) {
        $refGrid.RowDefinitions.Add([System.Windows.Controls.RowDefinition]::new())
        
        $symText = [System.Windows.Controls.TextBlock]::new()
        $symText.Text = $item.Symbol
        $symText.FontFamily = [System.Windows.Media.FontFamily]::new('Consolas')
        $symText.FontWeight = 'Bold'
        $symText.Foreground = ConvertTo-UiBrush $colors.Accent
        [System.Windows.Controls.Grid]::SetRow($symText, $row)
        [System.Windows.Controls.Grid]::SetColumn($symText, 0)
        [void]$refGrid.Children.Add($symText)
        
        $descText = [System.Windows.Controls.TextBlock]::new()
        $descText.Text = $item.Desc
        $descText.FontSize = 12
        $descText.Opacity = 0.8
        $descText.Foreground = ConvertTo-UiBrush $colors.WindowFg
        [System.Windows.Controls.Grid]::SetRow($descText, $row)
        [System.Windows.Controls.Grid]::SetColumn($descText, 1)
        [void]$refGrid.Children.Add($descText)
        
        $row++
    }
    [void]$mainPanel.Children.Add($refGrid)
    
    # Buttons panel
    $buttonPanel = [System.Windows.Controls.StackPanel]::new()
    $buttonPanel.Orientation = 'Horizontal'
    $buttonPanel.HorizontalAlignment = 'Right'
    
    # OK button
    $okButton = [System.Windows.Controls.Button]::new()
    $okButton.Content = 'Apply'
    $okButton.Width = 80
    $okButton.Height = 32
    $okButton.Margin = [System.Windows.Thickness]::new(0, 0, 8, 0)
    $okButton.IsDefault = $true
    Set-ButtonStyle -Button $okButton -Accent
    [void]$buttonPanel.Children.Add($okButton)
    
    # Cancel button
    $cancelButton = [System.Windows.Controls.Button]::new()
    $cancelButton.Content = 'Cancel'
    $cancelButton.Width = 80
    $cancelButton.Height = 32
    $cancelButton.IsCancel = $true
    Set-ButtonStyle -Button $cancelButton
    [void]$buttonPanel.Children.Add($cancelButton)
    
    [void]$mainPanel.Children.Add($buttonPanel)
    
    # Add main panel to outer layout
    [void]$outerPanel.Children.Add($mainPanel)
    
    # Wire up preset selection
    $presetsCombo.Add_SelectionChanged({
        param($sender, $eventArgs)
        if ($sender.SelectedIndex -gt 0) {
            $selectedKey = $sender.SelectedItem
            if ($presets.Contains($selectedKey)) {
                $patternBox.Text = $presets[$selectedKey]
            }
        }
    }.GetNewClosure())
    
    # Wire up OK button
    $okButton.Add_Click({
        $result = $patternBox.Text
        $popup.Tag = $result
        $popup.DialogResult = $true
        $popup.Close()
    }.GetNewClosure())
    
    # Wire up Cancel button
    $cancelButton.Add_Click({
        $popup.DialogResult = $false
        $popup.Close()
    }.GetNewClosure())
    
    # Fade-in animation
    Start-UIFadeIn -Window $popup
    
    # Show dialog
    $dialogResult = $popup.ShowDialog()
    
    if ($dialogResult -eq $true) {
        return $popup.Tag
    }
    
    return $null
}