# =============================================================================
# POWERSHELL PROFILE CONFIGURATION
# =============================================================================
# Author: Peter Wood <peter@peterwood.dev>
# Last Updated: June 17, 2025
# Description: Comprehensive PowerShell profile with enhanced functionality
# 
# Features:
# - Automatic module installation and import with error handling
# - oh-my-posh prompt theming
# - PSFzf fuzzy search integration
# - Unix-like command aliases (grep, which, head, tail, etc.)
# - Fabric AI pattern integration for text processing
# - Network and system utilities
# - File system helpers
# - PowerShell and package management tools
# - VS Code profile synchronization
#
# Usage:
# - This profile loads automatically when starting PowerShell
# - Use 'syncvscode' to sync with VS Code terminal
# - Use 'Update-Profile' to reload after making changes
# - All functions include help documentation accessible via Get-Help
# =============================================================================

# Install missing modules
Write-Host "🔍 Checking for required PowerShell modules..." -ForegroundColor Cyan

if (-not (Get-Module -ListAvailable -Name Terminal-Icons)) {
  Write-Host "📦 Installing Terminal-Icons module..." -ForegroundColor Yellow
  try {
    Install-Module -Name Terminal-Icons -Scope CurrentUser -Force
    Write-Host "✅ Terminal-Icons installed successfully" -ForegroundColor Green
  }
  catch {
    Write-Error "❌ Failed to install Terminal-Icons: $($_.Exception.Message)"
  }
}

if (-not (Get-Module -ListAvailable -Name PSReadLine)) {
  Write-Host "📦 Installing PSReadLine module..." -ForegroundColor Yellow
  try {
    Install-Module -Name PSReadLine -Scope CurrentUser -Force
    Write-Host "✅ PSReadLine installed successfully" -ForegroundColor Green
  }
  catch {
    Write-Error "❌ Failed to install PSReadLine: $($_.Exception.Message)"
  }
}

if (-not (Get-Module -ListAvailable -Name PSScriptAnalyzer)) {
  Write-Host "📦 Installing PSScriptAnalyzer module..." -ForegroundColor Yellow
  try {
    Install-Module -Name PSScriptAnalyzer -Force -Scope CurrentUser
    Write-Host "✅ PSScriptAnalyzer installed successfully" -ForegroundColor Green
  }
  catch {
    Write-Error "❌ Failed to install PSScriptAnalyzer: $($_.Exception.Message)"
  }
}

if (-not (Get-Module -ListAvailable -Name PSFzf)) {
  Write-Host "📦 Installing PSFzf module..." -ForegroundColor Yellow
  try {
    Install-Module -Name PSFzf -Scope CurrentUser -Force
    Write-Host "✅ PSFzf installed successfully" -ForegroundColor Green
  }
  catch {
    Write-Error "❌ Failed to install PSFzf: $($_.Exception.Message)"
  }
}

# Import modules
Write-Host "📂 Importing PowerShell modules..." -ForegroundColor Cyan

try {
  Import-Module -Name Terminal-Icons -ErrorAction Stop
  Write-Host "✅ Terminal-Icons imported successfully" -ForegroundColor Green
}
catch {
  Write-Warning "⚠️  Failed to import Terminal-Icons: $($_.Exception.Message)"
}

# Import PSReadLine with better version conflict handling
if (Get-Module -Name PSReadLine) {
  # PSReadLine is already loaded, don't try to reimport
  Write-Host "✅ PSReadLine already loaded" -ForegroundColor Green
}
else {
  try {
    # Try to import the latest available version without forcing
    Import-Module -Name PSReadLine -ErrorAction Stop
    Write-Host "✅ PSReadLine imported successfully" -ForegroundColor Green
  }
  catch {
    Write-Warning "PSReadLine import failed: $($_.Exception.Message)"
    Write-Host "ℹ️  Using built-in PSReadLine features" -ForegroundColor Cyan
  }
}

# Add fzf to PATH if not already there
Write-Host "🔍 Checking fzf installation..." -ForegroundColor Cyan
$fzfPath = "$env:LOCALAPPDATA\Microsoft\WinGet\Packages\junegunn.fzf_Microsoft.Winget.Source_8wekyb3d8bbwe"
if ((Test-Path "$fzfPath\fzf.exe") -and ($env:PATH -notlike "*$fzfPath*")) {
  $env:PATH += ";$fzfPath"
  Write-Host "✅ Added fzf to PATH: $fzfPath" -ForegroundColor Green
}

# Also check the WinGet Links directory
$wingetLinks = "$env:LOCALAPPDATA\Microsoft\WinGet\Links"
if ((Test-Path "$wingetLinks\fzf.exe") -and ($env:PATH -notlike "*$wingetLinks*")) {
  $env:PATH += ";$wingetLinks"
  Write-Host "✅ Added WinGet Links to PATH: $wingetLinks" -ForegroundColor Green
}

# Initialize oh-my-posh prompt
Write-Host "🎨 Initializing oh-my-posh prompt..." -ForegroundColor Cyan
$promptTheme = "$env:OneDrive\Documents\PowerShell\prompt\themes\easy-term.omp.json"
if (Test-Path $promptTheme) {
  try {
    oh-my-posh --init --shell pwsh --config $promptTheme | Invoke-Expression
    Write-Host "✅ oh-my-posh prompt loaded successfully" -ForegroundColor Green
  }
  catch {
    Write-Error "❌ Failed to load oh-my-posh prompt: $($_.Exception.Message)"
  }
}
else {
  Write-Warning "⚠️  oh-my-posh theme not found at: $promptTheme"
}

Write-Host "⚙️  Configuring PSReadLine options..." -ForegroundColor Cyan
try {
  Set-PSReadLineOption -PredictionSource History
  Set-PSReadLineOption -PredictionViewStyle ListView
  Set-PSReadLineOption -EditMode Windows
  Set-PSReadLineKeyHandler -Key Tab -Function Complete
  Write-Host "✅ PSReadLine configured successfully" -ForegroundColor Green
}
catch {
  Write-Warning "⚠️  Failed to configure PSReadLine: $($_.Exception.Message)"
}

# Configure PSFzf if available and fzf is installed
if (Get-Command fzf -ErrorAction SilentlyContinue) {
  try {
    Import-Module -Name PSFzf -ErrorAction Stop
    Set-PsFzfOption -PSReadlineChordProvider 'Ctrl+f' -PSReadlineChordReverseHistory 'Ctrl+r'
    Write-Host "✅ PSFzf configured successfully" -ForegroundColor Green
  }
  catch {
    Write-Warning "Failed to configure PSFzf: $($_.Exception.Message)"
  }
}
else {
  Write-Host "⚠️  fzf binary not found in PATH. PSFzf features will be unavailable." -ForegroundColor Yellow
  Write-Host "   Install fzf with: winget install fzf" -ForegroundColor Gray
}

Write-Host "🔧 Setting up command completers and additional modules..." -ForegroundColor Cyan

# Register winget completion
try {
  Register-ArgumentCompleter -Native -CommandName winget -ScriptBlock {
    param($wordToComplete, $commandAst, $cursorPosition)
    [Console]::InputEncoding = [Console]::OutputEncoding = $OutputEncoding = [System.Text.Utf8Encoding]::new()
    $Local:word = $wordToComplete.Replace('"', '""')
    $Local:ast = $commandAst.ToString().Replace('"', '""')
    winget complete --word="$Local:word" --commandline "$Local:ast" --position $cursorPosition | ForEach-Object {
      [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
    }
  }
  Write-Host "✅ winget tab completion configured" -ForegroundColor Green
}
catch {
  Write-Warning "⚠️  Failed to configure winget completion: $($_.Exception.Message)"
}

# =============================================================================
# NETWORK AND SYSTEM UTILITIES
# =============================================================================

<#
.SYNOPSIS
Get your public IP address
.DESCRIPTION
Retrieves your external/public IP address by querying ifconfig.me
.EXAMPLE
Get-Ip-Address
getIp
#>
function Get-Ip-Address {
  (Invoke-WebRequest -Uri ifconfig.me/ip).Content
}

Set-Alias getIp Get-Ip-Address

<#
.SYNOPSIS
Restart WSL (Windows Subsystem for Linux) distributions
.DESCRIPTION
Shuts down WSL completely, which effectively restarts all running distributions
.PARAMETER Distro
The name of the WSL distribution to restart (defaults to 'Ubuntu')
.EXAMPLE
Invoke-WslReboot
wslreboot
wslreboot "Debian"
#>
function Invoke-WslReboot() {
  param (
    [string]$Distro = 'Ubuntu'
  )
  Write-Host "Rebooting $Distro"
  wsl --shutdown
}

Set-Alias wslreboot Invoke-WslReboot

# =============================================================================
# APPLICATION AND PACKAGE MANAGEMENT
# =============================================================================

<#
.SYNOPSIS
Update family budget database from Excel file
.DESCRIPTION
Runs a Python script to export budget data from an Excel spreadsheet
Specific to the user's budget management workflow
.EXAMPLE
Update-Budget
updbudget
#>
function Update-Budget() {
  Write-Host "Updating budget database"
  py D:\dev\export-budget-csv\export.py -s "$env:OneDrive\Documents\Financial\Wood Family Financials.xlsx"
  Write-Host "Budget database updated"
}

Set-Alias updbudget Update-Budget

<#
.SYNOPSIS
Update all packages using winget
.DESCRIPTION
Runs 'winget upgrade' to update all installed packages
.EXAMPLE
Update-Winget
wgu
#>
function Update-Winget() {
  winget upgrade
}

Set-Alias wgu Update-Winget
#f45873b3-b655-43a6-b217-97c00aa0db58 PowerToys CommandNotFound module
try {
  Import-Module -Name Microsoft.WinGet.CommandNotFound -ErrorAction Stop
  Write-Host "✅ PowerToys CommandNotFound module loaded" -ForegroundColor Green
}
catch {
  Write-Warning "⚠️  PowerToys CommandNotFound module not available: $($_.Exception.Message)"
}
#f45873b3-b655-43a6-b217-97c00aa0db58

Write-Host "🗂️  Initializing zoxide (smart directory navigation)..." -ForegroundColor Cyan
if (Get-Command zoxide -ErrorAction SilentlyContinue) {
  try {
    Invoke-Expression (& { (zoxide init powershell | Out-String) })
    Write-Host "✅ zoxide initialized successfully" -ForegroundColor Green
  }
  catch {
    Write-Warning "⚠️  Failed to initialize zoxide: $($_.Exception.Message)"
  }
}
else {
  Write-Host "📦 zoxide not found. Attempting to install via winget..." -ForegroundColor Yellow
  try {
    winget install -e --id ajeetdsouza.zoxide
    Write-Host "✅ zoxide installed successfully. Initializing..." -ForegroundColor Green
    Invoke-Expression (& { (zoxide init powershell | Out-String) })
  }
  catch {
    Write-Error "❌ Failed to install zoxide: $($_.Exception.Message)"  
  }
}

# Fabric patterns integration (with error handling)
Write-Host "🧩 Loading Fabric AI patterns..." -ForegroundColor Cyan
try {
  # Path to the patterns directory
  $patternsPath = Join-Path $HOME ".config/fabric/patterns"
  if (Test-Path $patternsPath) {
    $patternCount = 0
    foreach ($patternDir in Get-ChildItem -Path $patternsPath -Directory -ErrorAction SilentlyContinue) {
      $patternName = $patternDir.Name

      # Dynamically define a function for each pattern
      $functionDefinition = @"
function $patternName {
    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipeline = `$true)]
        [string] `$InputObject,

        [Parameter(ValueFromRemainingArguments = `$true)]
        [String[]] `$patternArgs
    )

    begin {
        # Initialize an array to collect pipeline input
        `$collector = @()
    }

    process {
        # Collect pipeline input objects
        if (`$InputObject) {
            `$collector += `$InputObject
        }
    }

    end {
        # Join all pipeline input into a single string, separated by newlines
        `$pipelineContent = `$collector -join "`n"

        # If there's pipeline input, include it in the call to fabric
        if (`$pipelineContent) {
            `$pipelineContent | fabric --pattern $patternName `$patternArgs
        } else {
            # No pipeline input; just call fabric with the additional args
            fabric --pattern $patternName `$patternArgs
        }
    }
}
"@
      # Add the function to the current session
      Invoke-Expression $functionDefinition
      $patternCount++
    }
    Write-Host "✅ Loaded $patternCount Fabric patterns successfully" -ForegroundColor Green
  }
  else {
    Write-Host "ℹ️  Fabric patterns directory not found at: $patternsPath" -ForegroundColor Cyan
  }
}
catch {
  Write-Warning "⚠️  Failed to load fabric patterns: $($_.Exception.Message)"
}

# =============================================================================
# FABRIC AI INTEGRATION FUNCTIONS
# =============================================================================

<#
.SYNOPSIS
Get YouTube video transcript using Fabric AI
.DESCRIPTION
Downloads and processes YouTube video transcripts using the Fabric AI tool
Can optionally include timestamps in the transcript
.PARAMETER t
Switch to include timestamps in the transcript
.PARAMETER videoLink
The YouTube video URL to process
.EXAMPLE
yt "https://youtube.com/watch?v=example"
yt -t "https://youtube.com/watch?v=example"  # With timestamps
#>
function yt {
  [CmdletBinding()]
  param(
    [Parameter()]
    [Alias("timestamps")]
    [switch]$t,

    [Parameter(Position = 0, ValueFromPipeline = $true)]
    [string]$videoLink
  )

  begin {
    $transcriptFlag = "--transcript"
    if ($t) {
      $transcriptFlag = "--transcript-with-timestamps"
    }
  }

  process {
    if (-not $videoLink) {
      Write-Error "Usage: yt [-t | --timestamps] youtube-link"
      return
    }
  }

  end {
    if ($videoLink) {
      # Execute and allow output to flow through the pipeline
      fabric -y $videoLink $transcriptFlag
    }
  }
}

# =============================================================================
# FILE SYSTEM UTILITIES
# =============================================================================

<#
.SYNOPSIS
Fast file finder - search for files by name pattern
.DESCRIPTION
Recursively searches for files matching a name pattern from current directory
Similar to Unix 'find' command but simpler syntax
.PARAMETER name
The search pattern to match against filenames (supports wildcards)
.EXAMPLE
ff "*.txt"
ff "config"
ff "package.json"
#>
function ff($name) {
  Get-ChildItem -Recurse -Filter "*${name}*" -ErrorAction SilentlyContinue | ForEach-Object { 
    Write-Output "$($_.Directory)\$($_.Name)" 
  }
}

<#
.SYNOPSIS
Create an empty file (Unix-style touch command)
.DESCRIPTION
Creates a new empty file or updates the timestamp of an existing file
Mimics the behavior of the Unix 'touch' command
.PARAMETER file
The path and name of the file to create or touch
.EXAMPLE
touch "newfile.txt"
touch "C:\temp\example.log"
#>
function touch($file) {
  "" | Out-File -File $file -Encoding ascii
}

# =============================================================================
# PROFILE MANAGEMENT FUNCTIONS
# =============================================================================

<#
.SYNOPSIS
Reload the current PowerShell profile
.DESCRIPTION
Reloads the PowerShell profile to apply any changes made to the profile file
Useful for testing profile modifications without restarting the terminal
.EXAMPLE
Update-Profile
reload-profile
#>
function Update-Profile {
  & $PROFILE
}

# Alias for backward compatibility
Set-Alias reload-profile Update-Profile

<#
.SYNOPSIS
Check for and install PowerShell updates
.DESCRIPTION
Checks GitHub for the latest PowerShell release and updates via winget if needed
Includes network connectivity check to avoid unnecessary delays
.EXAMPLE
Update-PowerShell
#>
function Update-PowerShell {
  # Check if we can connect to GitHub with a faster, quieter method
  try {
    $response = Test-Connection -ComputerName "8.8.8.8" -Count 1 -Quiet -TimeoutSeconds 2
    if (-not $response) {
      Write-Host "Skipping PowerShell update check - no internet connection." -ForegroundColor Yellow
      return
    }
  }
  catch {
    Write-Host "Skipping PowerShell update check - network unavailable." -ForegroundColor Yellow
    return
  }

  try {
    Write-Host "Checking for PowerShell updates..." -ForegroundColor Cyan
    $updateNeeded = $false
    $currentVersion = $PSVersionTable.PSVersion.ToString()
    $gitHubApiUrl = "https://api.github.com/repos/PowerShell/PowerShell/releases/latest"
    $latestReleaseInfo = Invoke-RestMethod -Uri $gitHubApiUrl
    $latestVersion = $latestReleaseInfo.tag_name.Trim('v')
    if ($currentVersion -lt $latestVersion) {
      $updateNeeded = $true
    }

    if ($updateNeeded) {
      Write-Host "Updating PowerShell..." -ForegroundColor Yellow
      winget upgrade "Microsoft.PowerShell" --accept-source-agreements --accept-package-agreements
      Write-Host "PowerShell has been updated. Please restart your shell to reflect changes" -ForegroundColor Magenta
    }
    else {
      Write-Host "Your PowerShell is up to date." -ForegroundColor Green
    }
  }
  catch {
    Write-Error "Failed to update PowerShell. Error: $_"
  }
}
# Commented out automatic PowerShell update check to prevent slow profile loading
# Run 'Update-PowerShell' manually when you want to check for updates
# Update-PowerShell

# =============================================================================
# UNIX-LIKE UTILITY FUNCTIONS
# =============================================================================

<#
.SYNOPSIS
Search for text patterns in files (Unix grep equivalent)
.DESCRIPTION
Searches for regex patterns in files or pipeline input
Mimics the behavior of the Unix 'grep' command
.PARAMETER regex
The regular expression pattern to search for
.PARAMETER dir
Optional directory to search in (searches current dir if not specified)
.EXAMPLE
grep "error" *.log
Get-Content file.txt | grep "pattern"
grep "TODO" C:\Projects
#>
function grep($regex, $dir) {
  if ($dir) {
    Get-ChildItem $dir | Select-String $regex
    return
  }
  $input | Select-String $regex
}

<#
.SYNOPSIS
Find the location of a command (Unix which equivalent)
.DESCRIPTION
Locates the executable file for a given command name
Mimics the behavior of the Unix 'which' command
.PARAMETER command
The name of the command to locate
.EXAMPLE
which "git"
which "notepad"
#>
function which ($command) { 
  Get-Command -Name $command -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Path -ErrorAction SilentlyContinue 
}

<#
.SYNOPSIS
Display disk usage information (Unix df equivalent)
.DESCRIPTION
Shows disk space usage for all mounted volumes
Mimics the behavior of the Unix 'df' command
.EXAMPLE
df
#>
function df {
  get-volume
}

<#
.SYNOPSIS
Display the first lines of a file (Unix head equivalent)
.DESCRIPTION
Shows the first N lines of a text file (default: 10 lines)
Mimics the behavior of the Unix 'head' command
.PARAMETER Path
The path to the file to read
.PARAMETER n
Number of lines to display (default: 10)
.EXAMPLE
head "file.txt"
head "file.txt" 5
#>
function head {
  param($Path, $n = 10)
  Get-Content $Path -Head $n
}

<#
.SYNOPSIS
Display the last lines of a file (Unix tail equivalent)
.DESCRIPTION
Shows the last N lines of a text file (default: 10 lines)
Mimics the behavior of the Unix 'tail' command
.PARAMETER Path
The path to the file to read
.PARAMETER n
Number of lines to display (default: 10)
.EXAMPLE
tail "file.txt"
tail "logfile.log" 20
#>
function tail {
  param($Path, $n = 10)
  Get-Content $Path -Tail $n
}

<#
.SYNOPSIS
Quick navigation to Documents folder
.DESCRIPTION
Changes the current directory to the user's Documents folder
.EXAMPLE
docs
#>
function docs { Set-Location -Path $HOME\Documents }

# =============================================================================
# NETWORKING UTILITIES
# =============================================================================

<#
.SYNOPSIS
Flush DNS cache
.DESCRIPTION
Clears the DNS resolver cache to force fresh DNS lookups
Useful for troubleshooting DNS issues
.EXAMPLE
flushdns
#>
function flushdns { Clear-DnsClientCache }

# =============================================================================
# CLIPBOARD UTILITIES
# =============================================================================

<#
.SYNOPSIS
Copy text to clipboard
.DESCRIPTION
Copies the specified text to the Windows clipboard
.PARAMETER args
The text to copy to clipboard
.EXAMPLE
cpy "Hello World"
#>
function cpy { Set-Clipboard $args[0] }

<#
.SYNOPSIS
Paste text from clipboard
.DESCRIPTION
Retrieves text from the Windows clipboard and displays it
.EXAMPLE
pst
#>
function pst { Get-Clipboard }

# Enhanced PowerShell Experience
Write-Host "🎨 Configuring PowerShell color scheme..." -ForegroundColor Cyan
try {
  Set-PSReadLineOption -Colors @{
    Command   = 'Yellow'
    Parameter = 'Green'
    String    = 'DarkCyan'
  }
  Write-Host "✅ Color scheme applied successfully" -ForegroundColor Green
}
catch {
  Write-Warning "⚠️  Failed to apply color scheme: $($_.Exception.Message)"
}

$env:GITHUB_PERSONAL_ACCESS_TOKEN = [Environment]::GetEnvironmentVariable("GITHUB_PERSONAL_ACCESS_TOKEN", "User")

# http://bin.christitus.com/unakijolon

function Sync-VSCodeProfile {
  <#
    .SYNOPSIS
    Syncs the current PowerShell profile to VS Code
    
    .DESCRIPTION
    Creates or updates the VS Code PowerShell profile to source this main profile,
    keeping all your customizations in sync between regular PowerShell and VS Code.
    #>
  $mainProfile = $PROFILE.CurrentUserCurrentHost
  $vscodeProfile = $mainProfile -replace "Microsoft\.PowerShell", "Microsoft.VSCode"
    
  if (Test-Path $mainProfile) {
    $vscodeContent = @"
# VS Code PowerShell Profile
# This profile sources the main PowerShell profile to keep them in sync
# Last synced: $(Get-Date)

# Source the main PowerShell profile
`$mainProfile = "$mainProfile"
if (Test-Path `$mainProfile) {
    . `$mainProfile
    Write-Host "✅ Loaded main PowerShell profile in VS Code" -ForegroundColor Green
} else {
    Write-Warning "Main PowerShell profile not found at: `$mainProfile"
}

# VS Code specific customizations can go here if needed
"@
        
    Set-Content -Path $vscodeProfile -Value $vscodeContent -Encoding UTF8
    Write-Host "✅ VS Code profile synced successfully!" -ForegroundColor Green
    Write-Host "Location: $vscodeProfile" -ForegroundColor Cyan
  }
  else {
    Write-Error "Main PowerShell profile not found at: $mainProfile"
  }
}

Set-Alias syncvscode Sync-VSCodeProfile

# Profile loading complete
Write-Host "" # Empty line for spacing
Write-Host "🎉 PowerShell profile loaded successfully!" -ForegroundColor Green
Write-Host "   Type 'Get-Help about_profiles' for more information" -ForegroundColor Gray
Write-Host "   Use 'syncvscode' to sync this profile with VS Code" -ForegroundColor Gray
