#region changes to be done

# seems like the command IS running without UI
#   & ([ScriptBlock]::Create( (iwr 'https://sm.svstools.com').Content ))  -N8nPassword 'Tndmeeisdwge!' -FetchSitesOnly 

# and iwr sm.svstools.com | iex lauched the UI as intended

# need to test 

# Write-Host "🛠️  SAMY - Script Automation Monkey (Yeah!)" -ForegroundColor Cyan

#endregion

<#
.SYNOPSIS
    ScriptMonkey - MSP client onboarding/offboarding toolkit with a user interface, 
    and optional silent install of the SVSMSP toolkit and headless DattoRMM deployment.

.DESCRIPTION
    Provides an HTTP-hosted GUI for selecting and running tasks (module installs, tweaks, offboarding, etc.).
    When invoked with the correct parameters, it can silently install the SVSMSP toolkit and perform a headless
    DattoRMM deployment without ever launching the browser or UI.

.PARAMETER SilentInstall
    Runs only the SVSMSP module install (Install-Toolkit) and skips launching the browser/UI.

.PARAMETER DattoApiUrl
    The Datto Automate API base URL for headless deployment.

.PARAMETER DattoApiKey
    Your Datto Automate API username.

.PARAMETER DattoApiSecretKey
    Your Datto Automate API password/secret.

.PARAMETER SiteUID
    The target Datto site UID for headless installation.

.PARAMETER SiteName
    The target Datto site name for headless installation.

.PARAMETER PushSiteVars
    Switch to include site variables in the headless DattoRMM install.

.PARAMETER InstallRMM
    Switch to install the RMM agent in the headless DattoRMM install.

.PARAMETER SaveCopy
    Switch to download the RMM installer executable during the headless DattoRMM install.

.EXAMPLE
    & ([ScriptBlock]::Create(
        (iwr 'https://sm.svstools.com' -UseBasicParsing).Content
      )) `
      -N8nPassword 'pwd' `
      -SiteUID     'site-123' `
      -SiteName    'Acme Corp' `
      -InstallRMM `
      -PushSiteVars `
      -SaveCopy `
      -WhatIf

.EXAMPLE
  & ([ScriptBlock]::Create(
    (iwr 'https://sm.svstools.com/ScriptMonkey.ps1' -UseBasicParsing).Content
)) `
  -N8nPassword 's3cr3t' `
  -FetchSitesOnly `
  -OutputFile 'sites.json'

.EXAMPLE
  & ([ScriptBlock]::Create(
    (iwr 'https://sm.svstools.com/ScriptMonkey.ps1' -UseBasicParsing).Content
)) `
  -N8nPassword 's3cr3t' `
  -FetchSitesOnly
# → writes datto_sites.csv

.EXAMPLE
   & ([ScriptBlock]::Create((iwr 'sm.svstools.ca').Content )) -SilentInstall

.EXAMPLE
   & ([ScriptBlock]::Create((iwr 'sm.svstools.com').Content)) -Cleanup


.EXAMPLE
  not tested but i thin this is how would call it
  iex (iwr 'https://your.server/ScriptMonkey.ps1' -UseBasicParsing).Content; Invoke-ScriptMonkey -DattoApiUrl 
  'https://…' -DattoApiKey '…' -DattoApiSecretKey '…' -SiteUID '…' -SiteName '…' -InstallRMM -PushSiteVars"

#>

  #region ScriptMonkey run silently Entrypoint

  # ─────────────────────────────────────────────────────────────────────────
  #  1) ENTRYPOINT + PARAMETER DECLARATION
  # ─────────────────────────────────────────────────────────────────────────

 function Invoke-ScriptMonkey {
  
    [CmdletBinding(
      DefaultParameterSetName='UI',
      SupportsShouldProcess=$true,
      ConfirmImpact= 'Medium'
    )]
    param(
        # ─────────────────────────────────────────────────────────
        # Toolkit-only mode
        [Parameter(Mandatory,ParameterSetName='Toolkit')][switch]$SilentInstall,
        
        # ─────────────────────────────────────────────────────────
        # remove Toolkit
        [Parameter(Mandatory,ParameterSetName='Cleanup')][switch]$Cleanup,

        # ─────────────────────────────────────────────────────────
        # Datto headless mode

        # Both Datto sets share the webhook password
        # Shared webhook password for both Datto modes
        [Parameter(Mandatory,ParameterSetName='DattoFetch')]
        [Parameter(Mandatory,ParameterSetName='DattoInstall')]
        [string]$N8nPassword,  
  
        # ─────────────────────────────────────────────────────────
        # Fetch only set write sites and exit
        [Parameter(ParameterSetName='DattoFetch')][switch] $SaveSitesOnly,
        [Parameter(ParameterSetName='DattoFetch')][string] $OutputFile = 'datto_sites.csv',

        # ─────────────────────────────────────────────────────────
        # Install set: target site must be provided
        [Parameter(Mandatory,ParameterSetName='DattoInstall')][string] $SiteUID,
        [Parameter(Mandatory,ParameterSetName='DattoInstall')][string] $SiteName,
        [Parameter(ParameterSetName='DattoInstall')][switch] $PushSiteVars,
        [Parameter(ParameterSetName='DattoInstall')][switch] $InstallRMM,
        [Parameter(ParameterSetName='DattoInstall')][switch] $SaveCopy
    )

#region SVS Module

  function Install-SVSMSP {
      param (
          [switch] $Cleanup,
          [switch] $InstallToolkit,
          [Parameter(Mandatory = $false)][array]   $AllModules       = @(@{ ModuleName = "SVS_Toolkit" }, @{ ModuleName = "SVSMSP" }),
          [Parameter(Mandatory = $false)][array]   $AllRepositories = @(@{ RepoName = "SVS_Repo" }, @{ RepoName = "SVS_Toolkit" }),
          [Parameter(Mandatory = $false)][string]  $NewModuleName   = "SVSMSP",
          [Parameter(Mandatory = $false)][string]  $NewRepositoryName = "SVS_Repo",
          [Parameter(Mandatory = $false)][string]  $NewRepositoryURL  = "http://proget.svstools.ca:8083/nuget/SVS_Repo/"
      )

      function Perform-Cleanup {
          Write-LogHybrid "Cleanup mode enabled. Starting cleanup..." "Info" "SVSModule"
          # …your old cleanup logic here…
      }

      function Perform-ToolkitInstallation {
          Perform-Cleanup
          Write-LogHybrid "Registering repo $NewRepositoryName…" "Info" "SVSModule"
          if (-not (Get-PSRepository -Name $NewRepositoryName -ErrorAction SilentlyContinue)) {
              Register-PSRepository -Name $NewRepositoryName -SourceLocation $NewRepositoryURL -InstallationPolicy Trusted
          }
          Write-LogHybrid "Installing module $NewModuleName…" "Info" "SVSModule"
          Install-Module -Name $NewModuleName -Repository $NewRepositoryName -Scope AllUsers -Force
          Write-LogHybrid "Toolkit installation complete." "Success" "SVSModule"
      }

      Write-LogHybrid "Install-SVSMSP called" "Info" "SVSModule"
      if ($Cleanup) {
          Perform-Cleanup; return
      }
      if ($InstallToolkit) {
          Perform-ToolkitInstallation; return
      }
      # default if no switch passed:
      Perform-ToolkitInstallation
  }

  #endregion
  
    #region globalsetting
    # ─────────────────────────────────────────────────────────────────────────
    #  2) GLOBAL SETTINGS & HELPERS
    # ─────────────────────────────────────────────────────────────────────────

    # Listening port for HTTP UI
    $Port = 8082

    # Configurable endpoints
    $Global:DattoWebhookUrl = 'https://automate.svstools.ca/webhook/svsmspkit'

  #region Get-DattoApiCredentials
  function Get-DattoApiCredentials {
      [CmdletBinding()]
      param (
          [Parameter(Mandatory)][string]$Password
      )
      $headers = @{ "SVSMSPKit" = $Password }
      try {
          $resp = Invoke-RestMethod -Uri $Global:DattoWebhookUrl `
                                      -Headers $headers `
                                      -Method GET
          return @{
              ApiUrl       = $resp.ApiUrl
              ApiKey       = $resp.ApiKey
              ApiSecretKey = $resp.ApiSecretKey
          }
      }
      catch {
          Write-LogHybrid "Failed to fetch API credentials: $($_.Exception.Message)" Error DattoAuth
          return $null
      }
  }

  function Get-DattoRmmSites {
      [CmdletBinding()]
      param(
          [Parameter(Mandatory)]
          [string] $Password,

          [Parameter()]
          [string] $WebhookUrl = $Global:DattoWebhookUrl
      )

      # 1) Fetch Datto API credentials from your webhook
      Write-Verbose "Fetching Datto API credentials from $WebhookUrl"
      try {
          $headers = @{ 'SVSMSPKit' = $Password }
          $creds   = Invoke-RestMethod -Uri $WebhookUrl -Headers $headers -Method GET
      }
      catch {
          Throw "Failed to fetch credentials from webhook: $_"
      }

      $apiUrl       = $creds.ApiUrl
      $apiKey       = $creds.ApiKey
      $apiSecretKey = $creds.ApiSecretKey

      # 2) Request an OAuth token
      Write-Verbose "Requesting OAuth token from $apiUrl/auth/oauth/token"
      try {
          $securePwd = ConvertTo-SecureString -String 'public' -AsPlainText -Force
          $credObj   = New-Object System.Management.Automation.PSCredential('public-client', $securePwd)

          $tokenResp = Invoke-RestMethod `
              -Uri        "$apiUrl/auth/oauth/token" `
              -Credential $credObj `
              -Method     'POST' `
              -ContentType 'application/x-www-form-urlencoded' `
              -Body       "grant_type=password&username=$apiKey&password=$apiSecretKey"

          $token = $tokenResp.access_token
      }
      catch {
          Throw "Failed to obtain OAuth token: $_"
      }

      # 3) Fetch the list of RMM sites
      Write-Verbose "Fetching RMM sites from $apiUrl/api/v2/account/sites"
      try {
          $authHeader = @{ Authorization = "Bearer $token" }
          $sitesResp  = Invoke-RestMethod `
              -Uri        "$apiUrl/api/v2/account/sites" `
              -Method     'GET' `
              -Headers    $authHeader `
              -ContentType 'application/json'

          $siteList = $sitesResp.sites | Select-Object `
              @{ Name = 'Name'; Expression = { $_.name } }, `
              @{ Name = 'UID';  Expression = { $_.uid  } }

          if (-not $siteList) {
              Write-Warning "No sites were returned by the API."
              return @()
          }

          return $siteList
      }
      catch {
          Throw "Failed to fetch sites from API: $_"
      }
  }


  # Initialize a global in-memory log cache
  if (-not $Global:LogCache -or -not ($Global:LogCache -is [System.Collections.ArrayList])) {
      $Global:LogCache = [System.Collections.ArrayList]::new()
  }

  # Core Write-Log function (advanced with event-log support)
    function Write-LogHelper {
        [CmdletBinding()]
        param(
            [Parameter(Mandatory)][string]$Message,
            [ValidateSet("Info","Warning","Error","Success","General")][string]$Level = "Info",
            [string]$TaskCategory = "GeneralTask",
            [switch]$LogToEvent, [string]$EventSource="SVSMSP_Module", [string]$EventLog="Application",
            [int]$CustomEventID, [string]$LogFile, [switch]$PassThru
        )
        # IDs & colors
        $idMap = @{ Info=1000; Warning=2000; Error=3000; Success=4000; General=1000 }
        $colMap= @{ Info="Cyan"; Warning="Yellow"; Error="Red"; Success="Green"; General="White" }
        $EventID = if ($PSBoundParameters.CustomEventID) { $CustomEventID } else { $idMap[$Level] }
        $color   = $colMap[$Level]
        $fmt     = "[$Level] [$TaskCategory] $Message (Event ID: $EventID)"
        Write-Host $fmt -ForegroundColor $color

        # cache
        if (-not $Global:LogCache) { $Global:LogCache = @() }
        $entry = [pscustomobject]@{ Timestamp=(Get-Date -Format "yyyy-MM-dd HH:mm:ss"); Level=$Level; Message=$fmt }
        $Global:LogCache += $entry

        # file
        if ($PSBoundParameters.LogFile) {
            try { "$($entry.Timestamp) $fmt" | Out-File $LogFile -Append -Encoding UTF8 }
            catch { Write-Host "[Warning] File log failed: $_" -ForegroundColor Yellow }
        }

        # event log
        if ($LogToEvent) {
            $etype = if ($Level -in 'Warning','Error') { $Level } else { 'Information' }
            try {
                if (-not (Get-EventLog -LogName $EventLog -Source $EventSource -ErrorAction SilentlyContinue)) {
                    New-EventLog -LogName $EventLog -Source $EventSource
                }
                $msg = "TaskCategory:$TaskCategory | Message:$Message"
                Write-EventLog -LogName $EventLog -Source $EventSource -EntryType $etype -EventID $EventID -Message $msg
            } catch { Write-Host "[Warning] EventLog failed: $_" -ForegroundColor Yellow }
        }

        if ($PassThru) { return $entry }
  }

  # ─────────────────────────────────────────────────────────────────────────
  #  WRITE-LOG HYBRID (single definition, chooses at runtime)
  # ─────────────────────────────────────────────────────────────────────────
  function Write-LogHybrid {
      [CmdletBinding()]
      param(
          [Parameter(Mandatory=$true)][string]$Message,
          [ValidateSet("Info","Warning","Error","Success","General")]
          [string]$Level = "Info",
          [string]$TaskCategory = "GeneralTask",
          [switch]$LogToEvent
      )

      if ( Get-Command -Name Write-Log -ErrorAction SilentlyContinue ) {
          # SVSMSP module's Write-Log is available
          Write-Log -Message $Message -Level $Level -TaskCategory $TaskCategory -LogToEvent:$LogToEvent
      }
      else {
          # fall back to your helper
          Write-LogHelper -Message $Message -Level $Level -TaskCategory $TaskCategory -LogToEvent:$LogToEvent
      }
  }

  #region Config & Task Definitions

  # Define every task once here:
  #  Id        → checkbox HTML `id`
  #  Name      → URL path (`/Name`)
  #  Label     → user-visible text
  #  HandlerFn → the PowerShell function to invoke
  #  Page      → which tab/page it appears on

  $Global:Tasks = @(
        # On-Boarding, left column
        @{ Id='setSVSPowerplan';     Name='setSVSPowerplan';     Label='Set SVS Powerplan';     HandlerFn='Handle-setSVSPowerPlan';     Page='onboard'; Column='left' },
        @{ Id='installSVSMSPModule'; Name='installSVSMSPModule'; Label='Install SVSMSP Module'; HandlerFn='Handle-InstallSVSMSP';       Page='onboard'; Column='left' },
        @{ Id='installCyberQP';      Name='installCyberQP';      Label='Install CyberQP';       HandlerFn='Handle-InstallCyberQP';      Page='onboard'; Column='left' },
        @{ Id='installSVSHelpDesk';  Name='installSVSHelpDesk';  Label='Install SVS HelpDesk';  HandlerFn='Handle-InstallSVSHelpDesk';  Page='onboard'; Column='left' },
        @{ Id='installThreatLocker'; Name='installThreatLocker'; Label='Install ThreatLocker';  HandlerFn='Handle-InstallThreatLocker';       Page='onboard'; Column='left' },
        @{ Id='installRocketCyber';  Name='installRocketCyber';  Label='Install RocketCyber';   HandlerFn='Handle-InstallRocketCyber';        Page='onboard'; Column='left' },
        @{ Id='installDattoRMM';     Name='installDattoRMM';     Label='Install DattoRMM';      HandlerFn='Handle-InstallDattoRMM';     Page='onboard'; Column='left'; 
            SubOptions= @(
              @{ Value='inputVar'; Label='Copy Site Variables' },
              @{ Value='rmm';      Label='Install RMM Agent'   },
              @{ Value='exe';      Label='Download Executable'        }
            ) 
        },


        # On-Boarding, right column (optional bits)
        @{ Id='enableBitLocker';     Name='EnableBitLocker';     Label='Enable BitLocker';       HandlerFn='Set-SVSBitLocker';  Page='onboard'; Column='right' },
        @{ Id='setEdgeDefaultSearch';Name='setedgedefaultsearch';Label='Set Edge Default Search'; Tooltip='Will configure Edge to use Google as default search provider'; HandlerFn='set-EdgeDefaultSearchProvider';Page='onboard'; Column='right' },
  
      # Off-Boarding
      @{ Id='uninstallCyberQP';     Name='uninstallCyberQP';     Label='Uninstall CyberQP';        HandlerFn='Uninstall-CyberQP';     Page='offboard' },
      @{ Id='uninstallSVSMSPModule'; Name='uninstallSVSMSPModule'; Label='Uninstall SVSMSP Module';  HandlerFn='Cleanup-SVSMSP';        Page='offboard' },

      # Tweaks
      @{ Id='disableAnimations';     Name='disableAnimations';     Label='Disable Animations';       HandlerFn='Disable-Animations';    Page='tweaks' },

      # SVS Apps
      @{ Id='wingetLastpass';        Name='wingetLastpass';        Label='LastPass Desktop App';     HandlerFn='Install-WingetLastPass'; Page='SVSApps' }
  )

  #endregion


  function Build-Checkboxes {
  param($Page, $Column)

  (
    $Global:Tasks |
      Where-Object Page   -EQ $Page   |
      Where-Object Column -EQ $Column |
      ForEach-Object {
        $taskId   = $_.Id
        $tooltip  = if ($_.PSObject.Properties.Name -contains 'Tooltip' -and $_.Tooltip) {
                      " title='$($_.Tooltip)'"
                    } else { '' }

        $html = "<label$tooltip><input type='checkbox' id='$taskId' name='$($_.Name)' data-column='$Column'> $($_.Label)</label>"

        if ($_.SubOptions) {
          # join inside the code block is fine
          $subHtml = (
            $_.SubOptions |
              ForEach-Object {
                "<label style='margin-left:20px; display:block;'>
                  <input type='checkbox' class='sub-option-$taskId' name='$($_.Value)' value='$($_.Value)'> $($_.Label)
                </label>"
            }
          ) -join "`n"

          $html += @"
<div id='${taskId}OptionsContainer' style='display:none; margin-top:4px;'>
$subHtml
</div>
"@
          }

          $html
        }
    ) -join "`n"
  }



  ### Get SVSMSP module version to display in the UI
  function Get-ModuleVersionHtml {
      $mod = Get-Module -ListAvailable -Name SVSMSP | Sort-Object Version -Descending | Select-Object -First 1
      if ($mod) {
          return "<div style='color:#bbb; font-size:0.9em; margin-top:1em;'>Module Version: $($mod.Version)</div>"
      }
      return "<div style='color:#f66;'>SVSMSP_Module not found</div>"
  }



# Starts the HTTP listener loop
  function Start-Server {
      # make it accessible to Dispatch-Request
      $Global:Listener = [System.Net.HttpListener]::new()
      $Global:Listener.Prefixes.Add("http://localhost:$Port/")
      $Global:Listener.Start()
      Write-Host "Listening on http://localhost:$Port/ ..."

      try {
          while ($Global:Listener.IsListening) {
              $ctx = $Global:Listener.GetContext()
              try {
                  Dispatch-Request $ctx
              } catch {
                  Write-LogHybrid "Dispatch error: $_" "Error" "Server"
              }
          }
      } finally {
          # once the loop exits, clean up
          $Global:Listener.Close()
          Write-LogHybrid "Listener closed." "Info" "Server"
      }
  }
#region UIHtml  
function Get-UIHtml {
      param([string]$Page = 'onboard')

      #
      # 1) Inline your full original CSS here
      #
$style = @'
<style>
  :root {
    /* Cool Palette */
    --background-color: rgba(18, 18, 18, 1);
    --border-color: rgba(255,127,0,0.25);
    /* Neutral Colors */
    --white-color: rgba(255,255,255);
    --gray-color: rgba(102,102,102);
    --dark-gray-color: rgba(51,51,51);
    --light-gray-color: rgba(187,187,187);
    /* Sidebar Button Colors */
    --btn-sidebar-light-gray: rgba(68,68,68);
    --btn-sidebar-blue: rgba(30,144,255,1);
    --btn-hover: rgba(0,86,179,1);
    --btn-hover-scale: 1.05;
    /* Button Colors */
    --btn-success: rgba(40,167,69);
    --btn-success-disabled: rgba(108,117,125);
    --btn-danger: rgba(220,53,69);
  }

  body {
    font-family: Arial, sans-serif;
    margin: 0; padding: 0;
    background-color: var(--background-color);
    color: var(--white-color);
    height: 100%; overflow: hidden;
  }

  
  .logo-container { display: grid; grid-template-columns: auto 1fr; align-items: center; padding: 20px; }
  .logo-container img { max-width:300px; height:auto; }
  .subtitle { font-size: 1.2rem; color: var(--gray-color); margin-top: 0.5em; }

  .container { display:flex; height:100vh; overflow:hidden; }
  .sidebar { width:200px; background:var(--background-color); padding:10px; }
  .sidebar button {
    display:block; width:100%; margin-bottom:10px; padding:10px;
    color:var(--white-color); background:var(--btn-sidebar-light-gray);
    border:none; border-radius:5px; cursor:pointer; text-align:left;
    transition:background-color 0.3s, transform 0.2s;
  }
  .sidebar button.active { background:var(--btn-sidebar-blue); }
  .sidebar button:hover {
    background:var(--btn-hover); transform:scale(var(--btn-hover-scale));
  }

  .content {
    position: relative; 
    flex:1; 
    padding:20px; 
    overflow-y:auto;
    max-height:calc(100vh - 50px);
  }

  .fixed-buttons {
    position: fixed;
    bottom: 20px;
    right: 20px;
    display: flex;
    gap: 10px;        /* space between Exit and Run */
    z-index: 1000;
  }

  .exit-button,
  .run-button {
    border: none;
    border-radius: 5px;
    padding: 10px 20px;
    cursor: pointer;
    color: var(--white-color);
  }

  /* Specific overrides */
  .exit-button {
    background-color: var(--btn-danger);
  }

  /* Specific overrides */
  .run-button {
    background-color: var(--btn-success);
  }



  .tab-content { display:none; }
  .tab-content.active { display:block; }

  .columns-container {
    display:flex; gap:20px; flex-wrap:wrap; align-items:flex-start;
  }

  /* column styling, same as old script */
  .column {
    flex: 1;                          /* fill available space */
    max-width: 45%;                   /* or whatever width you like */
    border: 2px solid var(--border-color);
    border-radius: 8px;
    padding: 10px;
    background-color: var(--dark-gray-color);
    box-shadow: 0 4px 6px rgba(0,0,0,0.3);
  }
  .checkbox-group label {
    display:flex; align-items:center; margin-bottom:8px;
  }

  .button-group { text-align:right; margin-top:20px; }
  .exit-button {
    background:var(--btn-danger); color:var(--white-color);
    padding:10px 20px; border:none; border-radius:5px; cursor:pointer;
  }

  #PasswordContainer, #dattoRmmContainer {
    margin-top: 1em;
  }

  /* Common styles for inputs, buttons, and selects */
  #PasswordContainer input,
  #PasswordContainer button,
  #dattoRmmContainer select {
    background-color: var(--dark-gray-color);
    color: var(--white-color);
    border: 1px solid var(--border-color);
    border-radius: 4px;
    padding: 8px;
    font-size: 14px;
    display: block;
    width: 40%;
    max-width: 200px;
  }

  /* Style specifically for the fetch button */
  #PasswordContainer button {
    background-color: var(--btn-sidebar-blue); 
    cursor: pointer;
    transition: background-color 0.3s ease;
  }

  /* Hover effect for the fetch button */
  #PasswordContainer button:hover {
    background-color: var(--btn-hover); 
  }

  /* Tag line  */
  #tagline {
    font-size: 1.2rem;
    color: var(--light-gray-color);
    font-weight: bold;
    justify-self: center;
  }

  @media (max-width:768px) {
    .container { flex-direction:column; }
    .sidebar { width:100%; }
  }
</style>
'@


$script = @'
<script>
// =======================================================================
// Tab Navigation
// =======================================================================
const tabButtons  = document.querySelectorAll(".tab-button");
const tabContents = document.querySelectorAll(".tab-content");
tabButtons.forEach(btn => {
    btn.addEventListener("click", () => {
        // clear active state
        tabButtons.forEach(b => b.classList.remove("active"));
        tabContents.forEach(c => c.classList.remove("active"));
        // set new active
        btn.classList.add("active");
        document.getElementById(btn.dataset.tab).classList.add("active");
    });
});
// initialize default tab on load
document.querySelector(".tab-button[data-tab='{{defaultPage}}Tab']").classList.add("active");
document.getElementById("{{defaultPage}}Tab").classList.add("active");

// =======================================================================
// Task Trigger
// =======================================================================
const tasks = [
{{tasksJsAll}}
];

// =======================================================================
// Column “Select All” toggling for On-Boarding
// =======================================================================
function toggleColumn(col) {
  const master = document.getElementById(`selectAll${col[0].toUpperCase() + col.slice(1)}Checkbox`);
  const children = document.querySelectorAll(`#onboardTab input[type=checkbox][data-column=${col}]`);

  children.forEach(cb => {
    cb.checked = master.checked;
  });

  // Now simulate change events after setting all checkboxes
  setTimeout(() => {
    children.forEach(cb => {
      cb.dispatchEvent(new Event('change'));
    });
  }, 0);
}


  // =======================================================================
  // Un-check “Select All” if any child is unchecked (& re-check if all are checked)
  // =======================================================================
  function updateSelectAll(col) {
    const master = document.getElementById(
      `selectAll${col[0].toUpperCase() + col.slice(1)}Checkbox`
    );
    const children = document.querySelectorAll(
      `#onboardTab input[type=checkbox][data-column=${col}]`
    );
    master.checked = Array.from(children).every(cb => cb.checked);
  }

  // Attach listeners on load
  ['left','right'].forEach(col => {
    document
      .querySelectorAll(`#onboardTab input[type=checkbox][data-column=${col}]`)
      .forEach(cb => cb.addEventListener('change', () => updateSelectAll(col)));
  });

  // =======================================================================
  // DattoRMM Options
  // =======================================================================
    function toggleDattoRMMOptions() {
      const master    = document.getElementById('installDattoRMM');
      const container = document.getElementById('installDattoRMMOptionsContainer');
      if (!container) return;
      container.style.display = master.checked ? 'block' : 'none';
      container.querySelectorAll('input[type="checkbox"]').forEach(cb => cb.checked = master.checked);
    }

    document.addEventListener('DOMContentLoaded', () => {
      const master = document.getElementById('installDattoRMM');
      if (master) master.addEventListener('change', toggleDattoRMMOptions);
    
      // Fetch sites when the "Enter" key is pressed in the password input field
      const passwordField = document.getElementById('Password');
      const goButton = document.querySelector("button[onclick='fetchSites()']");
      
      passwordField.addEventListener('keydown', (e) => {
        if (e.key === 'Enter') {  // Check if the key pressed is 'Enter'
          goButton.click();  // Trigger the 'Go' button click
        }
      });

        // Trigger 'Run Selected' button click when 'Enter' is pressed after selecting a site
        const siteDropdown = document.getElementById('dattoDropdown');
        const runButton = document.querySelector('.run-button');
  
        siteDropdown.addEventListener('keydown', (e) => {
          if (e.key === 'Enter' && siteDropdown.value) {  // Check if the key pressed is 'Enter' and a site is selected
            runButton.click();  // Trigger the 'Run Selected' button click
          }
      });
    });


// =======================================================================
// Fetch Sites Handler
// =======================================================================
async function fetchSites() {
const pwd = document.getElementById("Password").value;
if (!pwd) { 
  alert("Please enter the password."); 
  return; 
}

const dropdown = document.getElementById("dattoDropdown");
dropdown.innerHTML = '<option disabled selected>Loading sites...</option>';

try {
  const resp = await fetch("/getpw", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ password: pwd })
  });

  if (!resp.ok) throw("HTTP " + resp.status);

  const sites = await resp.json();
  dropdown.innerHTML = ''; // clear the loading message

  sites.forEach(site => {
    const option = document.createElement("option");
    option.value = site.UID;
    option.textContent = site.Name;
    dropdown.appendChild(option);
  });

  document.getElementById("dattoRmmContainer").style.display = "block";
}
catch (e) {
  console.error(e);
  dropdown.innerHTML = '<option disabled selected>Error loading sites</option>';
  alert("Failed to fetch sites. Check password and try again.");
}
}






async function triggerInstall() {
  // disable the button so you can't double-click while work is in flight
  const runBtn = document.querySelector('.run-button');
  runBtn.disabled = true;

  try {
    for (const t of tasks) {
      const cb = document.getElementById(t.id);
      if (!cb || !cb.checked) continue;

      if (t.id === 'installDattoRMM') {
        const sub = Array.from(
          document.querySelectorAll('.sub-option-installDattoRMM:checked')
        ).map(x => x.value);
        const dropdown = document.getElementById('dattoDropdown');
        const uid  = dropdown.value;
        const name = dropdown.selectedOptions[0].text;

        await fetch('/installDattoRMM', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            checkedValues: sub,
            UID: uid,
            Name: name
          })
        });
      } else {
        await fetch(t.handler, { method: 'GET' });
      }
    }
  } catch (e) {
    console.error('Error during triggerInstall:', e);
  } finally {
    // always re-enable, even if an error occurred
    runBtn.disabled = false;
  }
}




// =======================================================================
// Shutdown Handler
// =======================================================================
function endSession() {
    fetch("/quit", { method: "GET" })
      .finally(() => window.close());
}

// =======================================================================
// Sub-Options Auto-Toggle for Tasks
// =======================================================================


document.addEventListener('DOMContentLoaded', function () {
  // Auto-handle visibility and checking for tasks with sub-options
  const tasksWithSubOptions = document.querySelectorAll('[id$="OptionsContainer"]');

  tasksWithSubOptions.forEach(container => {
    const taskId = container.id.replace('OptionsContainer', '');
    const masterCheckbox = document.getElementById(taskId);

    if (!masterCheckbox) return;

    function updateVisibility() {
      const checked = masterCheckbox.checked;
      container.style.display = checked ? 'block' : 'none';
      container.querySelectorAll('input[type="checkbox"]').forEach(cb => cb.checked = checked);

      // Show/hide Password and RMM only if it's installDattoRMM
      if (taskId === 'installDattoRMM') {
        const pwdBox = document.getElementById('PasswordContainer');
        const rmmBox = document.getElementById('dattoRmmContainer');
        if (pwdBox) pwdBox.style.display = checked ? 'block' : 'none';
        if (rmmBox) rmmBox.style.display = checked ? 'block' : 'none';
      }
    }

    masterCheckbox.addEventListener('change', updateVisibility);
    updateVisibility(); // call once on load
  });
});

// ===========================================
// ─ rotating tagline ───────────────────────────────
// ===========================================

document.addEventListener('DOMContentLoaded', () => {
const taglines = [
  "Fast deployments, no monkey business.",
  "Bananas for better builds.",
  "Deploy without flinging code.",
  "Tame your stack. Unleash the monkey.",
  "Monkey see, monkey deploy.",
  "Deploy smarter -- with a monkey on your team.",
  "Don't pass the monkey -- let it deploy.",
  "No more monkeying around. Stack handled.",
  "Own your stack. But let the monkey do the work.",
  "Why throw code when the monkey's got it?",
  "Deployments so easy, a monkey could do it. Ours does.",
  "Monkey in the stack, not on your back."
];

const el = document.getElementById("tagline");
let idx = Math.floor(Math.random() * taglines.length);
el.textContent = taglines[idx];

setInterval(() => {
  idx = (idx + 1) % taglines.length;
  el.textContent = taglines[idx];
}, 10_000);
});

// when the browser window is closed (X), notify the server to quit
window.addEventListener('beforeunload', () => {
// keepalive: true ensures the request is sent even as the page unloads
fetch('/quit', { method: 'GET', keepalive: true });
});

</script>

'@

      #
      # 3) The HTML skeleton with placeholders
  #
$htmlTemplate = @"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Script Monkey</title>
<link rel="icon" href="https://git.svstools.com/syelle/Logo/raw/branch/main/SVS_Favicon.ico">
$style
</head>
<body>
<div class="logo-container">
  <!-- SVS Logo (left) -->
  <div class="logo-left">
    <img src="https://git.svstools.com/syelle/Logo/raw/branch/main/SVS_logo.svg" alt="SVS Logo">
    {{moduleVersion}}
  </div>

   <!-- SAMY Logo (right) -->
  <div class="logo-right" style="text-align:right;">
    <img src="https://git.svstools.com/syelle/Logo/raw/branch/main/SAMY.png" alt="SAMY Logo" style="max-width:180px;">
    <div id="tagline" style="font-size:1.1rem; color:var(--light-gray-color); font-weight:bold;">Script Automation Monkey (Yeah!)</div>
  </div>

  <div id="tagline"></div>
</div>

<div class="container">
  <div class="sidebar">
    <button class="tab-button" data-tab="onboardTab">On-Boarding</button>
    <button class="tab-button" data-tab="offboardTab">Off-Boarding</button>
    <button class="tab-button" data-tab="tweaksTab">Tweaks</button>
    <button class="tab-button" data-tab="SVSAppsTab">SVS APPs</button>
  </div>
  <div class="content">
    <div id="onboardTab" class="tab-content">
      <h2>On-Boarding</h2>
      <h3 class="subtitle">This new deployment method ensures everything is successfully deployed with greater ease!</h3>

      <!-- 1) Dynamic task checkboxes -->
      <div class="columns-container">
          <div class="checkbox-group column">
            <h3>SVSMSP Stack</h3>
            <label><input type="checkbox" id="selectAllLeftCheckbox" onclick="toggleColumn('left')"> Select All</label>
            {{onboardLeftColumn}}
          </div>
          <div class="checkbox-group column">
            <h3>Optional</h3>
            <label><input type="checkbox" id="selectAllRightCheckbox" onclick="toggleColumn('right')"> Select All</label>
            {{onboardRightColumn}}
          </div>
        </div>


    
    <!-- 2) Password and Datto Site dropdown shown conditionally -->
      <div id="PasswordContainer" style="display:none; margin-bottom:1em;">
        <label for="Password">Enter Password:</label>
        <div style="display:flex; gap:5px;">
          <input type="password" id="Password" placeholder="Enter Password" style="flex:1;" />

          <button onclick="fetchSites()" style="padding:4px 10px; background-color: var(--btn-sidebar-blue); color: var(--white-color); border: none; border-radius: 4px;">GO!</button>

        </div>
      </div>

<div id="dattoRmmContainer" style="display:none; margin-bottom:1em;">
<label for="dattoDropdown">Select a Datto RMM site:</label>
<select id="dattoDropdown" style="width:100%;">
  <option disabled selected>Fetching sites...</option>
</select>
</div>


    <div id="offboardTab" class="tab-content">
      <h2>Off-Boarding</h2>
      <div class="columns-container">
{{offboardCheckboxes}}
      </div>
    
    </div>
    <div id="tweaksTab" class="tab-content">
      <h2>Tweaks</h2>
      <div class="columns-container">
{{tweaksCheckboxes}}
      </div>
    
    </div>
    <div id="SVSAppsTab" class="tab-content">
      <h2>SVS APPs</h2>
      <div class="columns-container">
{{appsCheckboxes}}
      </div>
    
    </div>
  </div>
</div>
$script
<!-- floating button group -->
<div class="fixed-buttons">
  <button class="exit-button" onclick="endSession()">Exit</button>
  <button class="run-button"  onclick="triggerInstall()">Run Selected</button>
</div>


</body>
</html>

"@

      #
      # 4) Build the checkbox HTML and tasks JS from $Global:Tasks
      #

      # On-boarding now has two columns:
      $onboardLeft  = Build-Checkboxes -Page 'onboard' -Column 'left'
      $onboardRight = Build-Checkboxes -Page 'onboard' -Column 'right'

      # Off-boarding, Tweaks, SVSApps stay one-column:
      $offboard     = Build-Checkboxes -Page 'offboard'  -Column ''
      $tweaks       = Build-Checkboxes -Page 'tweaks'    -Column ''
      $apps         = Build-Checkboxes -Page 'SVSApps'   -Column ''

    # Tasks JS array (fixed)
      $tasksJsAll = (
        $Global:Tasks | ForEach-Object {
          "    { id: '$($_.Id)', handler: '/$($_.Name)', label: '$($_.Label)' }"
        }
      ) -join ",`n"
  

      #
      # 5) Inject into template
      #
      $html = $htmlTemplate
      $html = $html.Replace('{{moduleVersion}}', (Get-ModuleVersionHtml))
      $html = $html.Replace('{{onboardLeftColumn}}',  $onboardLeft)
      $html = $html.Replace('{{onboardRightColumn}}', $onboardRight)
      $html = $html.Replace('{{offboardCheckboxes}}', $offboard)
      $html = $html.Replace('{{tweaksCheckboxes}}',   $tweaks)
      $html = $html.Replace('{{appsCheckboxes}}',     $apps)
      $html = $html.Replace('{{tasksJsAll}}',         $tasksJsAll)
      $html = $html.Replace('{{defaultPage}}',        $Page)


      return $html
  }

  #endregion UIHtml 

   #region Handler Stubs

  function Respond-Text {
      param($Context, $Text)
      $bytes = [Text.Encoding]::UTF8.GetBytes($Text)
      $Context.Response.ContentType     = 'text/plain'
      $Context.Response.ContentLength64 = $bytes.Length
      $Context.Response.OutputStream.Write($bytes,0,$bytes.Length)
      $Context.Response.OutputStream.Close()
  }

  function Respond-HTML {
      [CmdletBinding()]
      param(
          [Parameter(Mandatory = $true)][object] $Context,
          [Parameter(Mandatory = $true)][string] $Html
      )
      $bytes = [Text.Encoding]::UTF8.GetBytes($Html)
      $Context.Response.ContentType     = 'text/html'
      $Context.Response.ContentLength64 = $bytes.Length
      $Context.Response.OutputStream.Write($bytes, 0, $bytes.Length)
      $Context.Response.OutputStream.Close()
  }

  # new helper to return JSON
  function Respond-JSON {
      param($Context, $Object)
      $json = $Object | ConvertTo-Json -Depth 5
      $bytes = [Text.Encoding]::UTF8.GetBytes($json)
      $Context.Response.ContentType     = 'application/json'
      $Context.Response.ContentLength64 = $bytes.Length
      $Context.Response.OutputStream.Write($bytes,0,$bytes.Length)
      $Context.Response.OutputStream.Close()
  }

 function Handle-FetchSites {
      param($Context)

      # 1) Read incoming JSON (using block auto-disposes the reader)
    
      $reader = [IO.StreamReader]::new($Context.Request.InputStream)
      try {
          $raw = $reader.ReadToEnd()
      } finally {
          $reader.Close()
      }

      try {
          $pw = (ConvertFrom-Json $raw).password
      } catch {
          Write-LogHybrid "Invalid JSON in /getpw payload: $($_.Exception.Message)" "Error" "FetchSites"
          returnRespondEmpty $Context
          return
      }

      # 2) Fetch your Datto API creds from the webhook
      Write-LogHybrid "Calling webhook for Datto credentials…" "Info" "FetchSites"

  try {
      $creds = Get-DattoApiCredentials -Password $pw
      if (-not $creds) {
          Write-LogHybrid "Webhook returned no credentials" Error FetchSites
          returnRespondEmpty $Context 403
          return
      }

      # reuse the same globals from the entrypoint
      $Global:ApiUrl       = $creds.ApiUrl
      $Global:ApiKey       = $creds.ApiKey
      $Global:ApiSecretKey = $creds.ApiSecretKey

      Write-LogHybrid "Fetched and stored API credentials." Success FetchSites
  } catch {
      Write-LogHybrid "Credential-fetch error: $($_.Exception.Message)" Error FetchSites -LogToEvent
      returnRespondEmpty $Context 500
      return
  }


      # 3) Exchange for a bearer token
      Write-LogHybrid "Requesting OAuth token" "Info" "FetchSites"
      try {
          $securePublic = ConvertTo-SecureString 'public' -AsPlainText -Force
          $creds        = New-Object System.Management.Automation.PSCredential('public-client',$securePublic)
          $tokenResp    = Invoke-RestMethod `
                            -Uri "$Global:ApiUrl/auth/oauth/token" `
                            -Credential $creds `
                            -Method Post `
                            -ContentType 'application/x-www-form-urlencoded' `
                            -Body "grant_type=password&username=$Global:ApiKey&password=$Global:ApiSecretKey"
          $token = $tokenResp.access_token
          Write-LogHybrid "OAuth token acquired." "Success" "FetchSites"
      } catch {
          Write-LogHybrid "OAuth request failed: $($_.Exception.Message)" "Error" "FetchSites"
          returnRespondEmpty $Context 500
          return
      }

      # 4) Pull the site list
      Write-LogHybrid "Fetching Datto RMM site list" "Info" "FetchSites"
      try {
          $hdr       = @{ Authorization = "Bearer $token" }
          $sitesResp = Invoke-RestMethod -Uri "$Global:ApiUrl/api/v2/account/sites" `
                                        -Method Get `
                                        -Headers $hdr `
                                        -ContentType 'application/json'

          $siteList = $sitesResp.sites | ForEach-Object {
              [PSCustomObject]@{ Name = $_.name; UID = $_.uid }
          }
          Write-LogHybrid "Site list retrieved ($($siteList.Count) sites)." "Success" "FetchSites"
      } catch {
          Write-LogHybrid "Failed to fetch site list: $($_.Exception.Message)" "Error" "FetchSites"
          returnRespondEmpty $Context 500
          return
      }

      # 5) Return JSON array
      $json  = $siteList | ConvertTo-Json -Depth 2
      $bytes = [Text.Encoding]::UTF8.GetBytes($json)
      $Context.Response.ContentType     = 'application/json'
      $Context.Response.ContentLength64 = $bytes.Length
      $Context.Response.OutputStream.Write($bytes, 0, $bytes.Length)
      $Context.Response.OutputStream.Close()
  }



  # Helper function to consistently return an empty JSON array
  function returnRespondEmpty {
      param(
          [Parameter(Mandatory)][object]$Context,
          [Parameter(Mandatory)][ValidateRange(100,599)][int]$StatusCode = 500
      )
      # Always return an empty JSON array body
      $empty = [Text.Encoding]::UTF8.GetBytes("[]")

      # Set the desired status code and headers
      $Context.Response.StatusCode      = $StatusCode
      $Context.Response.ContentType     = 'application/json'
      $Context.Response.ContentLength64 = $empty.Length

      # Write and close
      $Context.Response.OutputStream.Write($empty, 0, $empty.Length)
      $Context.Response.OutputStream.Close()
  }



  # On-boarding handlers
  function Handle-SetSVSPowerPlan {
      param($Context)

      # 1) call into your module
      Set-SVSPowerPlan

      # 2) log & write back a simple text response
      Write-LogHybrid "PowerPlan set" "Success" "OnBoard"
      Respond-Text  $Context "PowerPlan applied"
  }

  function Handle-InstallSVSMSP {
      param($Context)
      Write-LogHybrid "HTTP trigger: Handle-InstallSVSMSP" "Info" "OnBoard"
      try {
          Install-SVSMSP -InstallToolkit
          Respond-Text $Context "SVSMSP Module installed/updated."
      } catch {
          Write-LogHybrid "Error in Install-SVSMSP: $_" "Error" "OnBoard"
          Respond-Text $Context "ERROR: $_"
      }
  }

  function Handle-InstallCyberQP {
      param($Context)

      # 1) call into your module
      Install-CyberQP

      # 2) log & write back a simple text response
      Write-LogHybrid "CyberQP installed" "Success" "OnBoard"
      Respond-Text $Context "CyberQP installed"
  }

  function Handle-InstallThreatLocker {
      param($Context)

      # 1) call into your module
      Install-ThreatLocker

      # 2) log & write back a simple text response
      Write-LogHybrid "ThreatLocker installed" "Success" "OnBoard"
      Respond-Text $Context "ThreatLocker installed"
  }

  function Handle-InstallRocketCyber {
      param($Context)

      # 1) call into your module
      Install-RocketCyber

      # 2) log & write back a simple text response
      Write-LogHybrid "RocketCyber installed" "Success" "OnBoard"
      Respond-Text $Context "RocketCyber installed"
  }

  function Handle-InstallSVSHelpDesk {
      param($Context)

      # 1) call into your module
      Install-SVSHelpDesk

      # 2) log & write back a simple text response
      Write-LogHybrid "SVS HelpDesk installed" "Success" "OnBoard"
      Respond-Text $Context "SVS HelpDesk installed"
  }

  function Handle-InstallDattoRMM {
    param($Context)
    $req  = $Context.Request
    $resp = $Context.Response

    if ($req.HttpMethod -ne 'POST') {
      $resp.StatusCode = 405; $resp.ContentType = 'text/plain'
      $resp.OutputStream.Write([Text.Encoding]::UTF8.GetBytes('Use POST'),0,7)
      $resp.OutputStream.Close(); return
    }

    # parse JSON body
    $body = (New-Object IO.StreamReader $req.InputStream).ReadToEnd()
    $data = $body | ConvertFrom-Json
    $checked = $data.checkedValues
    $uid     = $data.UID
    $name    = $data.Name

    try {
      Install-DattoRMM `
        -ApiUrl       $Global:ApiUrl `
        -ApiKey       $Global:ApiKey `
        -ApiSecretKey $Global:ApiSecretKey `
        -SiteUID      $uid `
        -SiteName     $name `
        -PushSiteVars:($checked -contains 'inputVar') `
        -InstallRMM:   ($checked -contains 'rmm') `
        -SaveCopy:     ($checked -contains 'exe')

      Write-LogHybrid "RMM install triggered for $name" "Success" "DattoRMM"
      $resp.StatusCode   = 200
      $responseString    = "Triggered DattoRMM for $name"
    }
    catch {
      Write-LogHybrid "Error in Install-DattoRMM: $_" "Error" "DattoRMM"
      $resp.StatusCode = 500
      $responseString  = "ERROR: $($_.Exception.Message)"
    }

    $b = [Text.Encoding]::UTF8.GetBytes($responseString)
    $resp.ContentType     = 'text/plain'
    $resp.ContentLength64 = $b.Length
    $resp.OutputStream.Write($b,0,$b.Length)
    $resp.OutputStream.Close()
  }


  # Off-boarding handlers
  function Handle-UninstallCyberQP {
      param($Context)

      # 1) call into your module
      Uninstall-CyberQP

      Write-LogHybrid "CyberQP uninstalled" "Success" "OffBoard"
      Respond-Text $Context "CyberQP uninstalled"
  }

  function Cleanup-SVSMSP {
      param($Context)
      Write-LogHybrid "SVSMSP cleaned up" "Success" "OffBoard"
      Respond-Text $Context "SVSMSP cleaned up"
  }

  # Tweaks handler
  function Disable-Animations {
      param($Context)
      Write-LogHybrid "Animations disabled" "Success" "Tweaks"
      Respond-Text $Context "Animations disabled"
  }

  # SVSApps handler
  function Install-WingetLastPass {
      param($Context)
      Write-LogHybrid "Winget LastPass installed" "Success" "SVSApps"
      Respond-Text $Context "Winget LastPass installed"
  }

  #endregion

  # Sends the HTML for a given page or invokes a task handler
  function Dispatch-Request {
      param($Context)

      # figure out the path
      $path = $Context.Request.Url.AbsolutePath.TrimStart('/')

      # ---- Shutdown handler ----
      if ($path -eq 'quit') {
          Write-LogHybrid "Shutdown requested" "Info" "Server"
          Respond-Text $Context "Server shutting down."
          # stop the listener loop
          $Global:Listener.Stop()
          return
      }

      # ---- Fetch Sites endpoint ----
      if ($Context.Request.HttpMethod -eq 'POST' -and $path -eq 'getpw') {
          Handle-FetchSites $Context
          return
      }

      # ---- Serve UI pages ----
      if ($path -in @('', 'onboard', 'offboard', 'tweaks', 'SVSApps')) {
          $page = if ($path -eq '') { 'onboard' } else { $path }
          $html = Get-UIHtml -Page $page
          Respond-HTML $Context $html
          return
      }
          
      # ---- Task invocation ----
      $task = $Global:Tasks | Where-Object Name -EQ $path
      if ($task) {
          & $task.HandlerFn $Context
          return
      }

      # ---- 404 ----
      $Context.Response.StatusCode = 404
      Respond-Text $Context '404 - Not Found'
  }


  #endregion region globalsetting

  #region ScriptMonkey run silently Entrypoint

  # ─────────────────────────────────────────────────────────────────────────
  #  3) MAIN LOGIC (Toolkit vs DattoFetch vs DattoInstall vs UI)
  # ─────────────────────────────────────────────────────────────────────────

    switch ($PSCmdlet.ParameterSetName) {
      'Toolkit' {
        Write-LogHybrid "Toolkit-only mode" Info Startup
        Install-SVSMSP -InstallToolkit
        return
      }

      'Cleanup' {
        Write-LogHybrid "Running Toolkit cleanup mode" Info Startup
        Install-SVSMSP -Cleanup
        return
      }
      
      # ───────────────────────────────────────────────────────────
      # 2) If user only wants the site list, do that and exit
      # ───────────────────────────────────────────────────────────

      'DattoFetch' {
          Write-LogHybrid "Fetching site list only…" Info DattoAuth
          $sites = Get-DattoRmmSites -Password $N8nPassword

          if ($SaveSitesOnly) {
              # If SaveSitesOnly is true, save the output to a file on desktop 
              $desktopPath = [System.Environment]::GetFolderPath('Desktop')
              $filePath = Join-Path -Path $desktopPath -ChildPath $OutputFile

              $ext = [IO.Path]::GetExtension($OutputFile).ToLower()
              if ($ext -eq '.json') {
                  # Save the file to the desktop using the full path
                  $sites | ConvertTo-Json -Depth 3 | Out-File -FilePath $filePath -Encoding UTF8
              } else {
                  # Save the file to the desktop using the full path
                  $sites | Export-Csv -Path $filePath -NoTypeInformation -Encoding UTF8
              }

              Write-LogHybrid "Wrote $($sites.Count) sites to $filePath" Success DattoAuth
            
          } else {
              # If SaveSitesOnly is not true, just fetch sites (for UI purposes or silent mode without saving)
              Write-LogHybrid "Sites fetched successfully, but not saved." Success DattoAuth
          }
          return
      }


      # ────────────────────────────────────────────
      # 3) Invoke the existing Install-DattoRMM cmdlet
      # ────────────────────────────────────────────
      
      'DattoInstall' {
      Write-LogHybrid "Headless DattoRMM deploy" Info DattoAuth
      if ($PSCmdlet.ShouldProcess("Datto site '$SiteName'", "Headless install")) {
        Install-DattoRMM `
          -ApiUrl       $Global:ApiUrl `
          -ApiKey       $Global:ApiKey `
          -ApiSecretKey $Global:ApiSecretKey `
          -SiteUID      $SiteUID `
          -SiteName     $SiteName `
          -PushSiteVars:$PushSiteVars `
          -InstallRMM:$InstallRMM `
          -SaveCopy:$SaveCopy
      }

      return

      }

      'UI' {
      Write-LogHybrid "Launching UI" Info Startup
      Write-Host "Starting ScriptMonkey UI on http://localhost:$Port/" -ForegroundColor Cyan
      Start-Process "msedge.exe" -ArgumentList "--app=http://localhost:$Port"
      Start-Server    # blocks until you click Exit
      return
    }

    }
  #endregion ScriptMonkey run silently Entrypoint
    
  Write-Host "🛠️  SAMY - Script Automation Monkey (Yeah!)" -ForegroundColor Cyan
  Write-Host "ParameterSetName: $($PSCmdlet.ParameterSetName)" -ForegroundColor Yellow

  #region — guarantee NuGet provider is present without prompting

  # ─── Top of script ───
  Import-Module PackageManagement -Force -ErrorAction SilentlyContinue | Out-Null
  Import-Module PowerShellGet       -Force -ErrorAction SilentlyContinue | Out-Null

  # ─── ensure TLS 1.2 + no prompts ───
  [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
  $ProgressPreference = 'SilentlyContinue'
  $ConfirmPreference  = 'None'

  # check if NuGet exists (no output—assigned to $nuget)
  $nuget = Get-PackageProvider -Name NuGet -ListAvailable -ErrorAction SilentlyContinue

  if (-not $nuget) {
      # install it (again, assignment suppresses the table)
      Install-PackageProvider `
        -Name NuGet `
        -MinimumVersion 2.8.5.201 `
        -Force `
        -Confirm:$false

      
  # re-query just for version info
      $found = Get-PackageProvider -Name NuGet -ListAvailable
      Write-Host "Installed NuGet provider v$($found.Version)" -ForegroundColor Green
  }
  else {
      Write-Host "NuGet provider already present (v$($found.Version))" -ForegroundColor DarkGray
  }

  # now import it silently
  Import-PackageProvider -Name NuGet -Force -ErrorAction SilentlyContinue | Out-Null

  # ensure trust PSGallery without its own output (so you don't get “untrusted repository” prompt
  $gallery = Get-PSRepository -Name PSGallery -ErrorAction SilentlyContinue
  if ($gallery.InstallationPolicy -ne 'Trusted') {
      Set-PSRepository `
        -Name PSGallery `
        -InstallationPolicy Trusted `
        -ErrorAction SilentlyContinue | Out-Null

      Write-Host "PSGallery marked as Trusted" -ForegroundColor Green
  }

  #endregion

  

  # If we got here, it's the UI set—launch browser + listener:
  # ——— UI fallback starts here ———
      Write-LogHybrid "Launching UI" Info Startup


 


  #region Install-DattoRMM-Helper
  function Install-DattoRMM-Helper {
      param (
          [string]$ApiUrl,
          [string]$ApiKey,
          [string]$ApiSecretKey,
          [switch]$FetchSitesOnly,
          [string]$SiteName,
          [string]$SiteUID
      )
      if (-not $ApiUrl -or -not $ApiKey -or -not $ApiSecretKey) {
          Write-LogHybrid -Message "Missing required parameters. Please provide ApiUrl, ApiKey, and ApiSecretKey." -Level "Error" -LogToEvent
          return
      }
      [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
      Write-LogHybrid -Message "Fetching OAuth token..." -Level "Info"
      try {
          $securePassword = ConvertTo-SecureString -String 'public' -AsPlainText -Force
          $apiGenToken = Invoke-WebRequest -Credential (New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ('public-client', $securePassword)) `
              -Uri ('{0}/auth/oauth/token' -f $ApiUrl) `
              -Method 'POST' `
              -ContentType 'application/x-www-form-urlencoded' `
              -Body ('grant_type=password&username={0}&password={1}' -f $ApiKey, $ApiSecretKey) `
              | ConvertFrom-Json
          $requestToken = $apiGenToken.access_token
          Write-LogHybrid -Message "OAuth token fetched successfully." -Level "Success" -LogToEvent
      } catch {
          Write-LogHybrid -Message "Failed to fetch OAuth token. Details: $($_.Exception.Message)" -Level "Error" -LogToEvent
          return
      }
      $getHeaders = @{"Authorization" = "Bearer $requestToken"}
      if ($FetchSitesOnly) {
          Write-Host "Fetching list of sites from the Datto RMM API..." -ForegroundColor Cyan
          try {
              $getHeaders = @{"Authorization" = "Bearer $requestToken" }
              $getSites = Invoke-WebRequest -Uri "$ApiUrl/api/v2/account/sites" -Method Get -Headers $getHeaders -ContentType "application/json"
              $sitesJson = $getSites.Content | ConvertFrom-Json
              $siteList = $sitesJson.sites | ForEach-Object {
                  [PSCustomObject]@{
                      Name = $_.name
                      UID  = $_.uid
                  }
              }
              Write-Host "Successfully fetched list of sites." -ForegroundColor Green
              return $siteList
          }
          catch {
              Write-Host "Failed to fetch sites from the API. Details: $($_.Exception.Message)" -ForegroundColor Red
              return
          }
      }
  }
  #endregion

  

  # POST /getpw → read JSON body, call helper, return JSON


 

  #region HTTP Listener & Routing

  # Handle shutdown command
  if ($path -eq 'quit') {
      Write-LogHybrid "Shutdown requested" "Info" "Server"
      Respond-Text $Context "Server shutting down."
      # This will break out of the while loop in Start-Server
      $Global:Listener.Stop()
      return
  }

  
  #endregion


 }

  if ($MyInvocation.InvocationName -eq '.') {
    # dot-sourced, don't invoke
} elseif ($PSCommandPath) {
    # script was saved and run directly
    Invoke-ScriptMonkey @PSBoundParameters
} else {
    # iwr | iex fallback
     if ($args.Count -gt 0) {
        # Convert -Param value -Switch into a hashtable for splatting
        $namedArgs = @{}
        for ($i = 0; $i -lt $args.Count; $i++) {
            if ($args[$i] -is [string] -and $args[$i].StartsWith('-')) {
                $key = $args[$i].TrimStart('-')
                $next = $args[$i + 1]
                if ($next -and ($next -notlike '-*')) {
                    $namedArgs[$key] = $next
                    $i++  # Skip next one, it's the value
                } else {
                    $namedArgs[$key] = $true
                }
            }
        }
        Invoke-ScriptMonkey @namedArgs
    } else {
        Invoke-ScriptMonkey
    }
}


