<#
.SYNOPSIS
    One-time setup for AnywhereNow UCC Site Creator — deploys the SPFx package to either
    the tenant App Catalog or a site collection App Catalog.
    
.DESCRIPTION
    This script performs one-time configuration:
    - Deploys the SPFx package to the tenant App Catalog (default), OR
    - Deploys to a site collection App Catalog when -SiteCollectionAppCatalogUrl is specified
    - Makes the solution available tenant-wide (tenant mode only, unless -SkipTenantWide)
    - After running this, UCC.Site.Creator.ps1 can create sites without deploying the package each time
    
.PARAMETER TenantAdminUrl
    The SharePoint tenant admin URL (e.g., https://contoso-admin.sharepoint.com)
    If not provided, will be derived from TenantUrl.
    Not required when using -SiteCollectionAppCatalogUrl.
    
.PARAMETER TenantUrl
    The SharePoint tenant URL (e.g., https://contoso.sharepoint.com)
    Used to derive admin URL if TenantAdminUrl is not provided.

.PARAMETER SiteCollectionAppCatalogUrl
    URL of the site collection where a site collection App Catalog has been activated.
    When specified, the SPFx package is deployed to that site collection catalog instead of
    the tenant catalog. The package will only be available to that site collection and its subsites.
    Example: https://contoso.sharepoint.com/sites/my-ucc
    Note: The site collection App Catalog must already be activated on the target site.

.PARAMETER Interactive
    Use interactive browser-based authentication.
    
.PARAMETER ClientId
    The Azure AD App Registration Client ID for authentication.
    
.PARAMETER CertificatePath
    Path to the certificate file (.pfx) for certificate-based authentication.
    
.PARAMETER CertificatePassword
    Password for the certificate file (SecureString).
    
.PARAMETER SkipTenantWide
    Tenant mode only: deploy to App Catalog but don't make tenant-wide available.
    Sites will need the app installed individually.
    Has no effect when -SiteCollectionAppCatalogUrl is used.

.PARAMETER Force
    Force re-deployment even if package is already deployed.

.EXAMPLE
    # Tenant App Catalog — interactive authentication
    .\Deploy-UccDashboard.ps1 -TenantUrl "https://contoso.sharepoint.com" -Interactive -ClientId "your-client-id"
    
.EXAMPLE
    # Tenant App Catalog — certificate-based authentication
    .\Deploy-UccDashboard.ps1 -TenantUrl "https://contoso.sharepoint.com" -ClientId "your-client-id" -CertificatePath ".\cert.pfx" -CertificatePassword $securePassword

.EXAMPLE
    # Site Collection App Catalog — certificate-based authentication
    .\Deploy-UccDashboard.ps1 -SiteCollectionAppCatalogUrl "https://contoso.sharepoint.com/sites/my-ucc" -ClientId "your-client-id" -CertificatePath ".\cert.pfx" -CertificatePassword $securePassword

.NOTES
    Version: 8.4.1.8
    This script only needs to run ONCE per customer tenant (or once per site collection when
    using a site collection App Catalog).
    After running, use UCC.Site.Creator.ps1 to create individual UCC sites.
#>

[CmdletBinding()]
param(
    [Parameter(Mandatory = $false)]
    [string]$TenantAdminUrl,
    
    [Parameter(Mandatory = $false)]
    [string]$TenantUrl,

    [Parameter(Mandatory = $false)]
    [string]$SiteCollectionAppCatalogUrl,
    
    [Parameter(Mandatory = $false)]
    [switch]$Interactive,
    
    [Parameter(Mandatory = $false)]
    [string]$ClientId,
    
    [Parameter(Mandatory = $false)]
    [string]$CertificatePath,
    
    [Parameter(Mandatory = $false)]
    [SecureString]$CertificatePassword,
    
    [Parameter(Mandatory = $false)]
    [switch]$SkipTenantWide,
    
    [Parameter(Mandatory = $false)]
    [switch]$Force
)

$ErrorActionPreference = "Stop"

# Script version
$scriptVersion = "8.4.1.8"

# Colors for output
function Write-Info($message) { Write-Host "Info : $message" -ForegroundColor Cyan }
function Write-Success($message) { Write-Host "Success : $message" -ForegroundColor Green }
function Write-Warn($message) { Write-Host "Warning : $message" -ForegroundColor Yellow }
function Write-Err($message) { Write-Host "Error : $message" -ForegroundColor Red }

# Determine deployment mode up front
$useSiteCollectionCatalog = -not [string]::IsNullOrEmpty($SiteCollectionAppCatalogUrl)

Write-Host ""
Write-Host "============================================================" -ForegroundColor Cyan
Write-Host "  AnywhereNow UCC - Deploy SPFx Package v$scriptVersion" -ForegroundColor Cyan
if ($useSiteCollectionCatalog) {
    Write-Host "  Mode: Site Collection App Catalog" -ForegroundColor Cyan
} else {
    Write-Host "  Mode: Tenant App Catalog" -ForegroundColor Cyan
}
Write-Host "============================================================" -ForegroundColor Cyan
Write-Host ""

# Determine script root
$RootPath = Split-Path -Parent $MyInvocation.MyCommand.Path

# Check for SPFx package
$spfxPackagePath = Join-Path $RootPath "SPFx\sharepoint\solution\ucc-dashboard-spfx.sppkg"
if (-not (Test-Path $spfxPackagePath)) {
    Write-Err "SPFx package not found at: $spfxPackagePath"
    Write-Err ""
    Write-Err "The pre-built SPFx package is required."
    Write-Err "Please ensure the package is included in the distribution."
    exit 1
}
Write-Info "Found SPFx package: $spfxPackagePath"

# Validate parameters
if ($useSiteCollectionCatalog) {
    # Site collection catalog mode — only need the site collection URL, no tenant admin required
    Write-Info "Target site collection App Catalog: $SiteCollectionAppCatalogUrl"
    # Derive tenantName for certificate auth (needed even in site collection mode)
    if ($SiteCollectionAppCatalogUrl -match "https://([^.]+)\.sharepoint\.com") {
        $tenantName = $matches[1]
    }
    elseif ($TenantUrl -match "https://([^.]+)\.sharepoint\.com") {
        $tenantName = $matches[1]
    }
    else {
        Write-Err "Could not derive tenant name. Please also provide -TenantUrl."
        exit 1
    }
}
else {
    # Tenant catalog mode — need tenant admin URL
    if (-not $TenantAdminUrl -and -not $TenantUrl) {
        Write-Err "Either -TenantAdminUrl or -TenantUrl must be provided for tenant catalog deployment."
        Write-Err "For site collection catalog deployment use -SiteCollectionAppCatalogUrl instead."
        exit 1
    }
    if (-not $TenantAdminUrl) {
        if ($TenantUrl -match "https://([^.]+)\.sharepoint\.com") {
            $tenantName = $matches[1]
            $TenantAdminUrl = "https://$tenantName-admin.sharepoint.com"
            Write-Info "Derived tenant admin URL: $TenantAdminUrl"
        }
        else {
            Write-Err "Could not derive admin URL from tenant URL. Please provide -TenantAdminUrl explicitly."
            exit 1
        }
    }
    else {
        if ($TenantAdminUrl -match "https://([^-]+)-admin\.sharepoint\.com") {
            $tenantName = $matches[1]
        }
        elseif ($TenantUrl -match "https://([^.]+)\.sharepoint\.com") {
            $tenantName = $matches[1]
        }
    }
}

# Helper: connect to a URL using the supplied auth parameters
function Connect-ToUrl {
    param([string]$Url)
    if ($Interactive) {
        if ($ClientId) { Connect-PnPOnline -Url $Url -Interactive -ClientId $ClientId }
        else            { Connect-PnPOnline -Url $Url -Interactive }
    }
    elseif ($CertificatePath) {
        if (-not $ClientId) { Write-Err "ClientId is required for certificate-based authentication."; exit 1 }
        Connect-PnPOnline -Url $Url -ClientId $ClientId -CertificatePath $CertificatePath -CertificatePassword $CertificatePassword -Tenant "$tenantName.onmicrosoft.com"
    }
    else {
        Write-Err "No authentication method specified. Use -Interactive or provide certificate parameters."
        exit 1
    }
}

# Import PnP PowerShell
$pnpModulePath = Join-Path $RootPath "bundle\PnP.PowerShell"
if (Test-Path $pnpModulePath) {
    Write-Info "Loading bundled PnP.PowerShell module..."
    Import-Module "$pnpModulePath\PnP.PowerShell.psd1" -Force -DisableNameChecking
}
else {
    Write-Info "Using installed PnP.PowerShell module..."
    if (-not (Get-Module -ListAvailable -Name PnP.PowerShell)) {
        Write-Err "PnP.PowerShell module not found. Please install it or ensure the bundle folder exists."
        exit 1
    }
    Import-Module PnP.PowerShell -Force -DisableNameChecking
}

# Prompt for certificate password if not provided
if ($CertificatePath -and -not $Interactive -and $null -eq $CertificatePassword) {
    Write-Host ""
    $CertificatePassword = Read-Host -Prompt "Enter certificate password" -AsSecureString
    Write-Host ""
}

# ============================================================
# SITE COLLECTION APP CATALOG MODE
# ============================================================
if ($useSiteCollectionCatalog) {

    Write-Info "Connecting to site collection: $SiteCollectionAppCatalogUrl"
    try {
        Connect-ToUrl -Url $SiteCollectionAppCatalogUrl
        Write-Success "Connected to site collection"
    }
    catch {
        Write-Err "Failed to connect: $($_.Exception.Message)"
        exit 1
    }

    # Verify site collection App Catalog exists on this site
    Write-Info "Verifying site collection App Catalog..."
    try {
        $scCatalog = Get-PnPSiteCollectionAppCatalog -CurrentSite -ErrorAction SilentlyContinue
        if (-not $scCatalog) {
            Write-Err "No site collection App Catalog found on: $SiteCollectionAppCatalogUrl"
            Write-Err ""
            Write-Err "To activate a site collection App Catalog, a SharePoint admin must run:"
            Write-Err "  Add-PnPSiteCollectionAppCatalog -Site '$SiteCollectionAppCatalogUrl'"
            exit 1
        }
        Write-Info "Site collection App Catalog confirmed"
    }
    catch {
        Write-Warn "Could not verify site collection App Catalog: $($_.Exception.Message)"
        Write-Warn "Proceeding — the catalog may still be present."
    }

    # Check if already deployed
    $existingApp = $null
    foreach ($identifier in @("ucc-dashboard-spfx-client-side-solution", "ucc-dashboard-spfx", "26A55D4E-07B7-4ADA-A0B8-0A31A8D2E3C1")) {
        try {
            $existingApp = Get-PnPApp -Identity $identifier -Scope Site -ErrorAction SilentlyContinue
            if ($existingApp) {
                Write-Info "Found existing app: $($existingApp.Title) (v$($existingApp.AppCatalogVersion))"
                break
            }
        }
        catch { }
    }

    if ($existingApp -and -not $Force) {
        Write-Warn "SPFx package is already deployed to this site collection App Catalog."
        Write-Warn "  Deployed: $($existingApp.Deployed)"
        Write-Warn "Use -Force to re-deploy and update the package."
        if ($existingApp.Deployed) {
            Write-Success "Site collection setup is already complete!"
            Disconnect-PnPOnline -ErrorAction SilentlyContinue
            exit 0
        }
    }

    # Deploy
    Write-Info "Deploying SPFx package to site collection App Catalog..."
    Write-Info "  Package: $spfxPackagePath"
    try {
        Disconnect-PnPOnline -ErrorAction SilentlyContinue
        Start-Sleep -Seconds 1
        Connect-ToUrl -Url $SiteCollectionAppCatalogUrl

        try { Set-PnPSite -NoScriptSite $false -ErrorAction SilentlyContinue } catch { }

        $result = Add-PnPApp -Path $spfxPackagePath -Scope Site -Overwrite -Publish -ErrorAction Stop
        Write-Success "Package uploaded successfully"
        Write-Info "  App ID: $($result.Id)"
    }
    catch {
        Write-Err "Failed to deploy package: $($_.Exception.Message)"
        exit 1
    }

    # Verify
    Start-Sleep -Seconds 2
    Write-Info "Verifying deployment..."
    try {
        $deployedApp = Get-PnPApp -Identity $result.Id -Scope Site -ErrorAction Stop
        if ($deployedApp.Deployed) {
            Write-Success "Package deployed and available to this site collection!"
            $appVersion = if ($deployedApp.AppCatalogVersion) { $deployedApp.AppCatalogVersion } else { $deployedApp.InstalledVersion }
            if ($appVersion) { Write-Info "  Version: $appVersion" }
        }
        else {
            Write-Info "Package uploaded. Deployment may take a few minutes to propagate."
        }
    }
    catch {
        Write-Warn "Could not verify deployment status: $($_.Exception.Message)"
    }
    # Install the app on all provisioned subsites listed in A365_UCC_Creator
    Write-Host ""
    Write-Info "Reading A365_UCC_Creator list for provisioned subsites..."
    $installedCount = 0
    $installErrors  = 0
    try {
        Connect-ToUrl -Url $SiteCollectionAppCatalogUrl
        $caml = "<View><Query><Where><Eq><FieldRef Name='A365_SiteStatus'/><Value Type='Text'>Provisioned</Value></Eq></Where></Query><ViewFields><FieldRef Name='Title'/><FieldRef Name='A365_SiteURL'/></ViewFields></View>"
        $listItems = @(Get-PnPListItem -List "/Lists/A365_UCC_Creator" -Query $caml -ErrorAction Stop)
        if ($listItems.Count -eq 0) {
            Write-Info "No provisioned subsites found in A365_UCC_Creator list."
            Write-Info "Run this script again after provisioning subsites with UCC.Site.Creator.ps1 -UseSiteCollectionAppCatalog."
        } else {
            Write-Info "Found $($listItems.Count) provisioned subsite(s) - installing..."
            foreach ($item in $listItems) {
                $rawUrl     = $item["A365_SiteURL"]
                # A365_SiteURL is a Hyperlink column - PnP returns a FieldUrlValue object
                $subsiteUrl = if ($rawUrl -is [Microsoft.SharePoint.Client.FieldUrlValue]) {
                    $rawUrl.Url
                } elseif ($rawUrl -and "$rawUrl" -like "*,*") {
                    ("$rawUrl" -split ",")[0].Trim()
                } else { "$rawUrl" }
                if ([string]::IsNullOrEmpty($subsiteUrl)) {
                    Write-Warn "  Skipping - no URL for: $($item["Title"])"
                    continue
                }
                Write-Info "  Installing on: $subsiteUrl"
                try {
                    Disconnect-PnPOnline -ErrorAction SilentlyContinue
                    Connect-ToUrl -Url $subsiteUrl
                    $installedApp = Get-PnPApp -Identity $result.Id -Scope Site -ErrorAction SilentlyContinue
                    if ($installedApp -and $installedApp.InstalledVersion) {
                        Write-Info "    Already installed (v$($installedApp.InstalledVersion)) - updating..."
                        Update-PnPApp -Identity $result.Id -Scope Site -ErrorAction SilentlyContinue
                    } else {
                        Install-PnPApp -Identity $result.Id -Scope Site -Wait -ErrorAction Stop
                    }
                    Write-Success "    Installed: $($item["Title"])"
                    $installedCount++
                } catch {
                    Write-Warn "    Failed on '$($item["Title"])': $($_.Exception.Message)"
                    $installErrors++
                }
            }
            Write-Host ""
            Write-Info "Install complete: $installedCount succeeded, $installErrors failed"
        }
    } catch {
        Write-Warn "Could not read A365_UCC_Creator list: $($_.Exception.Message)"
        Write-Info "Install the app manually via Site Contents > Add an app."
    }

    # Marker file
    $markerPath = Join-Path $RootPath ".sitecollection-setup-complete"
    @{
        SiteCollectionUrl = $SiteCollectionAppCatalogUrl
        Scope             = "SiteCollection"
        InstalledOnSites  = $installedCount
        SetupDate         = (Get-Date -Format "yyyy-MM-dd HH:mm:ss")
        Version           = $scriptVersion
    } | ConvertTo-Json | Set-Content -Path $markerPath -Force
    Write-Info "Created site collection setup marker file"
    Disconnect-PnPOnline -ErrorAction SilentlyContinue
    Write-Host ""
    Write-Host "============================================================" -ForegroundColor Green
    Write-Host "  Site Collection App Catalog Setup Complete!" -ForegroundColor Green
    Write-Host "============================================================" -ForegroundColor Green
    Write-Host ""
    Write-Host "The SPFx package is deployed and installed on $installedCount subsite(s)." -ForegroundColor Green
    Write-Host ""
    Write-Host "Important: The package is scoped to this site collection only." -ForegroundColor Yellow
    Write-Host "  UCC sites on other site collections will need their own deployment." -ForegroundColor Yellow
    Write-Host ""
    Write-Host "Next step: Create UCC sites using UCC.Site.Creator.ps1 -UseSiteCollectionAppCatalog" -ForegroundColor Yellow
    Write-Host ""
}


# ============================================================
# TENANT APP CATALOG MODE (original behaviour)
# ============================================================
else {

    # Connect to tenant admin
    Write-Info "Connecting to tenant admin: $TenantAdminUrl"
    try {
        Connect-ToUrl -Url $TenantAdminUrl
        Write-Success "Connected to tenant admin"
    }
    catch {
        Write-Err "Failed to connect to tenant admin: $($_.Exception.Message)"
        exit 1
    }

    # Get tenant App Catalog URL
    Write-Info "Getting tenant App Catalog URL..."
    try {
        $appCatalogUrl = Get-PnPTenantAppCatalogUrl
        if (-not $appCatalogUrl) {
            Write-Err "No tenant App Catalog found."
            Write-Err "Please create a tenant App Catalog first:"
            Write-Err "  1. Go to SharePoint Admin Center"
            Write-Err "  2. Navigate to More features > Apps"
            Write-Err "  3. Click 'App Catalog' and create one"
            exit 1
        }
        Write-Info "Tenant App Catalog: $appCatalogUrl"
    }
    catch {
        Write-Err "Failed to get App Catalog URL: $($_.Exception.Message)"
        exit 1
    }

    # Connect to App Catalog
    Write-Info "Connecting to App Catalog..."
    Disconnect-PnPOnline -ErrorAction SilentlyContinue
    try {
        Connect-ToUrl -Url $appCatalogUrl
        Write-Success "Connected to App Catalog"
    }
    catch {
        Write-Err "Failed to connect to App Catalog: $($_.Exception.Message)"
        exit 1
    }

    # Check if already deployed
    $existingApp = $null
    foreach ($identifier in @("ucc-dashboard-spfx-client-side-solution", "ucc-dashboard-spfx", "26A55D4E-07B7-4ADA-A0B8-0A31A8D2E3C1")) {
        try {
            $existingApp = Get-PnPApp -Identity $identifier -Scope Tenant -ErrorAction SilentlyContinue
            if ($existingApp) {
                Write-Info "Found existing app: $($existingApp.Title) (v$($existingApp.AppCatalogVersion))"
                break
            }
        }
        catch { }
    }

    if ($existingApp -and -not $Force) {
        Write-Warn "SPFx package is already deployed to tenant App Catalog."
        Write-Warn "  App ID: $($existingApp.Id)"
        $existingVersion = if ($existingApp.AppCatalogVersion) { $existingApp.AppCatalogVersion } else { $existingApp.InstalledVersion }
        if ($existingVersion) { Write-Warn "  Version: $existingVersion" }
        Write-Warn "  Deployed: $($existingApp.Deployed)"
        Write-Warn ""
        Write-Warn "Use -Force to re-deploy and update the package."
        if ($existingApp.Deployed) {
            Write-Success "Tenant setup is already complete!"
            Write-Host ""
            Write-Host "You can now use UCC.Site.Creator.ps1 to create UCC sites." -ForegroundColor Green
            Disconnect-PnPOnline -ErrorAction SilentlyContinue
            exit 0
        }
    }

    # Deploy
    Write-Info "Deploying SPFx package to tenant App Catalog..."
    Write-Info "  Package: $spfxPackagePath"
    Write-Info "  Tenant-wide: $(-not $SkipTenantWide)"
    try {
        Disconnect-PnPOnline -ErrorAction SilentlyContinue
        Start-Sleep -Seconds 1
        Connect-ToUrl -Url $appCatalogUrl

        try { Set-PnPSite -NoScriptSite $false -ErrorAction SilentlyContinue } catch { }

        $deployParams = @{
            Path      = $spfxPackagePath
            Scope     = "Tenant"
            Overwrite = $true
            Publish   = $true
        }
        if (-not $SkipTenantWide) {
            $deployParams.Add("SkipFeatureDeployment", $true)
        }

        $result = Add-PnPApp @deployParams
        Write-Success "Package uploaded successfully"
        Write-Info "  App ID: $($result.Id)"
    }
    catch {
        Write-Err "Failed to deploy package: $($_.Exception.Message)"
        exit 1
    }

    # Verify
    Start-Sleep -Seconds 2
    Write-Info "Verifying deployment..."
    try {
        $deployedApp = Get-PnPApp -Identity $result.Id -Scope Tenant -ErrorAction Stop
        if ($deployedApp.Deployed) {
            Write-Success "Package deployed and available!"
            $appVersion = if ($deployedApp.AppCatalogVersion) { $deployedApp.AppCatalogVersion } else { $deployedApp.InstalledVersion }
            if ($appVersion) { Write-Info "  Version: $appVersion" }
            if (-not $SkipTenantWide) {
                Write-Info "  Tenant-wide: Yes (available to all sites automatically)"
            }
            else {
                Write-Info "  Tenant-wide: No (must be installed per-site)"
            }
        }
        else {
            Write-Info "Package uploaded. Deployment may take a few minutes to propagate."
        }
    }
    catch {
        Write-Warn "Could not verify deployment status: $($_.Exception.Message)"
    }

    # Marker file
    $markerPath = Join-Path $RootPath ".tenant-setup-complete"
    @{
        TenantAdminUrl = $TenantAdminUrl
        AppCatalogUrl  = $appCatalogUrl
        TenantWide     = (-not $SkipTenantWide)
        Scope          = "Tenant"
        SetupDate      = (Get-Date -Format "yyyy-MM-dd HH:mm:ss")
        Version        = $scriptVersion
    } | ConvertTo-Json | Set-Content -Path $markerPath -Force
    Write-Info "Created tenant setup marker file"

    Disconnect-PnPOnline -ErrorAction SilentlyContinue

    Write-Host ""
    Write-Host "============================================================" -ForegroundColor Green
    Write-Host "  Tenant Setup Complete!" -ForegroundColor Green
    Write-Host "============================================================" -ForegroundColor Green
    Write-Host ""
    Write-Host "The SPFx package has been deployed to your tenant App Catalog." -ForegroundColor Green

    if (-not $SkipTenantWide) {
        Write-Host ""
        Write-Host "The solution is available TENANT-WIDE, which means:" -ForegroundColor Cyan
        Write-Host "  - No need to install the app on each site" -ForegroundColor Cyan
        Write-Host "  - The UCC Dashboard web part is automatically available" -ForegroundColor Cyan
    }

    Write-Host ""
    Write-Host "Next step: Create UCC sites using UCC.Site.Creator.ps1" -ForegroundColor Yellow
    Write-Host ""
    Write-Host "Example:" -ForegroundColor Yellow
    Write-Host "  .\UCC.Site.Creator.ps1 ``" -ForegroundColor White
    Write-Host "      -paramUccName 'my-ucc' ``" -ForegroundColor White
    Write-Host "      -paramUccTitle 'My UCC Site' ``" -ForegroundColor White
    Write-Host "      -paramSiteCollectionUrl 'https://contoso.sharepoint.com/sites/my-ucc' ``" -ForegroundColor White
    Write-Host "      -Interactive ``" -ForegroundColor White
    Write-Host "      -paramInteractiveClientId 'your-client-id'" -ForegroundColor White
    Write-Host ""
}
