# ==============================================================================
# 514 MSG SCO Administration Tool - Main Controller
# ==============================================================================
# ==============================================================================
# 0. Helper Functions
# ==============================================================================
Function Write-ColorOutput {
    param(
        [string]$Text,
        [string]$Color = "White"
    )
    
    $paragraph = New-Object System.Windows.Documents.Paragraph
    $run = New-Object System.Windows.Documents.Run($Text)
    $run.Foreground = Get-BrushColor -Color $Color
    $paragraph.Inlines.Add($run)
    $TxtOutput.Document.Blocks.Add($paragraph)
}

Function Write-Line {
    param(
        [string]$Text,
        [string]$Color = "White",
        [bool]$Bold = $false
    )
    
    $paragraph = New-Object System.Windows.Documents.Paragraph
    $run = New-Object System.Windows.Documents.Run($Text + "`r`n")
    $run.Foreground = Get-BrushColor -Color $Color
    if ($Bold) { $run.FontWeight = "Bold" }
    $paragraph.Inlines.Add($run)
    $TxtOutput.Document.Blocks.Add($paragraph)
}

Function Write-Command {
    param([string]$CommandText)
    
    $paragraph = New-Object System.Windows.Documents.Paragraph
    
    # Parse the command
    $tokens = $CommandText -split '(?=[-\$])|(?<=[\s])'
    
    foreach ($token in $tokens) {
        if ([string]::IsNullOrWhiteSpace($token)) { continue }
        
        $run = New-Object System.Windows.Documents.Run($token)
        
        # Color based on token type
        if ($token -match "^(Get|Enable|Disable|Reset|New|Remove)-[A-Za-z]+") {
            $run.Foreground = Get-BrushColor -Color "Cyan"
        }
        elseif ($token -match "^-([A-Z][a-zA-Z]+)") {
            $run.Foreground = Get-BrushColor -Color "Yellow"
        }
        elseif ($token -match "'.*'|"".*""") {
            $run.Foreground = Get-BrushColor -Color "DarkOrange"
        }
        elseif ($token -match "^\$") {
            $run.Foreground = Get-BrushColor -Color "Green"
        }
        elseif ($token -match "^(True|False|\$true|\$false)$") {
            $run.Foreground = Get-BrushColor -Color "DarkCyan"
        }
        elseif ($token -match "^\d+$") {
            $run.Foreground = Get-BrushColor -Color "Blue"
        }
        else {
            $run.Foreground = Get-BrushColor -Color "White"
        }
        
        $paragraph.Inlines.Add($run)
    }
    
    $TxtOutput.Document.Blocks.Add($paragraph)
}

Function Write-Result {
    param([string]$ResultText)
    
    $lines = $ResultText -split "`r`n"
    $paragraph = New-Object System.Windows.Documents.Paragraph
    
    foreach ($line in $lines) {
        if ([string]::IsNullOrWhiteSpace($line)) { 
            $paragraph.Inlines.Add((New-Object System.Windows.Documents.Run("`r`n")))
            continue 
        }
        
        $run = New-Object System.Windows.Documents.Run($line + "`r`n")
        
        if ($line -match "^Action:") {
            $run.Foreground = Get-BrushColor -Color "Green"
            $run.FontWeight = "Bold"
        }
        elseif ($line -match "^(IsDisabled|DNSHostName|Class|Description|DistinguishedName|FriendlyName|FriendlyPath|ObjectClass)") {
            $run.Foreground = Get-BrushColor -Color "Cyan"
        }
        elseif ($line -match "^ERROR") {
            $run.Foreground = Get-BrushColor -Color "Red"
            $run.FontWeight = "Bold"
        }
        elseif ($line -match "^WARNING") {
            $run.Foreground = Get-BrushColor -Color "Yellow"
            $run.FontWeight = "Bold"
        }
        elseif ($line -match "successfully") {
            $run.Foreground = Get-BrushColor -Color "Green"
        }
        elseif ($line -match "--- Operation Complete ---") {
            $run.Foreground = Get-BrushColor -Color "Magenta"
            $run.FontWeight = "Bold"
        }
        else {
            $run.Foreground = Get-BrushColor -Color "White"
        }
        
        $paragraph.Inlines.Add($run)
    }
    
    $TxtOutput.Document.Blocks.Add($paragraph)
}

Function Clear-Output {
    $TxtOutput.Document.Blocks.Clear()
}

Function Get-BrushColor {
    param([string]$Color)
    
    switch ($Color) {
        "Red" { return [System.Windows.Media.Brushes]::Red }
        "Green" { return [System.Windows.Media.Brushes]::LimeGreen }
        "Blue" { return [System.Windows.Media.Brushes]::DodgerBlue }
        "Yellow" { return [System.Windows.Media.Brushes]::Yellow }
        "Cyan" { return [System.Windows.Media.Brushes]::Cyan }
        "Magenta" { return [System.Windows.Media.Brushes]::Magenta }
        "DarkCyan" { return [System.Windows.Media.Brushes]::DarkCyan }
        "DarkOrange" { return [System.Windows.Media.Brushes]::DarkOrange }
        "Gray" { return [System.Windows.Media.Brushes]::Gray }
        "White" { return [System.Windows.Media.Brushes]::White }
        default { return [System.Windows.Media.Brushes]::White }
    }
}

Function Test-AFComputerName {
    param([string]$ComputerName)
    
    $prefix = $TxtPrefix.Text.Trim().ToUpper()
    $typeStr = $CmbDeviceType.SelectedItem.Content
    $type = "W"
    if ($typeStr -match "^(W|L|T|V)\s*-") { $type = $matches[1] }
    
    $expectedPattern = "^$prefix$type-"
    
    if ($ComputerName -match $expectedPattern) {
        return $true
    } else {
        return $false
    }
}

Function ConvertTo-AFComputerName {
    param([string]$RawName)
    
    $RawName = $RawName.Trim().ToUpper()
    if ([string]::IsNullOrWhiteSpace($RawName)) { return "" }
    
    $prefix = $TxtPrefix.Text.Trim().ToUpper()
    $typeStr = $CmbDeviceType.SelectedItem.Content
    $type = "W"
    if ($typeStr -match "^(W|L|T|V)\s*-") { $type = $matches[1] }
    $snDigits = [int]$TxtSnDigits.Text
    
    # Extract the serial number from whatever format was entered
    # Look for pattern: ANYTHING-hyphen-SERIAL or just SERIAL
    if ($RawName -match "-(\d+[A-Z0-9]*)$") {
        # Has hyphen, take everything after last hyphen
        $serial = $matches[1]
    } elseif ($RawName -match "^([A-Z]+[WLT]-?)?(\d+[A-Z0-9]*)$") {
        # Extract just the alphanumeric part at the end
        $serial = $matches[2]
    } else {
        # Use the whole thing as serial, stripping non-alphanumeric
        $serial = $RawName -replace '[^A-Z0-9]', ''
    }
    
    # Apply length limit
    if ($serial.Length -gt $snDigits) {
        $serial = $serial.Substring($serial.Length - $snDigits)
    }
    
    $finalName = "$prefix$type-$serial"
    
    # Final length check
    if ($finalName.Length -gt 15) {
        $finalName = $finalName.Substring(0, 15)
    }
    
    return $finalName
}


# 1. Determine the root directory of the application dynamically
$AppRoot = $PSScriptRoot
if (-not $AppRoot) { $AppRoot = Split-Path -Parent $MyInvocation.MyCommand.Path }

# Define subfolder paths
$ScriptsPath = Join-Path $AppRoot "ps1"
$UiPath      = Join-Path $AppRoot "ui"
$ConfigPath  = Join-Path $AppRoot "config"

# 2. Dot-source (load) all PowerShell functions from the .ps1 folder
$psScripts = Get-ChildItem -Path $ScriptsPath -Filter "*.ps1"
foreach ($script in $psScripts) {
    . $script.FullName
}

# 3. Load the Configuration JSON
$configFile = Join-Path $ConfigPath "config.json"
if (Test-Path $configFile) {
    $Global:AppConfig = Get-Content -Path $configFile | ConvertFrom-Json
} else {
    [System.Windows.MessageBox]::Show("Could not find config.json in the config folder!", "Error", "OK", "Error")
    exit
}

# 4. Load the XAML GUI
$xamlFile = Join-Path $UiPath "SCOAdminTool.xaml"
if (-not (Test-Path $xamlFile)) {
    [System.Windows.MessageBox]::Show("Could not find SCOAdminTool.xaml in the ui folder!", "Error", "OK", "Error")
    exit
}

# Use your custom function to build the window and auto-generate control variables
$guiData = New-XamlWindow -XamlFile $xamlFile
$MainWindow = $guiData[0]

# Set the Window Title from the JSON config to prove the config loaded successfully
$MainWindow.Title = $Global:AppConfig.AppTitle

# ==============================================================================
# Initialize UI Defaults from Config
# ==============================================================================
$TxtPrefix.Text = $Global:AppConfig.NamingConvention.BasePrefix
$TxtSnDigits.Text = $Global:AppConfig.NamingConvention.SerialNumberDigits
$TxtOS.Text = $Global:AppConfig.NamingConvention.DefaultOS

# Set default Device Type ComboBox
foreach ($item in $CmbDeviceType.Items) {
    if ($item.Content -match "^$($Global:AppConfig.NamingConvention.DefaultDeviceType)\s*-") {
        $CmbDeviceType.SelectedItem = $item
        break
    }
}

# ==============================================================================
# Auto-Calculated Description Logic
# ==============================================================================
$UpdateDescription = {
    $os    = $TxtOS.Text.Trim()
    $model = $TxtModel.Text.Replace(" ", "")
    $org   = $TxtOrg.Text.Replace(" ", "")
    
    $bldg  = $TxtBldg.Text.Replace(" ", "")
    if ($bldg) { $bldg = "B-$bldg" }
    
    $room  = $TxtRoom.Text.Replace(" ", "")
    if ($room) { $room = "R-$room" }
    
    $ext   = $TxtExt.Text.Replace(" ", "")
    if ($ext) { $ext = "X-$ext" }

    # Array of parts, filter out empty ones, join with comma + space
    $parts = @($os, $model, $org, $bldg, $room, $ext) | Where-Object { $_ -ne "" }
    $TxtDesc.Text = $parts -join ", "
}

# Attach the event to all text boxes so it updates live as you type
$TxtOS.add_TextChanged($UpdateDescription)
$TxtModel.add_TextChanged($UpdateDescription)
$TxtOrg.add_TextChanged($UpdateDescription)
$TxtBldg.add_TextChanged($UpdateDescription)
$TxtRoom.add_TextChanged($UpdateDescription)
$TxtExt.add_TextChanged($UpdateDescription)

# ==============================================================================
# Auto-Formatting Logic (Air Force Standard)
# ==============================================================================
Function ConvertTo-AFComputerName {
    param([string]$RawName)
    
    $RawName = $RawName.Trim().ToUpper()
    if ([string]::IsNullOrWhiteSpace($RawName)) { return "" }

    $prefix = $TxtPrefix.Text.Trim().ToUpper()
    $typeStr = $CmbDeviceType.SelectedItem.Content
    $type = "W" # Fallback
    if ($typeStr -match "^(W|L|T|V)\s*-") { $type = $matches[1] }
    
    if ($RawName.StartsWith($prefix)) {
        $finalName = $RawName
    } else {
        $snDigits = [int]$TxtSnDigits.Text
        if ($RawName.Length -gt $snDigits) {
            $RawName = $RawName.Substring($RawName.Length - $snDigits)
        }
        $finalName = "$prefix$type-$RawName"
    }

    if ($finalName.Length -gt 15) {
        $finalName = $finalName.Substring(0, 15)
    }
    
    return $finalName
}

# Use KeyUp instead of PreviewKeyUp so standard typing is not interrupted
$TxtComputerNames.add_KeyUp({
    param($sender, $e)
    if ($e.Key -eq 'Return' -or $e.Key -eq 'Enter') {
        $lines = $TxtComputerNames.Text -split "`r`n|`n"
        $formattedLines = @()
        $changedLines = @()
        
        foreach ($line in $lines) {
            if ($line.Trim()) {
                $oldName = $line.Trim()
                $newName = ConvertTo-AFComputerName -RawName $oldName
                $formattedLines += $newName
                
                if ($oldName -ne $newName) {
                    $changedLines += "'$oldName' -> '$newName'"
                }
            }
        }
        
        $filteredLines = $formattedLines | Where-Object { $_ -ne "" }
        $newText = ($filteredLines -join "`r`n") + "`r`n"
        
        if ($TxtComputerNames.Text -ne $newText) {
            $TxtComputerNames.Text = $newText
            $TxtComputerNames.CaretIndex = $TxtComputerNames.Text.Length
            
            # Show what changed
            if ($changedLines.Count -gt 0) {
                $TxtOutput.Text += "Formatted computer names:`r`n"
                foreach ($change in $changedLines) {
                    $TxtOutput.Text += "  $change`r`n"
                }
                $TxtOutput.Text += "`r`n"
            }
        }
    }
})

# ==============================================================================
# Import File Logic
# ==============================================================================
$BtnImport.add_Click({
    Add-Type -AssemblyName System.Windows.Forms
    $openFileDialog = New-Object System.Windows.Forms.OpenFileDialog
    $openFileDialog.Filter = "Supported Files (*.txt;*.csv;*.xlsx)|*.txt;*.csv;*.xlsx"
    $openFileDialog.Title = "Select a file containing computer names/serial numbers"
    
    if ($openFileDialog.ShowDialog() -eq 'OK') {
        $file = $openFileDialog.FileName
        $ext = [System.IO.Path]::GetExtension($file).ToLower()
        $importedNames = @()
        
        Write-Line "Importing from $file ..." -Color "Cyan"
        [System.Windows.Forms.Application]::DoEvents()
        
        try {
            # Define expected column names in order of preference
            $preferredColumns = @("computer name", "name", "serial number", "sn")
            
            if ($ext -eq '.txt') {
                # For text files, read all non-empty lines
                $allLines = Get-Content $file | Where-Object { $_.Trim() -ne '' }
                
                # Check if first line looks like a header (contains common column names)
                $firstLine = $allLines[0].ToLower()
                $isHeader = $false
                foreach ($col in $preferredColumns) {
                    if ($firstLine -match $col) {
                        $isHeader = $true
                        break
                    }
                }
                
                # If header exists, skip it; otherwise use all lines
                if ($isHeader -and $allLines.Count -gt 1) {
                    $importedNames = $allLines[1..$($allLines.Count-1)]
                    Write-Line "Detected header row in text file, skipping first line." -Color "Yellow"
                } else {
                    $importedNames = $allLines
                }
            } 
            elseif ($ext -eq '.csv') {
                $csv = Import-Csv $file
                
                # Find which column to use based on preferred columns
                $columnNames = $csv[0].PSObject.Properties.Name
                $selectedColumn = $null
                
                foreach ($pref in $preferredColumns) {
                    $selectedColumn = $columnNames | Where-Object { $_ -like "*$pref*" } | Select-Object -First 1
                    if ($selectedColumn) {
                        Write-Line "Found column '$selectedColumn' matching preferred name '$pref'" -Color "Green"
                        break
                    }
                }
                
                if ($selectedColumn) {
                    $importedNames = $csv.$selectedColumn | Where-Object { $_ -and $_.ToString().Trim() -ne '' }
                } else {
                    #$TxtOutput.Text += "ERROR: Could not find expected column names.`r`n"
                    #$TxtOutput.Text += "Expected columns (case-insensitive, partial match): $($preferredColumns -join ', ')`r`n"
                    #$TxtOutput.Text += "Found columns: $($columnNames -join ', ')`r`n"
                    #$TxtOutput.Text += "Please ensure your file has one of these column headers.`r`n"
                    Write-Line "ERROR: Could not find expected column names." -Color "Red" -Bold $true
                    Write-Line "Expected columns (case-insensitive, partial match): $($preferredColumns -join ', ')" -Color "Yellow"
                    Write-Line "Found columns: $($columnNames -join ', ')" -Color "Yellow"
                    Write-Line "Please ensure your file has one of these column headers." -Color "Yellow"
                    $BtnExecute.IsEnabled = $true
                    return
                }
            } 
            elseif ($ext -eq '.xlsx') {
                $xlsx = Import-Excel $file
                
                # Find which column to use based on preferred columns
                $columnNames = $xlsx[0].PSObject.Properties.Name
                $selectedColumn = $null
                
                foreach ($pref in $preferredColumns) {
                    $selectedColumn = $columnNames | Where-Object { $_ -like "*$pref*" } | Select-Object -First 1
                    if ($selectedColumn) {
                        $TxtOutput.Text += "Found column '$selectedColumn' matching preferred name '$pref'`r`n"
                        break
                    }
                }
                
                if ($selectedColumn) {
                    $importedNames = $xlsx.$selectedColumn | Where-Object { $_ -and $_.ToString().Trim() -ne '' }
                } else {
                    $TxtOutput.Text += "ERROR: Could not find expected column names.`r`n"
                    $TxtOutput.Text += "Expected columns (case-insensitive, partial match): $($preferredColumns -join ', ')`r`n"
                    $TxtOutput.Text += "Found columns: $($columnNames -join ', ')`r`n"
                    $TxtOutput.Text += "Please ensure your file has one of these column headers.`r`n"
                    $BtnExecute.IsEnabled = $true
                    return
                }
            }

            # Validate we actually got data
            if ($importedNames.Count -eq 0) {
                Write-Line "WARNING: No data found in the imported file." -Color "Yellow" -Bold $true
                return
            }

            # Get current lines that are not empty
            $existingLines = @()
            if ($TxtComputerNames.Text.Trim()) {
                $existingLines = $TxtComputerNames.Text -split "`r`n|`n" | Where-Object { $_.Trim() -ne '' }
            }
            
            # Format each imported name and add to collection
            $formattedNames = @()
            $skippedNames = @()
            
            foreach ($name in $importedNames) {
                if (-not [string]::IsNullOrWhiteSpace($name)) {
                    $formattedName = ConvertTo-AFComputerName -RawName $name.ToString()
                    if ($formattedName) {
                        $formattedNames += $formattedName
                    } else {
                        $skippedNames += $name
                    }
                }
            }
            
            # Report any skipped names
            if ($skippedNames.Count -gt 0) {
                Write-Line "WARNING: Skipped $($skippedNames.Count) invalid entries: $($skippedNames -join ', ')" -Color "Yellow"
            }
            
            # Combine existing and new names, remove duplicates
            $allNames = $existingLines + $formattedNames
            $uniqueNames = $allNames | Select-Object -Unique
            
            # Build the new text with each name on its own line
            $newText = ""
            for ($i = 0; $i -lt $uniqueNames.Count; $i++) {
                $newText += $uniqueNames[$i]
                if ($i -lt $uniqueNames.Count - 1) {
                    $newText += "`r`n"
                }
            }
            
            # Add a final newline if there are any names
            if ($uniqueNames.Count -gt 0) {
                $newText += "`r`n"
            }
            
            $TxtComputerNames.Text = $newText
            Write-Line "Successfully imported and formatted $($formattedNames.Count) computer names." -Color "Green"
            $TxtOutput.Text += "Successfully imported and formatted $($formattedNames.Count) computer names.`r`n"
            if ($skippedNames.Count -gt 0) {
                Write-Line "Skipped $($skippedNames.Count) invalid entries." -Color "Yellow"
            }
            Write-Line "Total unique computer names: $($uniqueNames.Count)" -Color "Cyan"
            
        } catch {
            Write-Line "ERROR importing file: $($_.Exception.Message)" -Color "Red" -Bold $true
            Write-Line "Please check the file format and try again." -Color "Yellow"
        }
    }
})

# ==============================================================================
# 5. UI Event Wiring (Interactive Logic)
# ==============================================================================

# Logic to Enable/Disable the "New Computer Details" group box
$RadNew.add_Checked({
    $GrpNewDetails.IsEnabled = $true
})
$RadNew.add_Unchecked({
    $GrpNewDetails.IsEnabled = $false
})

# Logic to Enable/Disable the "Recycle Bin" checkbox for Remove Computer
$RadRemove.add_Checked({
    $ChkRecycleBin.IsEnabled = $true
})
$RadRemove.add_Unchecked({
    $ChkRecycleBin.IsEnabled = $false
    $ChkRecycleBin.IsChecked = $false
})

# Close the window cleanly if needed (optional hook)
$MainWindow.add_Closed({
    # Add any cleanup tasks here if needed when the app closes
})

# ==============================================================================
# 6. Execute Button Logic
# ==============================================================================
try {
    if ($RadGet.IsChecked) {
        Write-Result "Action: Get Computer Info for $compString"
        if ($isDevMode) {
            Write-Command "Get-Computer -ComputerName $compString -DraServer $draServer"
        } else {
            $results = Get-Computer -ComputerName $targetComputers -DraServer $draServer -ErrorAction Stop | 
                Select-Object IsDisabled, DNSHostName, Class, Description, DistinguishedName, FriendlyName, FriendlyPath, IsManaged, ObjectClass, AdditionalAttributes | 
                Format-List | 
                Out-String
            Write-Result $results
        }
    }
    elseif ($RadEnable.IsChecked) {
        Write-Result "Action: Enable Computer for $compString"
        if ($isDevMode) {
            Write-Command "Enable-Computer -ComputerName $compString -DraServer $draServer"
        } else {
            Enable-Computer -ComputerName $targetComputers -DraServer $draServer -ErrorAction Stop
            Write-Line "Enable command sent successfully." -Color "Green"
        }
    }
    elseif ($RadDisable.IsChecked) {
        Write-Result "Action: Disable Computer for $compString"
        if ($isDevMode) {
            Write-Command "Disable-Computer -ComputerName $compString -DraServer $draServer"
        } else {
            Disable-Computer -ComputerName $targetComputers -DraServer $draServer -ErrorAction Stop
            Write-Line "Disable command sent successfully." -Color "Green"
        }
    }
    elseif ($RadReset.IsChecked) {
        Write-Result "Action: Reset Computer for $compString"
        if ($isDevMode) {
            Write-Command "Reset-Computer -ComputerName $compString -DraServer $draServer"
        } else {
            Reset-Computer -ComputerName $targetComputers -DraServer $draServer -ErrorAction Stop
            Write-Line "Reset command sent successfully." -Color "Green"
        }
    }
    elseif ($RadNew.IsChecked) {
        Write-Result "Action: Create New Computer for $compString"
        $org = $TxtOrg.Text
        $bldg = $TxtBldg.Text
        $room = $TxtRoom.Text
        $desc = $TxtDesc.Text
        $sg = $Global:AppConfig.ActiveDirectory.DefaultSecurityGroup

        if ([string]::IsNullOrWhiteSpace($org) -or [string]::IsNullOrWhiteSpace($bldg) -or [string]::IsNullOrWhiteSpace($room)) {
            Write-Line "ERROR: Organization, Building, and Room are required for new computers." -Color "Red" -Bold $true
        } else {
            if ($isDevMode) {
                Write-Command "New-Computer -ComputerName $compString -Organization '$org' -BuildingNum '$bldg' -RoomNum '$room' -Description '$desc' -DraServer '$draServer' -SecurityGroup '$sg'"
            } else {
                New-Computer -ComputerName $targetComputers -Organization $org -BuildingNum $bldg -RoomNum $room -Description $desc -DraServer $draServer -SecurityGroup $sg -ErrorAction Stop
                Write-Line "Create New Computer command sent successfully." -Color "Green"
            }
        }
    }
    elseif ($RadRemove.IsChecked) {
        Write-Result "Action: Remove Computer for $compString"
        
        if ($isDevMode) {
            if ($ChkRecycleBin.IsChecked) {
                Write-Command "Remove-Computer -ComputerName $compString -RecycleBin -DraServer $draServer"
            } else {
                Write-Command "Remove-Computer -ComputerName $compString -DraServer $draServer"
            }
        } else {
            if ($ChkRecycleBin.IsChecked) { 
                Remove-Computer -ComputerName $targetComputers -RecycleBin -DraServer $draServer -ErrorAction Stop
                Write-Line "Remove command (including Recycle Bin) sent successfully." -Color "Green"
            } else {
                Remove-Computer -ComputerName $targetComputers -DraServer $draServer -ErrorAction Stop
                Write-Line "Remove command sent successfully." -Color "Green"
            }
        }
    }
} catch {
    Write-Line "AN ERROR OCCURRED:" -Color "Red" -Bold $true
    Write-Line $_.Exception.Message -Color "Red"
}

Write-Line "--- Operation Complete ---" -Color "Magenta" -Bold $true

# ==============================================================================
# 7. Launch the Application
# ==============================================================================
$MainWindow.ShowDialog() | Out-Null
