modules/UI/SpotifyFormDisplay.psm1

# Spotify Windows Form Display Module
# Shows current track, artist, time, and next track in a Windows Form

Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

function Show-SpotifyForm {
    <#
    .SYNOPSIS
    Display current Spotify playback in a Windows Form
 
    .DESCRIPTION
    Shows a modern Windows Form with current track information that updates in real-time
 
    .PARAMETER Block
    Block terminal until window is closed (default: runs in background)
 
    .EXAMPLE
    Show-SpotifyForm
 
    .EXAMPLE
    Show-SpotifyForm -Block
 
    .EXAMPLE
    ss
    Quick alias to show Spotify form
    #>


    param(
        [switch]$Block
    )

    # If Block is NOT specified (default), launch in a new PowerShell window
    if (-not $Block) {
        $modulePath = $PSScriptRoot
        $mainModulePath = Split-Path (Split-Path $modulePath -Parent) -Parent
        $mainModulePath = Join-Path $mainModulePath "SpotifyCommands.psd1"

        # Get current environment variables for Spotify credentials
        $clientId = $env:SPOTIFY_CLIENT_ID
        $clientSecret = $env:SPOTIFY_CLIENT_SECRET

        $scriptBlock = @"
`$ErrorActionPreference = 'SilentlyContinue'
`$env:SPOTIFY_CLIENT_ID = '$clientId'
`$env:SPOTIFY_CLIENT_SECRET = '$clientSecret'
try {
    Import-Module '$mainModulePath' -Force -DisableNameChecking
    Show-SpotifyForm -Block
} catch {
    # Silently exit — do not show Read-Host which would make the window visible
}
"@


        # Save to temp file to avoid Windows Terminal intercepting Start-Process pwsh
        $tempScript = Join-Path $env:TEMP "SpotifyForm_$(Get-Date -Format 'yyyyMMddHHmmss').ps1"
        $scriptBlock | Out-File -FilePath $tempScript -Encoding UTF8

        $psi = [System.Diagnostics.ProcessStartInfo]::new()
        $psi.FileName = (Get-Command pwsh -ErrorAction SilentlyContinue)?.Source ?? 'pwsh.exe'
        $psi.Arguments = "-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$tempScript`""
        $psi.CreateNoWindow = $true
        $psi.UseShellExecute = $false
        [System.Diagnostics.Process]::Start($psi) | Out-Null

        Write-Host "Spotify display launched" -ForegroundColor Green
        return
    }

    # --- CREATE FORM ---
    $form = New-Object System.Windows.Forms.Form
    $form.Text = "Spotify - Now Playing"
    $form.Size = New-Object System.Drawing.Size(450, 260)
    $form.StartPosition = "CenterScreen"
    $form.TopMost = $true
    $form.FormBorderStyle = "FixedToolWindow"
    $form.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#191414") # Spotify Dark

    # Force absolute topmost using Win32 API
    Add-Type -TypeDefinition @"
        using System;
        using System.Runtime.InteropServices;
        public class WindowHelper {
            [DllImport("user32.dll")]
            public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
            public static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
            public const uint SWP_NOMOVE = 0x0002;
            public const uint SWP_NOSIZE = 0x0001;
            public const uint SWP_SHOWWINDOW = 0x0040;
        }
"@
 -ErrorAction SilentlyContinue

    # --- LAYOUT (Labels) ---

    # Song Title (Spotify Green) - Using Segoe UI Emoji for emoji support
    $lblSong = New-Object System.Windows.Forms.Label
    $lblSong.Location = New-Object System.Drawing.Point(15, 10)
    $lblSong.Size = New-Object System.Drawing.Size(360, 40)
    try {
        $lblSong.Font = New-Object System.Drawing.Font("Segoe UI Emoji", 14, [System.Drawing.FontStyle]::Bold)
    } catch {
        $lblSong.Font = New-Object System.Drawing.Font("Segoe UI", 14, [System.Drawing.FontStyle]::Bold)
    }
    $lblSong.ForeColor = [System.Drawing.ColorTranslator]::FromHtml("#1DB954") # Spotify Green
    $lblSong.Text = "Loading..."
    $form.Controls.Add($lblSong)

    # Artist (Yellow)
    $lblArtist = New-Object System.Windows.Forms.Label
    $lblArtist.Location = New-Object System.Drawing.Point(15, 50)
    $lblArtist.Size = New-Object System.Drawing.Size(360, 25)
    try {
        $lblArtist.Font = New-Object System.Drawing.Font("Segoe UI Emoji", 11, [System.Drawing.FontStyle]::Regular)
    } catch {
        $lblArtist.Font = New-Object System.Drawing.Font("Segoe UI", 11, [System.Drawing.FontStyle]::Regular)
    }
    $lblArtist.ForeColor = [System.Drawing.ColorTranslator]::FromHtml("#FFD700") # Gold/Yellow
    $lblArtist.Text = ""
    $form.Controls.Add($lblArtist)

    # Album (Blue)
    $lblAlbum = New-Object System.Windows.Forms.Label
    $lblAlbum.Location = New-Object System.Drawing.Point(15, 75)
    $lblAlbum.Size = New-Object System.Drawing.Size(360, 20)
    try {
        $lblAlbum.Font = New-Object System.Drawing.Font("Segoe UI Emoji", 9, [System.Drawing.FontStyle]::Italic)
    } catch {
        $lblAlbum.Font = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Italic)
    }
    $lblAlbum.ForeColor = [System.Drawing.ColorTranslator]::FromHtml("#64B5F6") # Light Blue
    $lblAlbum.Text = ""
    $form.Controls.Add($lblAlbum)

    # Time (Light Gray - Monospace)
    $lblTime = New-Object System.Windows.Forms.Label
    $lblTime.Location = New-Object System.Drawing.Point(15, 100)
    $lblTime.Size = New-Object System.Drawing.Size(360, 20)
    $lblTime.Font = New-Object System.Drawing.Font("Consolas", 10)
    $lblTime.ForeColor = [System.Drawing.ColorTranslator]::FromHtml("#B3B3B3")
    $lblTime.Text = "00:00 / 00:00"
    $form.Controls.Add($lblTime)

    # Progress Bar
    $progressBar = New-Object System.Windows.Forms.ProgressBar
    $progressBar.Location = New-Object System.Drawing.Point(15, 125)
    $progressBar.Size = New-Object System.Drawing.Size(360, 8)
    $progressBar.Style = "Continuous"
    $progressBar.ForeColor = [System.Drawing.ColorTranslator]::FromHtml("#1DB954")
    $form.Controls.Add($progressBar)

    # Next Track (Pink)
    $lblNext = New-Object System.Windows.Forms.Label
    $lblNext.Location = New-Object System.Drawing.Point(15, 140)
    $lblNext.Size = New-Object System.Drawing.Size(360, 20)
    try {
        $lblNext.Font = New-Object System.Drawing.Font("Segoe UI Emoji", 9, [System.Drawing.FontStyle]::Italic)
    } catch {
        $lblNext.Font = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Italic)
    }
    $lblNext.ForeColor = [System.Drawing.ColorTranslator]::FromHtml("#FF69B4") # Hot Pink
    $lblNext.Text = "Up Next: ..."
    $form.Controls.Add($lblNext)

    # --- CONTROL BUTTONS ---
    $buttonY = 170
    $buttonWidth = 75
    $buttonHeight = 30
    $buttonSpacing = 8

    # Previous Button
    $btnPrevious = New-Object System.Windows.Forms.Button
    $btnPrevious.Location = New-Object System.Drawing.Point(15, $buttonY)
    $btnPrevious.Size = New-Object System.Drawing.Size($buttonWidth, $buttonHeight)
    $btnPrevious.Text = "Prev"
    $btnPrevious.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#282828")
    $btnPrevious.ForeColor = "White"
    $btnPrevious.FlatStyle = "Flat"
    $btnPrevious.FlatAppearance.BorderColor = [System.Drawing.ColorTranslator]::FromHtml("#404040")
    $btnPrevious.Tag = "previous"
    $form.Controls.Add($btnPrevious)

    # Play/Pause Button
    $btnPlayPause = New-Object System.Windows.Forms.Button
    $btnPlayPause.Location = New-Object System.Drawing.Point((15 + $buttonWidth + $buttonSpacing), $buttonY)
    $btnPlayPause.Size = New-Object System.Drawing.Size($buttonWidth, $buttonHeight)
    $btnPlayPause.Text = "Pause"
    $btnPlayPause.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#1DB954")
    $btnPlayPause.ForeColor = "White"
    $btnPlayPause.FlatStyle = "Flat"
    $btnPlayPause.FlatAppearance.BorderColor = [System.Drawing.ColorTranslator]::FromHtml("#1DB954")
    $btnPlayPause.Tag = "playpause"
    $form.Controls.Add($btnPlayPause)

    # Next Button
    $btnNext = New-Object System.Windows.Forms.Button
    $btnNext.Location = New-Object System.Drawing.Point((15 + ($buttonWidth + $buttonSpacing) * 2), $buttonY)
    $btnNext.Size = New-Object System.Drawing.Size($buttonWidth, $buttonHeight)
    $btnNext.Text = "Next"
    $btnNext.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#282828")
    $btnNext.ForeColor = "White"
    $btnNext.FlatStyle = "Flat"
    $btnNext.FlatAppearance.BorderColor = [System.Drawing.ColorTranslator]::FromHtml("#404040")
    $btnNext.Tag = "next"
    $form.Controls.Add($btnNext)

    # Shuffle Button
    $btnShuffle = New-Object System.Windows.Forms.Button
    $btnShuffle.Location = New-Object System.Drawing.Point((15 + ($buttonWidth + $buttonSpacing) * 3), $buttonY)
    $btnShuffle.Size = New-Object System.Drawing.Size($buttonWidth, $buttonHeight)
    $btnShuffle.Text = "Shuffle"
    $btnShuffle.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#282828")
    $btnShuffle.ForeColor = "White"
    $btnShuffle.FlatStyle = "Flat"
    $btnShuffle.FlatAppearance.BorderColor = [System.Drawing.ColorTranslator]::FromHtml("#404040")
    $btnShuffle.Tag = "shuffle"
    $form.Controls.Add($btnShuffle)

    # Repeat Button
    $btnRepeat = New-Object System.Windows.Forms.Button
    $btnRepeat.Location = New-Object System.Drawing.Point((15 + ($buttonWidth + $buttonSpacing) * 4), $buttonY)
    $btnRepeat.Size = New-Object System.Drawing.Size($buttonWidth, $buttonHeight)
    $btnRepeat.Text = "Repeat"
    $btnRepeat.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#282828")
    $btnRepeat.ForeColor = "White"
    $btnRepeat.FlatStyle = "Flat"
    $btnRepeat.FlatAppearance.BorderColor = [System.Drawing.ColorTranslator]::FromHtml("#404040")
    $btnRepeat.Tag = "repeat"
    $form.Controls.Add($btnRepeat)

    # Add click handlers
    $btnPrevious.Add_Click({
        try {
            Invoke-SpotifyApi -Method POST -Path "/me/player/previous" | Out-Null
        } catch { }
    })

    $btnPlayPause.Add_Click({
        try {
            $currentState = Invoke-SpotifyApi -Method GET -Path "/me/player"
            if ($currentState -and $currentState.is_playing) {
                Invoke-SpotifyApi -Method PUT -Path "/me/player/pause" | Out-Null
            } else {
                Invoke-SpotifyApi -Method PUT -Path "/me/player/play" | Out-Null
            }
        } catch { }
    })

    $btnNext.Add_Click({
        try {
            Invoke-SpotifyApi -Method POST -Path "/me/player/next" | Out-Null
        } catch { }
    })

    $btnShuffle.Add_Click({
        try {
            $currentState = Invoke-SpotifyApi -Method GET -Path "/me/player"
            $newState = -not $currentState.shuffle_state
            Invoke-SpotifyApi -Method PUT -Path "/me/player/shuffle?state=$newState" | Out-Null
            # Update button immediately
            if ($newState) {
                $btnShuffle.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#1DB954")
            } else {
                $btnShuffle.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#282828")
            }
            # Update "Up Next" after short delay (queue changes when shuffle toggles)
            Start-Sleep -Milliseconds 300
            try {
                $queue = Invoke-SpotifyApi -Method GET -Path "/me/player/queue"
                if ($queue -and $queue.queue -and $queue.queue.Count -gt 0) {
                    $nextTrack = $queue.queue[0]
                    if ($nextTrack.type -eq "episode") {
                        $lblNext.Text = "Up Next: " + $nextTrack.name
                    } else {
                        $nextArtists = ($nextTrack.artists | ForEach-Object { $_.name }) -join ", "
                        $lblNext.Text = "Up Next: " + $nextTrack.name + " - " + $nextArtists
                    }
                }
            } catch {
                $lblNext.Text = "Up Next: ..."
            }
        } catch { }
    })

    $btnRepeat.Add_Click({
        try {
            $currentState = Invoke-SpotifyApi -Method GET -Path "/me/player"
            $newState = if ($currentState.repeat_state -eq "off") { "context" } else { "off" }
            Invoke-SpotifyApi -Method PUT -Path "/me/player/repeat?state=$newState" | Out-Null
            # Update button immediately
            if ($newState -ne "off") {
                $btnRepeat.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#1DB954")
                $btnRepeat.Text = "Repeat"
            } else {
                $btnRepeat.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#282828")
                $btnRepeat.Text = "Repeat"
            }
        } catch { }
    })

    # Event to force absolute topmost when form is shown
    $form.Add_Shown({
        try {
            [WindowHelper]::SetWindowPos(
                $form.Handle,
                [WindowHelper]::HWND_TOPMOST,
                0, 0, 0, 0,
                [WindowHelper]::SWP_NOMOVE -bor [WindowHelper]::SWP_NOSIZE -bor [WindowHelper]::SWP_SHOWWINDOW
            ) | Out-Null
        } catch {
            Write-Verbose "Could not set absolute topmost: $($_.Exception.Message)"
        }
    })

    # --- UPDATE TIMER ---
    $script:lastTrackId = ""
    $timer = New-Object System.Windows.Forms.Timer
    $timer.Interval = 1000 # Update every second

    # Create scriptblock with closure to capture module scope
    $updateScriptBlock = {
        try {
            # Get current playback
            $currentTrack = Invoke-SpotifyApi -Method GET -Path "/me/player/currently-playing"

            if ($currentTrack -and $currentTrack.item) {
                $item = $currentTrack.item
                $progress = if ($currentTrack.progress_ms) { $currentTrack.progress_ms } else { 0 }
                $duration = $item.duration_ms
                $isPlaying = $currentTrack.is_playing

                # Update song if changed
                if ($item.id -ne $script:lastTrackId) {
                    $script:lastTrackId = $item.id

                    if ($currentTrack.currently_playing_type -eq "episode") {
                        # Podcast
                        $songText = "🎙️ " + $item.name
                        $lblSong.Text = $songText
                        # Adjust song font size
                        $songSize = if ($songText.Length -gt 45) { 9 } elseif ($songText.Length -gt 30) { 12 } else { 14 }
                        try {
                            $lblSong.Font = New-Object System.Drawing.Font("Segoe UI Emoji", $songSize, [System.Drawing.FontStyle]::Bold)
                        } catch {
                            $lblSong.Font = New-Object System.Drawing.Font("Segoe UI", $songSize, [System.Drawing.FontStyle]::Bold)
                        }

                        $artistText = "👤 " + $item.show.name
                        $lblArtist.Text = $artistText
                        # Adjust artist font size
                        $artistSize = if ($artistText.Length -gt 50) { 8 } elseif ($artistText.Length -gt 35) { 9 } else { 11 }
                        try {
                            $lblArtist.Font = New-Object System.Drawing.Font("Segoe UI Emoji", $artistSize, [System.Drawing.FontStyle]::Regular)
                        } catch {
                            $lblArtist.Font = New-Object System.Drawing.Font("Segoe UI", $artistSize, [System.Drawing.FontStyle]::Regular)
                        }

                        $lblAlbum.Text = "📻 Podcast Episode"
                    } else {
                        # Music
                        $songText = "🎵 " + $item.name
                        $lblSong.Text = $songText
                        # Adjust song font size
                        $songSize = if ($songText.Length -gt 45) { 9 } elseif ($songText.Length -gt 30) { 12 } else { 14 }
                        try {
                            $lblSong.Font = New-Object System.Drawing.Font("Segoe UI Emoji", $songSize, [System.Drawing.FontStyle]::Bold)
                        } catch {
                            $lblSong.Font = New-Object System.Drawing.Font("Segoe UI", $songSize, [System.Drawing.FontStyle]::Bold)
                        }

                        $artists = ($item.artists | ForEach-Object { $_.name }) -join ", "
                        $artistText = "👤 " + $artists
                        $lblArtist.Text = $artistText
                        # Adjust artist font size
                        $artistSize = if ($artistText.Length -gt 50) { 8 } elseif ($artistText.Length -gt 35) { 9 } else { 11 }
                        try {
                            $lblArtist.Font = New-Object System.Drawing.Font("Segoe UI Emoji", $artistSize, [System.Drawing.FontStyle]::Regular)
                        } catch {
                            $lblArtist.Font = New-Object System.Drawing.Font("Segoe UI", $artistSize, [System.Drawing.FontStyle]::Regular)
                        }

                        $albumText = "📀 " + $item.album.name
                        $lblAlbum.Text = $albumText
                        # Adjust album font size
                        if ($albumText.Length -gt 50) {
                            try {
                                $lblAlbum.Font = New-Object System.Drawing.Font("Segoe UI Emoji", 7, [System.Drawing.FontStyle]::Italic)
                            } catch {
                                $lblAlbum.Font = New-Object System.Drawing.Font("Segoe UI", 7, [System.Drawing.FontStyle]::Italic)
                            }
                        } else {
                            try {
                                $lblAlbum.Font = New-Object System.Drawing.Font("Segoe UI Emoji", 9, [System.Drawing.FontStyle]::Italic)
                            } catch {
                                $lblAlbum.Font = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Italic)
                            }
                        }
                    }

                    # Get next track from queue
                    try {
                        $queue = Invoke-SpotifyApi -Method GET -Path "/me/player/queue"
                        if ($queue -and $queue.queue -and $queue.queue.Count -gt 0) {
                            $nextTrack = $queue.queue[0]
                            if ($nextTrack.type -eq "episode") {
                                $lblNext.Text = "Up Next: " + $nextTrack.name
                            } else {
                                $nextArtists = ($nextTrack.artists | ForEach-Object { $_.name }) -join ", "
                                $lblNext.Text = "Up Next: " + $nextTrack.name + " - " + $nextArtists
                            }
                        } else {
                            $lblNext.Text = "Up Next: End of queue"
                        }
                    } catch {
                        $lblNext.Text = "Up Next: ..."
                    }
                }

                # Update time and progress
                $currentMin = [int][Math]::Floor($progress / 60000)
                $currentSec = [int][Math]::Floor(($progress % 60000) / 1000)
                $totalMin = [int][Math]::Floor($duration / 60000)
                $totalSec = [int][Math]::Floor(($duration % 60000) / 1000)

                $currentTime = "{0}:{1:D2}" -f $currentMin, $currentSec
                $totalTime = "{0}:{1:D2}" -f $totalMin, $totalSec

                $statusIcon = if ($isPlaying) { "▶️" } else { "⏸️" }
                $lblTime.Text = "$statusIcon $currentTime / $totalTime"

                # Update progress bar
                if ($duration -gt 0) {
                    $percentage = [Math]::Min(100, [Math]::Floor(($progress / $duration) * 100))
                    $progressBar.Value = $percentage
                }

                # Update Play/Pause button text
                if ($isPlaying) {
                    $btnPlayPause.Text = "Pause"
                } else {
                    $btnPlayPause.Text = "Play"
                }

                # Update Shuffle/Repeat button states
                try {
                    $playerState = Invoke-SpotifyApi -Method GET -Path "/me/player"
                    if ($playerState) {
                        # Update shuffle button
                        if ($playerState.shuffle_state) {
                            $btnShuffle.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#1DB954")
                        } else {
                            $btnShuffle.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#282828")
                        }

                        # Update repeat button
                        if ($playerState.repeat_state -ne "off") {
                            $btnRepeat.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#1DB954")
                            $btnRepeat.Text = if ($playerState.repeat_state -eq "track") { "Repeat 1" } else { "Repeat" }
                        } else {
                            $btnRepeat.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#282828")
                            $btnRepeat.Text = "Repeat"
                        }
                    }
                } catch {
                    # Ignore state update errors
                }
            } else {
                # Nothing playing
                $lblSong.Text = "No track playing"
                $lblArtist.Text = "Open Spotify and start playing"
                $lblAlbum.Text = ""
                $lblTime.Text = "Paused"
                $lblNext.Text = ""
                $progressBar.Value = 0
                $btnPlayPause.Text = "Play"
                $script:lastTrackId = ""
            }
        } catch {
            # Error occurred
            $lblSong.Text = "Error loading track"
            $lblArtist.Text = $_.Exception.Message
            $lblAlbum.Text = ""
            $lblTime.Text = ""
            $lblNext.Text = ""
            Write-Verbose "Error updating form: $($_.Exception.Message)"
        }

        # Force window to stay absolutely on top
        try {
            [WindowHelper]::SetWindowPos(
                $form.Handle,
                [WindowHelper]::HWND_TOPMOST,
                0, 0, 0, 0,
                [WindowHelper]::SWP_NOMOVE -bor [WindowHelper]::SWP_NOSIZE
            ) | Out-Null
        } catch {
            # Silently ignore if it fails
        }
    }.GetNewClosure()

    $timer.Add_Tick($updateScriptBlock)

    # Initial update
    $timer.Start()

    # Cleanup on close
    $form.Add_FormClosing({
        $timer.Stop()
        $timer.Dispose()
    })

    # Show form - this will block until window is closed
    # Using ShowDialog for proper event handling
    Write-Host "🎵 Opening Spotify display..." -ForegroundColor Cyan
    Write-Host "ℹ️ The window will update every second. Close it to return to terminal." -ForegroundColor Yellow

    $form.ShowDialog() | Out-Null

    Write-Host "✅ Spotify display closed." -ForegroundColor Green
}

# Create alias
Set-Alias -Name ShowSpotify -Value Show-SpotifyForm -ErrorAction SilentlyContinue
Set-Alias -Name ss -Value Show-SpotifyForm -ErrorAction SilentlyContinue

Export-ModuleMember -Function Show-SpotifyForm -Alias ShowSpotify, ss