Public/Show-LogAnalyzerUI.ps1

function Show-LogAnalyzerUI {
    Set-StrictMode -Version Latest
    Write-Host "[DEBUG] Starting Show-LogAnalyzerUI"

    # Check if running on Windows - compatible with both PowerShell 5.1 and 7+
    $isWindowsOS = if ($PSVersionTable.PSVersion.Major -ge 6) {
        $IsWindows
    } else {
        $env:OS -eq 'Windows_NT'
    }
    
    if (-not $isWindowsOS) {
        throw "[ERROR] The Smart Log Analyzer UI is only supported on Windows."
    }

    try {
        Add-Type -AssemblyName System.Windows.Forms
        Add-Type -AssemblyName System.Drawing
    } catch {
        throw "❌ Failed to load Windows Forms: $($_.Exception.Message)"
    }

    try {
        Import-Module -Name SmartLogAnalyzer -ErrorAction Stop -Verbose:$false
    } catch {
        throw "❌ Failed to import SmartLogAnalyzer module: $($_.Exception.Message)"
    }

    # --- Helper UI Creator Functions ---
    function New-Label ($text, $x, $y, $width = 100, $height = 20) {
        $lbl = New-Object Windows.Forms.Label
        $lbl.Text = $text
        $lbl.Location = New-Object Drawing.Point($x, $y)
        $lbl.Size = New-Object Drawing.Size($width, $height)
        return $lbl
    }

    function New-Button ($text, $x, $y, $width = 100, $height = 30) {
        $btn = New-Object Windows.Forms.Button
        $btn.Text = $text
        $btn.Location = New-Object Drawing.Point($x, $y)
        $btn.Size = New-Object Drawing.Size($width, $height)
        return $btn
    }

    function Export-LogDataToFile {
        param (
            [string]$Format,
            [scriptblock]$ExportBlock,
            [string]$Filter,
            [string]$Extension
        )
        if (-not $script:logData -or $script:logData.Count -eq 0) {
            [Windows.Forms.MessageBox]::Show("No log data to export.", "Information", 'OK', 'Information')
            return
        }
        $save = New-Object Windows.Forms.SaveFileDialog
        $save.Filter = $Filter
        $save.FileName = "LogExport_$(Get-Date -Format 'yyyyMMdd_HHmmss').$Extension"
        if ($save.ShowDialog() -eq [Windows.Forms.DialogResult]::OK) {
            try {
                & $ExportBlock $save.FileName
                [Windows.Forms.MessageBox]::Show("Exported to $Format successfully.", "Export Complete", 'OK', 'Information')
            } catch {
                [Windows.Forms.MessageBox]::Show("Export failed: $($_.Exception.Message)", "Export Error", 'OK', 'Error')
            }
        }
    }

    # --- Initialize Script Variables ---
    $script:logData = @()
    $script:rawLogEntries = @()

    # --- Form Setup ---
    $form = New-Object Windows.Forms.Form
    $form.Text = "Smart Log Analyzer"
    $form.Size = New-Object Drawing.Size(1000, 820)
    $form.StartPosition = "CenterScreen"
    $form.MinimumSize = New-Object Drawing.Size(800, 600)
    $form.FormBorderStyle = [Windows.Forms.FormBorderStyle]::Sizable

    # --- Controls ---
    $lblLogType = New-Label "Log Type:" 10 10 60
    $cmbLogType = New-Object Windows.Forms.ComboBox
    $cmbLogType.Location = New-Object Drawing.Point(75, 10)
    $cmbLogType.Size = New-Object Drawing.Size(120, 20)
    $cmbLogType.DropDownStyle = 'DropDownList'
    $cmbLogType.Items.AddRange(@("System", "Application", "Security", "All"))
    $cmbLogType.SelectedIndex = 0

    $lblStartTime = New-Label "Start Time:" 210 10 70
    $dtStart = New-Object Windows.Forms.DateTimePicker
    $dtStart.Format = 'Custom'
    $dtStart.CustomFormat = "yyyy-MM-dd HH:mm"
    $dtStart.Location = New-Object Drawing.Point(280, 10)
    $dtStart.Size = New-Object Drawing.Size(140, 20)
    $dtStart.Value = (Get-Date).AddHours(-1)

    $lblEndTime = New-Label "End Time:" 430 10 65
    $dtEnd = New-Object Windows.Forms.DateTimePicker
    $dtEnd.Format = 'Custom'
    $dtEnd.CustomFormat = "yyyy-MM-dd HH:mm"
    $dtEnd.Location = New-Object Drawing.Point(500, 10)
    $dtEnd.Size = New-Object Drawing.Size(140, 20)
    $dtEnd.Value = Get-Date

    $chkRedact = New-Object Windows.Forms.CheckBox
    $chkRedact.Text = "Redact Sensitive Data"
    $chkRedact.Location = New-Object Drawing.Point(10, 40)
    $chkRedact.Size = New-Object Drawing.Size(180, 20)

    $chkRedactLog = New-Object Windows.Forms.CheckBox
    $chkRedactLog.Text = "Generate Redaction Log"
    $chkRedactLog.Location = New-Object Drawing.Point(200, 40)
    $chkRedactLog.Size = New-Object Drawing.Size(180, 20)
    
    $chkAttentionOnly = New-Object Windows.Forms.CheckBox
    $chkAttentionOnly.Text = "Show Critical Events Only"
    $chkAttentionOnly.Location = New-Object Drawing.Point(10, 65)
    $chkAttentionOnly.Size = New-Object Drawing.Size(180, 20)
    $chkAttentionOnly.Checked = $false  # Default to showing all events

    $btnFetch = New-Button "Fetch & Analyze Logs" 400 65 150
    $btnExportCSV = New-Button "Export to CSV" 560 40 80
    $btnExportJSON = New-Button "Export to JSON" 650 40 80
    $btnExportReport = New-Button "Export Log Report" 740 40 110

    $grid = New-Object Windows.Forms.DataGridView
    $grid.Location = New-Object Drawing.Point(10, 100)
    $grid.Size = New-Object Drawing.Size(960, 580)
    $grid.Anchor = 'Top,Left,Right,Bottom'
    $grid.ReadOnly = $true
    $grid.AutoGenerateColumns = $true  # Let it auto-generate initially
    $grid.AutoSizeColumnsMode = 'Fill'
    $grid.AllowUserToAddRows = $false
    $grid.AllowUserToDeleteRows = $false
    $grid.SelectionMode = 'FullRowSelect'
    $grid.MultiSelect = $false

    $lblSummary = New-Label "Summary will appear here..." 10 690 960 60
    $lblSummary.AutoSize = $false
    $lblSummary.Anchor = 'Left,Right,Bottom'

    # --- Event Handlers ---
    $cellFormatEvent = Register-ObjectEvent -InputObject $grid -EventName CellFormatting -Action {
        try {
            if ($eventArgs.RowIndex -ge 0 -and $eventArgs.ColumnIndex -ge 0 -and $this.Rows -and $this.Rows.Count -gt $eventArgs.RowIndex) {
                $row = $this.Rows[$eventArgs.RowIndex]
                # Check if we have the Level column (our transformed column name)
                if ($row.Cells -and $row.Cells.Count -gt 1) {
                    $levelCell = $null
                    # Try to find the level cell by column name
                    foreach ($column in $this.Columns) {
                        if ($column.Name -eq 'Level' -and $row.Cells[$column.Index]) {
                            $levelCell = $row.Cells[$column.Index]
                            break
                        }
                    }
                    
                    if ($levelCell -and $levelCell.Value) {
                        $level = $levelCell.Value.ToString()
                        $eventArgs.CellStyle.BackColor = switch ($level) {
                            'Error'       { [Drawing.Color]::LightCoral }
                            'Warning'     { [Drawing.Color]::Khaki }
                            'Information' { [Drawing.Color]::LightGreen }
                            'Info'        { [Drawing.Color]::LightGreen }
                            default       { [Drawing.Color]::White }
                        }
                    }
                }
            }
        } catch {
            # Suppress CellFormatting errors to avoid spam
        }
    }

    # Double-click event to show full message details
    $grid.Add_CellDoubleClick({
        param($sender, $e)
        try {
            if ($e.RowIndex -ge 0 -and $e.RowIndex -lt $script:rawLogEntries.Count) {
                $selectedEntry = $script:rawLogEntries[$e.RowIndex]
                
                # Create a detail dialog
                $detailForm = New-Object Windows.Forms.Form
                $detailForm.Text = "Log Entry Details"
                $detailForm.Size = New-Object Drawing.Size(800, 500)
                $detailForm.StartPosition = "CenterParent"
                $detailForm.FormBorderStyle = [Windows.Forms.FormBorderStyle]::Sizable
                $detailForm.MinimumSize = New-Object Drawing.Size(600, 400)
                
                # Create a textbox for the full message
                $txtDetails = New-Object Windows.Forms.TextBox
                $txtDetails.Location = New-Object Drawing.Point(10, 10)
                $txtDetails.Size = New-Object Drawing.Size(760, 420)
                $txtDetails.Anchor = 'Top,Left,Right,Bottom'
                $txtDetails.Multiline = $true
                $txtDetails.ReadOnly = $true
                $txtDetails.ScrollBars = 'Both'
                $txtDetails.WordWrap = $true
                $txtDetails.Font = New-Object Drawing.Font("Consolas", 10)
                
                # Format the details
                $detailText = @"
Timestamp: $($selectedEntry.TimeCreated)
Level: $($selectedEntry.LevelDisplayName)
Provider: $($selectedEntry.ProviderName)
Event ID: $($selectedEntry.EventId)
 
=== Full Message ===
$($selectedEntry.Message)
 
"@

                
                # Add raw message if available and different
                if ($selectedEntry.PSObject.Properties['RawMessage'] -and $selectedEntry.RawMessage -and $selectedEntry.RawMessage -ne $selectedEntry.Message) {
                    $detailText += "`n=== Raw XML Message ===`n$($selectedEntry.RawMessage)"
                }
                
                $txtDetails.Text = $detailText
                
                # Add close button
                $btnClose = New-Object Windows.Forms.Button
                $btnClose.Text = "Close"
                $btnClose.Location = New-Object Drawing.Point(695, 440)
                $btnClose.Size = New-Object Drawing.Size(75, 25)
                $btnClose.Anchor = 'Bottom,Right'
                $btnClose.Add_Click({ $detailForm.Close() })
                
                $detailForm.Controls.AddRange(@($txtDetails, $btnClose))
                $detailForm.Add_KeyDown({
                    if ($_.KeyCode -eq 'Escape') {
                        $detailForm.Close()
                    }
                })
                
                [void]$detailForm.ShowDialog($form)
                $detailForm.Dispose()
            }
        } catch {
            [Windows.Forms.MessageBox]::Show("Error showing details: $($_.Exception.Message)", "Error", 'OK', 'Error')
        }
    })

    $btnFetch.Add_Click({
        try {
            Write-Host "[DEBUG] Starting log fetch with LogType: $($cmbLogType.SelectedItem)"
            
            # Clear previous data
            $script:logData = @()
            $script:rawLogEntries = @()
            $grid.DataSource = $null
            $grid.Columns.Clear()
            $lblSummary.Text = "Fetching logs..."
            
            $params = @{
                FetchLogs = $true
                LogType = $cmbLogType.SelectedItem.ToString()
                StartTime = $dtStart.Value
                EndTime = $dtEnd.Value
                AttentionOnly = $chkAttentionOnly.Checked
                Verbose = $true
            }
            if ($chkRedact.Checked) { $params.RedactSensitiveData = $true }
            if ($chkRedactLog.Checked) { $params.GenerateRedactionLog = $true }

            Write-Host "[DEBUG] Calling Invoke-SmartAnalyzer with params: $($params | ConvertTo-Json -Compress)"
            $result = Invoke-SmartAnalyzer @params -ErrorAction Stop
            
            if (-not $result -or -not $result.Entries) {
                Write-Warning "[DEBUG] No result or entries returned from Invoke-SmartAnalyzer"
                $lblSummary.Text = "No log entries found for the specified criteria."
                [Windows.Forms.MessageBox]::Show("No log entries found for the specified criteria. This could be due to:`n`n- Time range with no matching events`n- Security log access denied (requires Administrator)`n- Empty log files`n`nTry adjusting the time range or running as Administrator.", "No Data Found", 'OK', 'Information')
                return
            }

            $script:rawLogEntries = $result.Entries
            Write-Host "[DEBUG] Retrieved $($script:rawLogEntries.Count) raw log entries"

            if ($script:rawLogEntries.Count -eq 0) {
                $lblSummary.Text = "No log entries found for the specified criteria."
                [Windows.Forms.MessageBox]::Show("No log entries found for the specified criteria. This could be due to:`n`n- Time range with no matching events`n- Security log access denied (requires Administrator)`n- No matching events with AttentionOnly filter`n`nTry adjusting the time range, unchecking filters, or running as Administrator.", "No Data Found", 'OK', 'Information')
                return
            }

            # Transform data for the grid, handling potential null values
            $script:logData = $script:rawLogEntries | ForEach-Object {
                [pscustomobject]@{
                    Timestamp = if ($_.TimeCreated) { $_.TimeCreated.ToString("yyyy-MM-dd HH:mm:ss") } else { "Unknown" }
                    Level     = if ($_.LevelDisplayName) { $_.LevelDisplayName } else { "Unknown" }
                    Provider  = if ($_.ProviderName) { $_.ProviderName } else { "Unknown" }
                    EventId   = if ($_.EventId) { $_.EventId } else { "N/A" }
                    Message   = if ($_.Message) { $_.Message.Substring(0, [Math]::Min($_.Message.Length, 500)) } else { "No message" }
                }
            }
            
            Write-Host "[DEBUG] Transformed $($script:logData.Count) entries for display"

            # Convert to DataTable for proper DataGridView binding
            Write-Host "[DEBUG] Sample data for binding:" ($script:logData | Select-Object -First 1 | ConvertTo-Json -Compress)
            
            # Create DataTable
            $dataTable = New-Object System.Data.DataTable
            $dataTable.Columns.Add("Timestamp", [string]) | Out-Null
            $dataTable.Columns.Add("Level", [string]) | Out-Null
            $dataTable.Columns.Add("Provider", [string]) | Out-Null
            $dataTable.Columns.Add("EventId", [string]) | Out-Null
            $dataTable.Columns.Add("Message", [string]) | Out-Null
            
            # Populate DataTable
            foreach ($entry in $script:logData) {
                $row = $dataTable.NewRow()
                $row["Timestamp"] = $entry.Timestamp
                $row["Level"] = $entry.Level
                $row["Provider"] = $entry.Provider
                $row["EventId"] = $entry.EventId
                $row["Message"] = $entry.Message
                $dataTable.Rows.Add($row)
            }
            
            # Bind DataTable to grid
            $grid.AutoGenerateColumns = $true
            $grid.DataSource = $dataTable
            
            # Adjust column widths
            if ($grid.ColumnCount -gt 0) {
                $grid.Columns["Timestamp"].Width = 130
                $grid.Columns["Level"].Width = 90
                $grid.Columns["Provider"].Width = 200
                $grid.Columns["EventId"].Width = 80
                $grid.Columns["Message"].AutoSizeMode = 'Fill'
            }
            
            Write-Host "[DEBUG] Grid now has $($grid.RowCount) rows and $($grid.ColumnCount) columns"

            # Generate summary
            Write-Host "[DEBUG] Generating summary for $($result.Entries.Count) entries"
            $summary = Get-LogSummary -LogLines $result.Entries -ErrorAction Stop
            
            if ($summary) {
                $firstTs = if ($summary.FirstTimestamp) { $summary.FirstTimestamp.ToString("yyyy-MM-dd HH:mm:ss") } else { "Unknown" }
                $lastTs = if ($summary.LastTimestamp) { $summary.LastTimestamp.ToString("yyyy-MM-dd HH:mm:ss") } else { "Unknown" }
                $lblSummary.Text = "Total: $($summary.TotalLines) | Errors: $($summary.ErrorCount) | Warnings: $($summary.WarningCount) | Info: $($summary.InfoCount) | Other: $($summary.OtherCount)`nFrom: $firstTs To: $lastTs"
            } else {
                $lblSummary.Text = "Summary generation failed"
            }
            
            Write-Host "[DEBUG] Log fetch completed successfully"
        } catch {
            Write-Host "[DEBUG] Error in btnFetch.Add_Click: $($_.Exception.Message)"
            Write-Host "[DEBUG] Stack trace: $($_.ScriptStackTrace)"
            $lblSummary.Text = "Error occurred while fetching logs."
            [Windows.Forms.MessageBox]::Show("Failed to fetch logs:`n`n$($_.Exception.Message)`n`nIf accessing Security logs, try running PowerShell as Administrator.", "Error", 'OK', 'Error')
        }
    })

    $btnExportCSV.Add_Click({
        Export-LogDataToFile -Format "CSV" -Filter "CSV Files (*.csv)|*.csv" -Extension "csv" -ExportBlock {
            param($file) $script:logData | Export-Csv $file -NoTypeInformation -ErrorAction Stop
        }
    })

    $btnExportJSON.Add_Click({
        Export-LogDataToFile -Format "JSON" -Filter "JSON Files (*.json)|*.json" -Extension "json" -ExportBlock {
            param($file) $script:logData | ConvertTo-Json -Depth 5 | Set-Content $file -ErrorAction Stop
        }
    })

    $btnExportReport.Add_Click({
        if (-not $script:rawLogEntries -or $script:rawLogEntries.Count -eq 0) {
            [Windows.Forms.MessageBox]::Show("No log data to export as report.", "Information", 'OK', 'Information')
            return
        }
        $save = New-Object Windows.Forms.SaveFileDialog
        $save.Filter = "Text Files (*.txt)|*.txt|All Files (*.*)|*.*"
        $save.FileName = "LogReport_$(Get-Date -Format 'yyyyMMdd_HHmmss').txt"
        if ($save.ShowDialog() -eq [Windows.Forms.DialogResult]::OK) {
            try {
                Export-LogReport -LogLines $script:rawLogEntries -OutputPath $save.FileName -ErrorAction Stop
                [Windows.Forms.MessageBox]::Show("Log Report exported successfully.", "Export Complete", 'OK', 'Information')
            } catch {
                [Windows.Forms.MessageBox]::Show("Failed to export log report.`nError: $($_.Exception.Message)", "Export Error", 'OK', 'Error')
            }
        }
    })

    # Form resize handler for proper control positioning
    $form.Add_Resize({
        try {
            $formWidth = $form.ClientSize.Width
            $formHeight = $form.ClientSize.Height
            
            # Adjust grid size and summary position
            $grid.Size = New-Object Drawing.Size(($formWidth - 20), ($formHeight - 170))
            $lblSummary.Location = New-Object Drawing.Point(10, ($formHeight - 80))
            $lblSummary.Size = New-Object Drawing.Size(($formWidth - 20), 60)
        } catch {
            # Suppress resize errors
        }
    })

    $form.Controls.AddRange(@(
        $lblLogType, $cmbLogType,
        $lblStartTime, $dtStart,
        $lblEndTime, $dtEnd,
        $chkRedact, $chkRedactLog, $chkAttentionOnly,
        $btnFetch, $btnExportCSV, $btnExportJSON, $btnExportReport,
        $grid, $lblSummary
    ))

    Write-Host "[DEBUG] Displaying form"
    [void]$form.ShowDialog()
    $form.Dispose()

    Unregister-Event -SourceIdentifier $cellFormatEvent.Name
    Remove-Job -Id $cellFormatEvent.Id -Force

    Write-Host "[DEBUG] Show-LogAnalyzerUI finished"
    # exit gracefully without relying on $LASTEXITCODE
}