<#
.SYNOPSIS
    Schema updates for UCC sites during modernization.
.DESCRIPTION
    This module contains functions to update list schemas (columns, views, content types)
    as part of the modernization process. Each update checks if changes are already applied
    before making modifications.
.NOTES
    Version: 8.4.1.8
    Add new schema updates as functions following the Ensure-* naming pattern.
#>

# ============================================================================
# Helper Functions
# ============================================================================

function Ensure-ListColumn {
    <#
    .SYNOPSIS
        Ensures a column exists on a list, creating it if necessary.
    .DESCRIPTION
        Checks if a column with the specified internal name exists on the list.
        If not, creates the column with the specified properties.
    .OUTPUTS
        Returns $true if column was created, $false if it already existed.
    #>
    param(
        [Parameter(Mandatory=$true)]
        [string]$ListName,
        
        [Parameter(Mandatory=$true)]
        [string]$InternalName,
        
        [Parameter(Mandatory=$true)]
        [string]$DisplayName,
        
        [Parameter(Mandatory=$true)]
        [ValidateSet("Text", "Note", "Number", "Boolean", "DateTime", "Choice", "Lookup", "User", "URL", "Currency")]
        [string]$FieldType,
        
        [Parameter(Mandatory=$false)]
        [bool]$Required = $false,
        
        [Parameter(Mandatory=$false)]
        [int]$MaxLength = 255,
        
        [Parameter(Mandatory=$false)]
        [string]$DefaultValue = $null,
        
        [Parameter(Mandatory=$false)]
        [string]$Description = ""
    )
    
    # Check if list exists
    $list = Get-PnPList -Identity $ListName -ErrorAction SilentlyContinue
    if (-not $list) {
        return $false
    }
    
    # Check if field already exists
    $existingField = Get-PnPField -List $ListName -Identity $InternalName -ErrorAction SilentlyContinue
    if ($existingField) {
        return $false  # Already exists
    }
    
    # Build field XML based on type
    $fieldTypeMap = @{
        "Text" = "Text"
        "Note" = "Note"
        "Number" = "Number"
        "Boolean" = "Boolean"
        "DateTime" = "DateTime"
        "Choice" = "Choice"
        "Lookup" = "Lookup"
        "User" = "User"
        "URL" = "URL"
        "Currency" = "Currency"
    }
    
    $spFieldType = $fieldTypeMap[$FieldType]
    $requiredAttr = if ($Required) { "TRUE" } else { "FALSE" }
    
    # Build the field XML
    if ($FieldType -eq "Text") {
        $fieldXml = "<Field Type='$spFieldType' Name='$InternalName' DisplayName='$DisplayName' Required='$requiredAttr' MaxLength='$MaxLength' />"
    }
    elseif ($FieldType -eq "Note") {
        $descAttr = if (-not [string]::IsNullOrEmpty($Description)) { " Description='$([System.Security.SecurityElement]::Escape($Description))'" } else { "" }
        $fieldXml = "<Field Type='$spFieldType' Name='$InternalName' DisplayName='$DisplayName' Required='$requiredAttr' NumLines='6' RichText='FALSE'$descAttr />"
    }
    elseif ($FieldType -eq "Boolean") {
        $defaultVal = if ($DefaultValue -eq "Yes" -or $DefaultValue -eq "1" -or $DefaultValue -eq $true) { "1" } else { "0" }
        $fieldXml = "<Field Type='$spFieldType' Name='$InternalName' DisplayName='$DisplayName' Required='$requiredAttr'><Default>$defaultVal</Default></Field>"
    }
    else {
        $fieldXml = "<Field Type='$spFieldType' Name='$InternalName' DisplayName='$DisplayName' Required='$requiredAttr' />"
    }
    
    # Create the field
    try {
        Add-PnPFieldFromXml -List $ListName -FieldXml $fieldXml -ErrorAction Stop | Out-Null
        return $true  # Created successfully
    }
    catch {
        Write-Warning "Failed to create field '$InternalName' on list '$ListName': $($_.Exception.Message)"
        return $false
    }
}

function Ensure-ColumnInView {
    <#
    .SYNOPSIS
        Ensures a column is included in a list view.
    .DESCRIPTION
        Adds a column to a view if it's not already present.
    .OUTPUTS
        Returns $true if column was added to view, $false if already present or failed.
    #>
    param(
        [Parameter(Mandatory=$true)]
        [string]$ListName,
        
        [Parameter(Mandatory=$true)]
        [string]$ViewName,
        
        [Parameter(Mandatory=$true)]
        [string]$ColumnInternalName
    )
    
    try {
        $view = Get-PnPView -List $ListName -Identity $ViewName -ErrorAction SilentlyContinue
        if (-not $view) {
            return $false
        }
        
        # Check if column is already in view
        $viewFields = $view.ViewFields
        if ($viewFields -contains $ColumnInternalName) {
            return $false  # Already in view
        }
        
        # Add column to view
        $newFields = @($viewFields) + $ColumnInternalName
        Set-PnPView -List $ListName -Identity $ViewName -Fields $newFields -ErrorAction Stop | Out-Null
        return $true
    }
    catch {
        Write-Warning "Failed to add '$ColumnInternalName' to view '$ViewName': $($_.Exception.Message)"
        return $false
    }
}

function Ensure-ColumnInContentType {
    <#
    .SYNOPSIS
        Ensures a column is linked to a content type.
    .DESCRIPTION
        Adds a site column to a list content type if not already present.
    .OUTPUTS
        Returns $true if column was added, $false if already present or failed.
    #>
    param(
        [Parameter(Mandatory=$true)]
        [string]$ListName,
        
        [Parameter(Mandatory=$true)]
        [string]$ContentTypeName,
        
        [Parameter(Mandatory=$true)]
        [string]$ColumnInternalName
    )
    
    try {
        # Get the content type from the list
        $ct = Get-PnPContentType -List $ListName -Identity $ContentTypeName -ErrorAction SilentlyContinue
        if (-not $ct) {
            return $false
        }
        
        # Check if field is already in content type
        $ctFields = Get-PnPProperty -ClientObject $ct -Property "Fields" -ErrorAction SilentlyContinue
        $existingField = $ctFields | Where-Object { $_.InternalName -eq $ColumnInternalName }
        if ($existingField) {
            return $false  # Already in content type
        }
        
        # Add field to content type
        Add-PnPFieldToContentType -Field $ColumnInternalName -ContentType $ct -ErrorAction Stop | Out-Null
        return $true
    }
    catch {
        # Silently fail - content type might not exist or field might already be linked
        return $false
    }
}

# ============================================================================
# Schema Update Functions
# ============================================================================

function Update-AgentsListSchema {
    <#
    .SYNOPSIS
        Updates the Agents list with Teams-related columns.
    .DESCRIPTION
        Adds wsp_ucc_agent_teamsupn and wsp_ucc_agent_teamsphone columns
        to the Agents list if they don't exist.
    #>
    param()
    
    $listName = "Agents"
    $columnsAdded = 0
    $viewsUpdated = 0
    
    # Check if Agents list exists
    $list = Get-PnPList -Identity $listName -ErrorAction SilentlyContinue
    if (-not $list) {
        return @{ ColumnsAdded = 0; ViewsUpdated = 0; Skipped = $true }
    }
    
    # Column 1: Teams UPN
    $created = Ensure-ListColumn -ListName $listName `
        -InternalName "wsp_ucc_agent_teamsupn" `
        -DisplayName "Teams UPN" `
        -FieldType "Text" `
        -Required $false `
        -MaxLength 255
    
    if ($created) {
        $columnsAdded++
        Write-Host "    Added column 'Teams UPN' to $listName" -ForegroundColor Gray
        
        # Add to AllItems view
        if (Ensure-ColumnInView -ListName $listName -ViewName "AllItems" -ColumnInternalName "wsp_ucc_agent_teamsupn") {
            $viewsUpdated++
        }
        
        # Add to Agent content type
        Ensure-ColumnInContentType -ListName $listName -ContentTypeName "Agent" -ColumnInternalName "wsp_ucc_agent_teamsupn" | Out-Null
    }
    
    # Column 2: Teams Assigned Phone Number
    $created = Ensure-ListColumn -ListName $listName `
        -InternalName "wsp_ucc_agent_teamsphone" `
        -DisplayName "TeamsAssignedPhoneNumber" `
        -FieldType "Text" `
        -Required $false `
        -MaxLength 255
    
    if ($created) {
        $columnsAdded++
        Write-Host "    Added column 'TeamsAssignedPhoneNumber' to $listName" -ForegroundColor Gray
        
        # Add to AllItems view
        if (Ensure-ColumnInView -ListName $listName -ViewName "AllItems" -ColumnInternalName "wsp_ucc_agent_teamsphone") {
            $viewsUpdated++
        }
        
        # Add to Agent content type
        Ensure-ColumnInContentType -ListName $listName -ContentTypeName "Agent" -ColumnInternalName "wsp_ucc_agent_teamsphone" | Out-Null
    }
    
    return @{ ColumnsAdded = $columnsAdded; ViewsUpdated = $viewsUpdated; Skipped = $false }
}

function Update-SkillsListSchema {
    <#
    .SYNOPSIS
        Updates the Skills list with PathFinder-related columns.
    .DESCRIPTION
        Adds wsp_ucc_pathfinderenabled and wsp_ucc_pathfinderdescription columns
        to the Skills list if they don't exist.
    #>
    param()
    
    $listName = "Skills"
    $columnsAdded = 0
    $viewsUpdated = 0
    
    # Check if Skills list exists
    $list = Get-PnPList -Identity $listName -ErrorAction SilentlyContinue
    if (-not $list) {
        return @{ ColumnsAdded = 0; ViewsUpdated = 0; Skipped = $true }
    }
    
    # Content types that should have these columns (base type - inheritance should handle the rest)
    $baseContentType = "Skill"
    
    # Column 1: PathFinderEnabled (Boolean, default Yes)
    $created = Ensure-ListColumn -ListName $listName `
        -InternalName "wsp_ucc_pathfinderenabled" `
        -DisplayName "PathFinderEnabled" `
        -FieldType "Boolean" `
        -Required $false `
        -DefaultValue "Yes"
    
    if ($created) {
        $columnsAdded++
        Write-Host "    Added column 'PathFinderEnabled' to $listName" -ForegroundColor Gray
        
        # Add to AllItems view
        if (Ensure-ColumnInView -ListName $listName -ViewName "AllItems" -ColumnInternalName "wsp_ucc_pathfinderenabled") {
            $viewsUpdated++
        }
        
        # Add to base Skill content type
        Ensure-ColumnInContentType -ListName $listName -ContentTypeName $baseContentType -ColumnInternalName "wsp_ucc_pathfinderenabled" | Out-Null
    }
    
    # Column 2: PathfinderDescription (Note - multiple lines of text, plain text)
    $created = Ensure-ListColumn -ListName $listName `
        -InternalName "wsp_ucc_pathfinderdescription" `
        -DisplayName "PathfinderDescription" `
        -FieldType "Note" `
        -Required $false `
        -Description "For each Skill, enter a clear Description (an A.I. Prompt) which explains when the Skill should be used"
    
    if ($created) {
        $columnsAdded++
        Write-Host "    Added column 'PathfinderDescription' to $listName" -ForegroundColor Gray
        
        # Add to AllItems view
        if (Ensure-ColumnInView -ListName $listName -ViewName "AllItems" -ColumnInternalName "wsp_ucc_pathfinderdescription") {
            $viewsUpdated++
        }
        
        # Add to base Skill content type
        Ensure-ColumnInContentType -ListName $listName -ContentTypeName $baseContentType -ColumnInternalName "wsp_ucc_pathfinderdescription" | Out-Null
    }
    
    return @{ ColumnsAdded = $columnsAdded; ViewsUpdated = $viewsUpdated; Skipped = $false }
}

function Update-EndpointsListSchema {
    <#
    .SYNOPSIS
        Updates the Endpoints list with Hide and ShowAgentCallerId columns.
    .DESCRIPTION
        Adds wsp_ucc_endpoints_hide and wsp_ucc_endpoints_agentcallerid columns
        to the Endpoints list if they don't exist. Both columns are Boolean (Yes/No)
        with default value Yes, used in ModalityEndpoint and MainEndpoint content types.
    #>
    param()
    
    $listName = "Endpoints"
    $columnsAdded = 0
    $viewsUpdated = 0
    
    # Check if Endpoints list exists
    $list = Get-PnPList -Identity $listName -ErrorAction SilentlyContinue
    if (-not $list) {
        return @{ ColumnsAdded = 0; ViewsUpdated = 0; Skipped = $true }
    }
    
    # Content types that should have these columns
    $contentTypes = @("ModalityEndpoint", "MainEndpoint")
    
    # Column 1: Hide (Boolean, default Yes)
    $created = Ensure-ListColumn -ListName $listName `
        -InternalName "wsp_ucc_endpoints_hide" `
        -DisplayName "Hide" `
        -FieldType "Boolean" `
        -Required $false `
        -DefaultValue "Yes"
    
    if ($created) {
        $columnsAdded++
        Write-Host "    Added column 'Hide' to $listName" -ForegroundColor Gray
        
        # Add to AllItems view
        if (Ensure-ColumnInView -ListName $listName -ViewName "AllItems" -ColumnInternalName "wsp_ucc_endpoints_hide") {
            $viewsUpdated++
        }
        
        # Add to content types
        foreach ($ctName in $contentTypes) {
            Ensure-ColumnInContentType -ListName $listName -ContentTypeName $ctName -ColumnInternalName "wsp_ucc_endpoints_hide" | Out-Null
        }
    }
    
    # Column 2: ShowAgentCallerId (Boolean, default Yes)
    # Description: Valid for MS Connect Model calls made via the agent application
    $created = Ensure-ListColumn -ListName $listName `
        -InternalName "wsp_ucc_endpoints_agentcallerid" `
        -DisplayName "ShowAgentCallerId" `
        -FieldType "Boolean" `
        -Required $false `
        -DefaultValue "Yes" `
        -Description "Valid for MS Connect Model calls made via the agent application"
    
    if ($created) {
        $columnsAdded++
        Write-Host "    Added column 'ShowAgentCallerId' to $listName" -ForegroundColor Gray
        
        # Add to AllItems view
        if (Ensure-ColumnInView -ListName $listName -ViewName "AllItems" -ColumnInternalName "wsp_ucc_endpoints_agentcallerid") {
            $viewsUpdated++
        }
        
        # Add to content types
        foreach ($ctName in $contentTypes) {
            Ensure-ColumnInContentType -ListName $listName -ContentTypeName $ctName -ColumnInternalName "wsp_ucc_endpoints_agentcallerid" | Out-Null
        }
    }
    
    return @{ ColumnsAdded = $columnsAdded; ViewsUpdated = $viewsUpdated; Skipped = $false }
}

# ============================================================================
# SettingValues Content Updates
# ============================================================================

function Update-SettingValuesContent {
    <#
    .SYNOPSIS
        Ensures required SettingValues list items exist, adding any that are missing.
    .DESCRIPTION
        Checks for the presence of specific SettingValues entries introduced in v8.4.1.8
        and adds any that are absent. Existing items are never modified - only missing
        items are added. Outputs a single summary line rather than per-item messages.
    .OUTPUTS
        Returns a hashtable with ItemsAdded count and Skipped flag.
    #>
    param()

    $listName = "SettingValues"
    $list = Get-PnPList -Identity $listName -ErrorAction SilentlyContinue
    if (-not $list) {
        return @{ ItemsAdded = 0; Skipped = $true }
    }

    # Required items: each entry defines the full set of field values for a new row.
    # Only items absent from the list will be added.
    $requiredItems = @(
        @{
            Title                    = "UseOutboundCalling"
            wsp_ucc_sv_type          = "Boolean"
            wsp_ucc_sv_value         = ""
            wsp_ucc_sv_default       = "false"
            wsp_ucc_sv_description   = "TRUE = Enable the Direct Call Interceptor for the WebAgent.`nFALSE = Disable the Direct Call Interceptor for the WebAgent.`nDefault: FALSE"
            wsp_ucc_sv_scope         = "Dialer"
            wsp_ucc_sv_settinggroup  = "Dialer"
            wsp_ucc_sv_readonly      = $false
            wsp_ucc_sv_active        = $true
        },
        @{
            Title                    = "EnableInboundInterception"
            wsp_ucc_sv_type          = "Boolean"
            wsp_ucc_sv_value         = ""
            wsp_ucc_sv_default       = "false"
            wsp_ucc_sv_description   = "TRUE = Enable Inbound Interception of Calls to the direct number of the agent.`nFALSE = Disable Inbound Interception of Calls to the direct number of the agent.`nDefault: FALSE"
            wsp_ucc_sv_scope         = "Dialer"
            wsp_ucc_sv_settinggroup  = "Dialer"
            wsp_ucc_sv_readonly      = $false
            wsp_ucc_sv_active        = $true
        },
        @{
            Title                    = "EnableOutboundInterception"
            wsp_ucc_sv_type          = "Boolean"
            wsp_ucc_sv_value         = ""
            wsp_ucc_sv_default       = "false"
            wsp_ucc_sv_description   = "TRUE = Enable Outbound Interception of Calls from the native Teams client (contact list or dialpad) of the agent.`nFALSE = Disable Outbound Interception of Calls from the native Teams client (contact list or dialpad) of the agent.`nDefault: FALSE"
            wsp_ucc_sv_scope         = "Dialer"
            wsp_ucc_sv_settinggroup  = "Dialer"
            wsp_ucc_sv_readonly      = $false
            wsp_ucc_sv_active        = $true
        }
    )

    # Retrieve existing titles once to avoid repeated queries
    $existingItems = Get-PnPListItem -List $listName -Fields "Title" -PageSize 500 -ErrorAction SilentlyContinue
    $existingTitles = $existingItems | ForEach-Object { $_["Title"] }

    $itemsAdded = 0
    foreach ($item in $requiredItems) {
        if ($existingTitles -contains $item.Title) {
            continue  # Already present - never overwrite
        }

        $values = @{
            Title                   = $item.Title
            wsp_ucc_sv_type         = $item.wsp_ucc_sv_type
            wsp_ucc_sv_value        = $item.wsp_ucc_sv_value
            wsp_ucc_sv_default      = $item.wsp_ucc_sv_default
            wsp_ucc_sv_description  = $item.wsp_ucc_sv_description
            wsp_ucc_sv_scope        = $item.wsp_ucc_sv_scope
            wsp_ucc_sv_settinggroup = $item.wsp_ucc_sv_settinggroup
            wsp_ucc_sv_readonly     = $item.wsp_ucc_sv_readonly
            wsp_ucc_sv_active       = $item.wsp_ucc_sv_active
        }

        try {
            Add-PnPListItem -List $listName -Values $values -ErrorAction Stop | Out-Null
            $itemsAdded++
        }
        catch {
            Write-Warning "Failed to add SettingValues item '$($item.Title)': $($_.Exception.Message)"
        }
    }

    if ($itemsAdded -gt 0) {
        Write-Host "    Adding $itemsAdded additional SettingValues item(s)" -ForegroundColor Gray
    }

    return @{ ItemsAdded = $itemsAdded; Skipped = $false }
}

# ============================================================================
# Main Entry Point
# ============================================================================

function Invoke-SchemaUpdates {
    <#
    .SYNOPSIS
        Runs all schema updates for the current site.
    .DESCRIPTION
        Executes all schema update functions and reports results.
        Each update is idempotent - safe to run multiple times.
    .OUTPUTS
        Returns a summary hashtable with counts of changes made.
    #>
    param()
    
    $totalColumnsAdded = 0
    $totalViewsUpdated = 0
    $totalItemsAdded = 0
    $listsUpdated = @()
    
    # Update Agents list
    $agentsResult = Update-AgentsListSchema
    if (-not $agentsResult.Skipped) {
        $totalColumnsAdded += $agentsResult.ColumnsAdded
        $totalViewsUpdated += $agentsResult.ViewsUpdated
        if ($agentsResult.ColumnsAdded -gt 0) {
            $listsUpdated += "Agents"
        }
    }
    
    # Update Skills list
    $skillsResult = Update-SkillsListSchema
    if (-not $skillsResult.Skipped) {
        $totalColumnsAdded += $skillsResult.ColumnsAdded
        $totalViewsUpdated += $skillsResult.ViewsUpdated
        if ($skillsResult.ColumnsAdded -gt 0) {
            $listsUpdated += "Skills"
        }
    }
    
    # Update Endpoints list
    $endpointsResult = Update-EndpointsListSchema
    if (-not $endpointsResult.Skipped) {
        $totalColumnsAdded += $endpointsResult.ColumnsAdded
        $totalViewsUpdated += $endpointsResult.ViewsUpdated
        if ($endpointsResult.ColumnsAdded -gt 0) {
            $listsUpdated += "Endpoints"
        }
    }

    # Update SettingValues list content
    $settingValuesResult = Update-SettingValuesContent
    if (-not $settingValuesResult.Skipped) {
        $totalItemsAdded += $settingValuesResult.ItemsAdded
        if ($settingValuesResult.ItemsAdded -gt 0) {
            $listsUpdated += "SettingValues"
        }
    }
    
    return @{
        ColumnsAdded = $totalColumnsAdded
        ViewsUpdated = $totalViewsUpdated
        ItemsAdded   = $totalItemsAdded
        ListsUpdated = $listsUpdated
    }
}

