<#
.SYNOPSIS
    Script Automation Monkey (SAMY) main entry point.

.DESCRIPTION
    This file is now the orchestration layer only. It handles:
      - Execution policy bypass for restricted environments
      - Global config (branch, repo base, URLs)
      - Loading subsystem scripts (logging, SVSMSP, Datto, printers, UI, HTTP, etc.)
      - Exposing Invoke-ScriptAutomationMonkey with parameter sets for:
          - UI
          - Toolkit-only install
          - Toolkit cleanup
          - Headless Datto site fetch
          - Headless Datto install
          - Headless offboarding
      - The iwr | iex glue at the bottom so remote calls still work.

    All heavy logic lives in the Samy.*.ps1 subsystem files that are dot-sourced or
    loaded from your Git repo, now under a "module" subfolder.
#>

#region Safely bypass Restricted Execution Policy

if ($ExecutionContext.SessionState.LanguageMode -ne 'FullLanguage' -or
    (Get-ExecutionPolicy) -eq 'Restricted') {

    Write-Host "[Info] Relaunching with ExecutionPolicy Bypass..." -ForegroundColor Yellow

    if ($PSCommandPath) {
        powershell.exe -NoProfile -ExecutionPolicy Bypass -File "`"$PSCommandPath`""
    }
    else {
        powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "& { iwr 'https://samy.svstools.com' -UseBasicParsing | iex }"
    }

    exit
}

#endregion Safely bypass Restricted Execution Policy

#region Global defaults and config

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$ProgressPreference = 'SilentlyContinue'
$ConfirmPreference  = 'None'

# Default HTTP listening port for UI
$Script:SamyPort = 8082

# SAMY asset config (change branch or base once and it updates everything)
$Script:SamyBranch   = 'beta'  # or 'main'
$Script:SamyRepoBase = 'https://git.svstools.ca/SVS_Public_Repo/SAMY/raw/branch'

# Top level assets
$Script:SamyTopLogoUrl = "$Script:SamyRepoBase/$Script:SamyBranch/SVS_logo.svg"
$Script:SamyBgLogoUrl  = "$Script:SamyRepoBase/$Script:SamyBranch/SAMY.png"
$Script:SamyFaviconUrl = "$Script:SamyRepoBase/$Script:SamyBranch/SVS_Favicon.ico"
$Script:SamyCssUrl     = "$Script:SamyRepoBase/$Script:SamyBranch/samy.css?raw=1"
$Script:SamyJsUrl      = "$Script:SamyRepoBase/$Script:SamyBranch/samy.js?raw=1"

# Datto webhook URL (used by Datto subsystem)
$Global:DattoWebhookUrl = 'https://automate.svstools.ca/webhook/svsmspkit'

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

#endregion Global defaults and config

#region Module loader

function Import-SamyModule {
    <#
    .SYNOPSIS
        Loads a SAMY subsystem script from local disk or from the Git repo.

    .DESCRIPTION
        Local:
          - Prefer .\module\<Name>
          - Fallback to .\<Name>

        Remote (iwr | iex):
          - Try .../module/<Name> first
          - If that 404s, fallback to .../<Name>
    #>
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][string]$Name
    )

    # 1) Local dev mode: script saved to disk
    if ($PSCommandPath) {
        $moduleRoot      = Join-Path -Path $PSScriptRoot -ChildPath 'module'
        $localModulePath = Join-Path -Path $moduleRoot   -ChildPath $Name
        $localRootPath   = Join-Path -Path $PSScriptRoot -ChildPath $Name

        if (Test-Path $localModulePath) {
            . $localModulePath
            return
        }

        if (Test-Path $localRootPath) {
            . $localRootPath
            return
        }
    }

    # 2) Remote mode (iwr | iex): pull module from repo
    $base = "{0}/{1}" -f $Script:SamyRepoBase, $Script:SamyBranch

    $primaryUrl  = "{0}/module/{1}" -f $base, $Name
    $fallbackUrl = "{0}/{1}"        -f $base, $Name

    function Invoke-LoadUrl {
        param(
            [Parameter(Mandatory)][string]$Url,
            [Parameter(Mandatory)][string]$ModuleName
        )

        $resp    = Invoke-WebRequest -Uri $Url -UseBasicParsing -ErrorAction Stop
        $content = $resp.Content
        if (-not $content) {
            Write-Host ("[Error] Module {0} from {1} returned empty content." -f $ModuleName, $Url) -ForegroundColor Red
            throw "Empty module content."
        }
        Invoke-Expression $content
    }

    try {
        # Try /module/<Name> first
        Invoke-LoadUrl -Url $primaryUrl -ModuleName $Name
    }
    catch [System.Net.WebException] {
        $response   = $_.Exception.Response
        $statusCode = $null
        if ($response -and $response.StatusCode) {
            $statusCode = [int]$response.StatusCode
        }

        if ($statusCode -eq 404) {
            Write-Host ("[Info] Module {0} not found at {1} (404). Trying fallback {2}." -f $Name, $primaryUrl, $fallbackUrl) -ForegroundColor Yellow
            try {
                Invoke-LoadUrl -Url $fallbackUrl -ModuleName $Name
            }
            catch {
                Write-Host ("[Error] Failed to load module {0} from fallback {1}: {2}" -f $Name, $fallbackUrl, $_.Exception.Message) -ForegroundColor Red
                throw
            }
        }
        else {
            Write-Host ("[Error] Failed to load module {0} from {1}: {2}" -f $Name, $primaryUrl, $_.Exception.Message) -ForegroundColor Red
            throw
        }
    }
    catch {
        if (-not ($_ -is [System.Net.WebException])) {
            Write-Host ("[Error] Failed to load module {0}: {1}" -f $Name, $_.Exception.Message) -ForegroundColor Red
        }
        throw
    }
}


# Load subsystems in a predictable order
Import-SamyModule -Name 'Samy.Logging.ps1'       # Write-LogHelper, Write-LogHybrid
Import-SamyModule -Name 'Samy.SVSBootstrap.ps1'  # Install-SVSMSP, cleanup, NuGet bootstrap
Import-SamyModule -Name 'Samy.UI.ps1'            # $Global:SamyTasks, UI HTML, Get-UIHtml, etc.
Import-SamyModule -Name 'Samy.Datto.ps1'         # Install-DattoRMM, Datto HTTP handlers
Import-SamyModule -Name 'Samy.Printers.ps1'      # Printer config, drivers, HTTP handlers
Import-SamyModule -Name 'Samy.Apps.ps1'          # Winget app handlers (Chrome, Acrobat, etc.)
Import-SamyModule -Name 'Samy.Offboard.ps1'      # Offboarding handlers and full offboard flow
Import-SamyModule -Name 'Samy.Onboarding.ps1'    # Onboarding handlers, RenameComputer, etc.
Import-SamyModule -Name 'Samy.Http.ps1'          # Send-Text/JSON, Dispatch-Request, Start-SamyHttpServer

#endregion Module loader

#region Simple helpers that are local to this main file

function Test-ComputerName {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Name
    )

    if ([string]::IsNullOrWhiteSpace($Name)) { return $false }
    if ($Name.Length -gt 15) { return $false }
    if ($Name -notmatch '^[A-Za-z0-9-]+$') { return $false }
    return $true
}

#endregion Simple helpers

#region Main entry point: Invoke-ScriptAutomationMonkey

function Invoke-ScriptAutomationMonkey {
    [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,

        # Headless offboarding
        [Parameter(Mandatory, ParameterSetName = 'Offboard')]
        [switch]$Offboard,

        # Datto headless mode shared params
        [Parameter(Mandatory, ParameterSetName = 'DattoFetch')]
        [Parameter(Mandatory, ParameterSetName = 'DattoInstall')]
        [switch]$UseWebhook,

        [Parameter(Mandatory, ParameterSetName = 'DattoFetch')]
        [Parameter(Mandatory, ParameterSetName = 'DattoInstall')]
        [string]$WebhookPassword,

        [string]$WebhookUrl = $Global:DattoWebhookUrl,

        # DattoFetch only
        [Parameter(ParameterSetName = 'DattoFetch')]
        [switch]$FetchSites,

        [Parameter(ParameterSetName = 'DattoFetch')]
        [switch]$SaveSitesList,

        [Parameter(ParameterSetName = 'DattoFetch')]
        [ValidatePattern('\.csv$|\.json$')]
        [string]$OutputFile = 'datto_sites.csv',

        # DattoInstall only
        [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
    )

    switch ($PSCmdlet.ParameterSetName) {

        'Toolkit' {
            Write-LogHybrid "Toolkit-only mode requested." Info Startup -LogToEvent
            Install-SVSMSP -InstallToolkit
            return
        }

        'Cleanup' {
            Write-LogHybrid "Toolkit cleanup requested." Info Startup -LogToEvent
            Install-SVSMSP -Cleanup
            return
        }

        'DattoFetch' {
            Write-LogHybrid "DattoFetch mode: fetching site list." Info DattoAuth -LogToEvent

            $sites = Install-DattoRMM `
                -UseWebhook `
                -WebhookPassword $WebhookPassword `
                -WebhookUrl $WebhookUrl `
                -FetchSites `
                -SaveSitesList:$SaveSitesList `
                -OutputFile $OutputFile

            $count = if ($sites) { $sites.Count } else { 0 }
            Write-LogHybrid "DattoFetch completed with $count sites." Success DattoAuth -LogToEvent
            return
        }

        'DattoInstall' {
            Write-LogHybrid "DattoInstall mode: headless RMM deploy to '$SiteName'." Info DattoAuth -LogToEvent

            if ($PSCmdlet.ShouldProcess("Datto site '$SiteName'", "Headless Install-DattoRMM")) {
                Install-DattoRMM `
                    -UseWebhook `
                    -WebhookPassword $WebhookPassword `
                    -WebhookUrl $WebhookUrl `
                    -SiteUID $SiteUID `
                    -SiteName $SiteName `
                    -PushSiteVars:$PushSiteVars `
                    -InstallRMM:$InstallRMM `
                    -SaveCopy:$SaveCopy
            }

            Write-LogHybrid "DattoInstall completed for '$SiteName'." Success DattoAuth -LogToEvent
            return
        }

        'Offboard' {
            Write-LogHybrid "Headless offboarding requested." Info OffBoard -LogToEvent
            Invoke-SamyFullOffboard
            return
        }

        'UI' {
            # Default UI mode: launch browser and start HTTP listener
            $port = $Script:SamyPort
            $url  = "http://localhost:$port/"

            Write-LogHybrid "Starting ScriptAutomationMonkey UI on $url" Info Startup -LogToEvent

            # Resolve Edge path explicitly
            $edgeCandidates = @(
                "${env:ProgramFiles(x86)}\Microsoft\Edge\Application\msedge.exe",
                "$env:ProgramFiles\Microsoft\Edge\Application\msedge.exe"
            )

            $edgePath = $edgeCandidates |
                Where-Object { $_ -and (Test-Path $_) } |
                Select-Object -First 1

            if (-not $edgePath) {
                $cmd = Get-Command -Name 'msedge.exe' -ErrorAction SilentlyContinue
                if ($cmd) { $edgePath = $cmd.Path }
            }

            # Launch Edge (app mode) or default browser in a background job
            Start-Job -Name 'OpenScriptAutomationMonkeyUI' -ScriptBlock {
                param([string]$u, [string]$edgeExe)
                Start-Sleep -Milliseconds 400
                try {
                    if ($edgeExe -and (Test-Path $edgeExe)) {
                        Start-Process -FilePath $edgeExe -ArgumentList @('--new-window', "--app=$u")
                    }
                    else {
                        Start-Process -FilePath $u
                    }
                }
                catch { }
            } -ArgumentList $url, $edgePath | Out-Null

            # Start HTTP listener loop (implemented in Samy.Http.ps1)
            Start-SamyHttpServer -Port $port
            return
        }
    }
}

#endregion Main entry point: Invoke-ScriptAutomationMonkey

#region Auto invoke for direct execution and iwr | iex

if ($MyInvocation.InvocationName -eq '.') {
    # Dot-sourced, just expose functions
}
elseif ($PSCommandPath) {
    # Script was saved and run directly
    Invoke-ScriptAutomationMonkey @PSBoundParameters
}
else {
    # iwr | iex fallback with simple -Param value parsing
    if ($args.Count -gt 0) {
        $namedArgs = @{}
        for ($i = 0; $i -lt $args.Count; $i++) {
            $current = $args[$i]
            if ($current -is [string] -and $current.StartsWith('-')) {
                $key  = $current.TrimStart('-')
                $next = $null
                if ($i + 1 -lt $args.Count) {
                    $next = $args[$i + 1]
                }

                if ($next -and ($next -notlike '-*')) {
                    $namedArgs[$key] = $next
                    $i++
                }
                else {
                    $namedArgs[$key] = $true
                }
            }
        }

        Invoke-ScriptAutomationMonkey @namedArgs
    }
    else {
        Invoke-ScriptAutomationMonkey
    }
}

#endregion Auto invoke for direct execution and iwr | iex
