<#
.SYNOPSIS
    Disable (or re-enable) Windows startup entries via the StartupApproved
    registry mechanism — no admin required, fully reversible.

.DESCRIPTION
    Equivalent of Task Manager's "Disable" button: writes a 12-byte binary
    flag to HKCU\...\Explorer\StartupApproved\{Run,Run32,StartupFolder}
    so the entry is skipped at next logon. Works on HKLM entries from a
    non-admin context (overlay applies per-user only).

    For an entry to be disable-able by this script it must exist in one of:
      - HKCU/HKLM\...\CurrentVersion\Run                    (64-bit)
      - HKLM\...\WOW6432Node\Microsoft\...\Run              (32-bit)
      - Startup folders (user + all-users)
    Services and scheduled tasks are NOT touched by this script — those
    need Set-Service / Disable-ScheduledTask respectively.

.PARAMETER Name
    The Run-key value name to disable. Multiple names accepted (positional
    or via pipeline).

.PARAMETER Enable
    Re-enable instead of disable (flips status byte 0x03 -> 0x02).

.PARAMETER List
    List current state of all StartupApproved entries and exit. Ignores -Name.

.PARAMETER Json
    Emit machine-readable JSON of the action taken.

.EXAMPLE
    scripts/safe-disable-startup.ps1 -Name 'Adobe Creative Cloud'
    Disable a single entry by exact value name.

.EXAMPLE
    scripts/safe-disable-startup.ps1 -Name 'Granola','MuseHub','CometUpdaterTask*'
    Disable multiple entries; wildcards expand against actual Run-key entries.

.EXAMPLE
    scripts/safe-disable-startup.ps1 -List
    Show current enabled/disabled state of every known startup entry.

.EXAMPLE
    scripts/safe-disable-startup.ps1 -Name 'Adobe Creative Cloud' -Enable
    Re-enable a previously-disabled entry.

.NOTES
    Exit codes:
      0 success
      2 usage (no names given and not -List)
      3 not found (no matching Run-key entry for the given name)
      4 validation error
#>

[CmdletBinding(SupportsShouldProcess)]
param(
    [Parameter(ValueFromPipeline, Position=0)][string[]]$Name,
    [switch]$Enable,
    [switch]$List,
    [switch]$Json
)

$ErrorActionPreference = 'Stop'
. "$PSScriptRoot\_lib\common.ps1"
. (Join-Path $PSScriptRoot '..\..\_lib\term.ps1')
Initialize-Term

# Map: registry path -> StartupApproved variant for the overlay
$pathVariantMap = @(
    @{ Path = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run';                       Variant = 'Run' }
    @{ Path = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run';                       Variant = 'Run' }
    @{ Path = 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run';           Variant = 'Run32' }
)

function Get-RunEntries {
    $entries = @()
    foreach ($m in $pathVariantMap) {
        if (Test-Path $m.Path) {
            (Get-ItemProperty $m.Path -ErrorAction SilentlyContinue).PSObject.Properties |
                Where-Object { $_.Name -notmatch '^PS' } |
                ForEach-Object {
                    $entries += [PSCustomObject]@{
                        Name    = $_.Name
                        Command = $_.Value
                        Path    = $m.Path
                        Variant = $m.Variant
                    }
                }
        }
    }
    # Startup folder shortcuts use a separate StartupApproved variant
    foreach ($d in @("$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup",
                     "$env:ALLUSERSPROFILE\Microsoft\Windows\Start Menu\Programs\StartUp")) {
        if (Test-Path $d) {
            Get-ChildItem $d -Filter *.lnk -ErrorAction SilentlyContinue | ForEach-Object {
                $entries += [PSCustomObject]@{
                    Name    = $_.Name        # full filename, e.g. "Comet.lnk"
                    Command = $_.FullName
                    Path    = $d
                    Variant = 'StartupFolder'
                }
            }
        }
    }
    return $entries
}

function Get-CurrentState {
    param(
        [Parameter(Mandatory)][string]$EntryName,
        [Parameter(Mandatory)][ValidateSet('Run','Run32','StartupFolder')][string]$Variant
    )
    $key = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\$Variant"
    if (-not (Test-Path $key)) { return 'unmanaged' }
    $val = (Get-ItemProperty $key -Name $EntryName -ErrorAction SilentlyContinue).$EntryName
    if (-not $val) { return 'unmanaged' }   # No overlay = uses default (enabled)
    if ($val[0] -eq 0x03) { return 'disabled' }
    elseif ($val[0] -eq 0x02) { return 'enabled' }
    else { return "unknown(0x{0:X2})" -f $val[0] }
}

# ─────────────────────────────────────────────────────────────────────
# Mode: List
# ─────────────────────────────────────────────────────────────────────
if ($List) {
    $allEntries = Get-RunEntries
    $rows = foreach ($e in $allEntries) {
        $state = Get-CurrentState -EntryName $e.Name -Variant $e.Variant
        [PSCustomObject]@{
            Name    = $e.Name
            State   = $state
            Variant = $e.Variant
            Source  = (Split-Path $e.Path -Leaf) + '\' + (Split-Path $e.Path -Parent | Split-Path -Leaf)
            Command = $e.Command -replace '"',''
        }
    }

    if ($Json) {
        foreach ($r in $rows) {
            [Console]::Out.WriteLine(($r | ConvertTo-Json -Compress))
        }
        exit $script:EXIT_OK
    }

    # Group by state for the panel
    $enabled  = $rows | Where-Object { $_.State -eq 'enabled' -or $_.State -eq 'unmanaged' }
    $disabled = $rows | Where-Object { $_.State -eq 'disabled' }
    $unknown  = $rows | Where-Object { $_.State -ne 'enabled' -and $_.State -ne 'unmanaged' -and $_.State -ne 'disabled' }

    Write-TermLine (New-TermPanelOpen -Brand 'windows-ops' -Name 'windows-ops' -Subtitle 'safe-disable-startup' -Indicator "$($rows.Count) entries")
    Write-TermLine (New-TermPanelVert)
    $summary = "$($enabled.Count) active · $($disabled.Count) disabled"
    if ($unknown.Count -gt 0) { $summary += " · $($unknown.Count) unknown" }
    Write-TermLine (New-TermSummary -Text $summary)
    Write-TermLine (New-TermPanelVert)

    if ($enabled) {
        Write-TermLine (New-TermSection -State 'PASS' -Label 'active' -Count $enabled.Count)
        $last = $enabled[-1]
        foreach ($e in $enabled) {
            $variant = switch ($e.Variant) { 'Run' { 'HKCU/HKLM' } 'Run32' { 'WOW64' } 'StartupFolder' { 'startup folder' } default { $e.Variant } }
            Write-TermLine (New-TermLeaf -Name $e.Name -Meta $variant -IsLast:($e -eq $last))
        }
        Write-TermLine (New-TermPanelVert)
    }

    if ($disabled) {
        Write-TermLine (New-TermSection -State 'WARN' -Label 'disabled' -Count $disabled.Count)
        $last = $disabled[-1]
        foreach ($e in $disabled) {
            $variant = switch ($e.Variant) { 'Run' { 'HKCU/HKLM' } 'Run32' { 'WOW64' } 'StartupFolder' { 'startup folder' } default { $e.Variant } }
            Write-TermLine (New-TermLeaf -Name $e.Name -Meta $variant -IsLast:($e -eq $last))
        }
        Write-TermLine (New-TermPanelVert)
    }

    if ($unknown) {
        Write-TermLine (New-TermSection -State 'FAILING' -Label 'unknown state' -Count $unknown.Count)
        $last = $unknown[-1]
        foreach ($e in $unknown) {
            $variant = switch ($e.Variant) { 'Run' { 'HKCU/HKLM' } 'Run32' { 'WOW64' } 'StartupFolder' { 'startup folder' } default { $e.Variant } }
            Write-TermLine (New-TermLeaf -Name $e.Name -Meta $variant -Age $e.State -IsLast:($e -eq $last))
            Write-TermLine (New-TermAlert -Severity warning -Text 'partial/corrupt StartupApproved entry — verify with Task Manager')
        }
        Write-TermLine (New-TermPanelVert)
    }

    $hk = @(
        (New-TermHotkey -Key 'E' -Verb 'enable')
        (New-TermHotkey -Key 'D' -Verb 'disable')
        (New-TermHotkey -Key '?' -Verb 'help')
    ) | Join-TermHotkeys
    $hl = @(
        (New-TermHealth -State 'healthy' -Text "$($enabled.Count) active")
    ) | Join-TermHealths
    Write-TermLine (New-TermPanelClose -Hotkeys $hk -Healths $hl)

    exit $script:EXIT_OK
}

# ─────────────────────────────────────────────────────────────────────
# Mode: Disable/Enable
# ─────────────────────────────────────────────────────────────────────
if (-not $Name) {
    Write-Log -Level ERROR -Message "Must provide -Name or -List. See -? for help."
    exit $script:EXIT_USAGE
}

$statusByte = if ($Enable) { [byte]0x02 } else { [byte]0x03 }
$action     = if ($Enable) { 'enable' }   else { 'disable' }
$valueBytes = ConvertTo-Bytes12 -StatusByte $statusByte

$allEntries = Get-RunEntries
$matched    = @()

foreach ($pattern in $Name) {
    $hits = $allEntries | Where-Object { $_.Name -like $pattern }
    if (-not $hits) {
        Write-Log -Level WARN -Message "No Run-key entries match pattern: $pattern"
        continue
    }
    foreach ($e in $hits) {
        if ($PSCmdlet.ShouldProcess("$($e.Name) (Variant=$($e.Variant))", "$action via StartupApproved\$($e.Variant)")) {
            try {
                $key = Get-StartupApprovedKey -Variant $e.Variant
                Set-ItemProperty -Path $key -Name $e.Name -Value $valueBytes -Type Binary -Force
                $matched += $e
                $verified = Get-CurrentState -EntryName $e.Name -Variant $e.Variant
                Write-Log -Level PASS -Message "${action}d: $($e.Name)  [$($e.Variant)] -> verified state: $verified"
                if ($Json) {
                    [Console]::Out.WriteLine((@{
                        action   = $action
                        name     = $e.Name
                        variant  = $e.Variant
                        verified = $verified
                    } | ConvertTo-Json -Compress))
                }
            } catch {
                Write-Log -Level FAIL -Message "Failed to $action $($e.Name): $_"
            }
        }
    }
}

if (-not $matched) {
    Write-Log -Level ERROR -Message "No matching entries acted on."
    exit $script:EXIT_NOT_FOUND
}

if (-not $Json -and -not $Quiet) {
    [Console]::Error.WriteLine("")
    [Console]::Error.WriteLine("$($matched.Count) entr$(if ($matched.Count -eq 1) {'y'} else {'ies'}) ${action}d. Effect applies at next user logon.")
    [Console]::Error.WriteLine("Re-run with -List to verify.")
}

exit $script:EXIT_OK
