microsoft-365/SharePointSearchHelpers.psm1

function Search-DeletedItem {
    param(
        [Parameter(Mandatory = $true)]
        [string]$SiteURL,
        
        [Parameter(Mandatory = $false)]
        [string]$Keyword = "",
        
        [Parameter(Mandatory = $false)]
        [string]$OriginalLocation = "",
        
        [Parameter(Mandatory = $false)]
        [string]$DeletedBy = "",
        
        [Parameter(Mandatory = $false)]
        [int]$DaysBack = 93,
        
        [Parameter(Mandatory = $false)]
        [switch]$ExportToCSV,
        
        [Parameter(Mandatory = $false)]
        [switch]$IncludeSecondStage
    )
    
    try {
        
        # Connect to SharePoint
        Write-Host "Connecting to $SiteURL..." -ForegroundColor Yellow
        connect-pnponline -Interactive -ClientId deccb19c-bfba-483c-84aa-ee7076b19053 -Url $SiteURL
        
        # Calculate date range
        $StartDate = (Get-Date).AddDays(-$DaysBack)
        
        # Search first stage recycle bin
        Write-Host "Searching first stage recycle bin..." -ForegroundColor Green
        $FirstStageItems = Get-PnPRecycleBinItem | Where-Object {
            # $_.ItemType -eq "Folder" -and
            $_.DeletedDate -ge $StartDate -and
            ($Keyword -eq "" -or $_.Title -like "*$Keyword*") -and
            ($OriginalLocation -eq "" -or $_.DirName -like "*$OriginalLocation*") -and
            ($DeletedBy -eq "" -or $_.DeletedByName -like "*$DeletedBy*")
        }
        
        $AllResults = @()
        
        if ($FirstStageItems) {
            $FirstStageItems | ForEach-Object {
                $AllResults += [PSCustomObject]@{
                    Title       = $_.Title
                    DeletedDate = $_.DeletedDate
                    DeletedBy   = $_.DeletedByName
                    Location    = $_.DirName
                    Stage       = "First Stage"
                    Id          = $_.Id
                    Size        = $_.Size
                }
            }
        }
        
        # Search second stage recycle bin if requested
        if ($IncludeSecondStage) {
            Write-Host "Searching second stage recycle bin..." -ForegroundColor Green
            $SecondStageItems = Get-PnPRecycleBinItem -SecondStage | Where-Object {
                # $_.ItemType -eq "Folder" -and
                $_.DeletedDate -ge $StartDate -and
                ($Keyword -eq "" -or $_.Title -like "*$Keyword*") -and
                ($OriginalLocation -eq "" -or $_.DirName -like "*$OriginalLocation*") -and
                ($DeletedBy -eq "" -or $_.DeletedByName -like "*$DeletedBy*")
            }
            
            if ($SecondStageItems) {
                $SecondStageItems | ForEach-Object {
                    $AllResults += [PSCustomObject]@{
                        Title       = $_.Title
                        DeletedDate = $_.DeletedDate
                        DeletedBy   = $_.DeletedByName
                        Location    = $_.DirName
                        Stage       = "Second Stage"
                        Id          = $_.Id
                        Size        = $_.Size
                    }
                }
            }
        }
        
        # Display results
        if ($AllResults.Count -gt 0) {
            Write-Host "Found $($AllResults.Count) matching folder(s):" -ForegroundColor Green
            # $AllResults | Sort-Object DeletedDate -Descending | Format-Table -AutoSize
            
            # Export to CSV if requested
            if ($ExportToCSV) {
                $FileName = "DeletedFolders_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv"
                $AllResults | Export-Csv -Path $FileName -NoTypeInformation
                Write-Host "Results exported to $FileName" -ForegroundColor Green
            }
        }
        else {
            Write-Host "No matching folders found in the specified time range." -ForegroundColor Red
        }
        
        return $AllResults
        
    }
    catch {
        Write-Error "Error occurred: $($_.Exception.Message)"
    }
    finally {

    }
}

# Usage examples:
# Search-DeletedFolder -SiteURL "https://yourtenant.sharepoint.com/sites/yoursite"
# Search-DeletedFolder -SiteURL "https://yourtenant.sharepoint.com/sites/yoursite" -Keyword "ProjectFiles"
# Search-DeletedFolder -SiteURL "https://yourtenant.sharepoint.com/sites/yoursite" -DaysBack 30 -ExportToCSV
# Search-DeletedFolder -SiteURL "https://yourtenant.sharepoint.com/sites/yoursite" -IncludeSecondStage -ExportToCSV
# Search-DeletedItem "https://jranck-my.sharepoint.com/personal/kzeien_jranck_com" -OriginalLocation "IDrive Migration Logs" | Restore-DeletedItem

# Document Library Search Examples:
# Search-DocumentLibrary -SiteURL "https://yourtenant.sharepoint.com/sites/yoursite" -LibraryName "Documents" -SearchString "Project"
# Search-DocumentLibrary -SiteURL "https://yourtenant.sharepoint.com/sites/yoursite" -LibraryName "Documents" -SearchString "Budget" -ItemType "Files" -ExportToCSV

# Quick restore function
function Restore-DeletedItem {
    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipelineByPropertyName = $true, Position = 0)]
        [string]$Id,

        [Parameter(ValueFromPipeline = $true)]
        [psobject]$InputObject
    )

    process {
        try {
            # If an object was piped, normalize to a collection so we handle arrays or single objects
            if ($null -ne $InputObject) {
                $objs = if ($InputObject -is [System.Collections.IEnumerable] -and -not ($InputObject -is [string])) {
                    $InputObject
                }
                else {
                    @($InputObject)
                }

                foreach ($obj in $objs) {
                    $itemId = $null

                    if ($obj -is [string]) {
                        $itemId = $obj
                    }
                    elseif ($obj.PSObject.Properties.Name -contains 'Id') {
                        $itemId = $obj.Id
                    }
                    elseif ($obj.PSObject.Properties.Name -contains 'ID') {
                        $itemId = $obj.ID
                    }
                    else {
                        Write-Error "Piped object does not contain an 'Id' property: $($obj | Out-String)"
                        continue
                    }

                    if (-not $itemId) {
                        Write-Error "No Id found on piped object."
                        continue
                    }

                    # THIS ASSUMES YOU ARE ALREADY CONNECTED
                    Restore-PnPRecycleBinItem -Identity $itemId -Force
                    Write-Host "Item $itemId restored successfully" -ForegroundColor Green
                }

                return
            }

            # If Id was bound by property name (or passed directly), handle it here
            if ($PSBoundParameters.ContainsKey('Id') -and $Id) {
                Restore-PnPRecycleBinItem -Identity $Id -Force
                Write-Host "Item $Id restored successfully" -ForegroundColor Green
                return
            }

            Write-Error "No Id provided to Restore-DeletedItem."
        }
        catch {
            Write-Error "Error restoring item: $($_.Exception.Message)"
        }
    }
}

# Advanced search function using SharePoint Search API
function Search-DocumentLibrary {
    param(
        [Parameter(Mandatory = $true)]
        [string]$SiteURL,
        
        [Parameter(Mandatory = $true)]
        [string]$LibraryName,
        
        [Parameter(Mandatory = $true)]
        [string]$SearchString,
        
        [Parameter(Mandatory = $false)]
        [ValidateSet("All", "Files", "Folders")]
        [string]$ItemType = "All",
        
        [Parameter(Mandatory = $false)]
        [switch]$ExportToCSV
    )
    
    try {
        Write-Host "Connecting to $SiteURL..." -ForegroundColor Yellow

        connect-pnponline -Interactive -ClientId deccb19c-bfba-483c-84aa-ee7076b19053 -Url $SiteURL

        # Build search query
        $Query = "path:$SiteURL/$LibraryName* AND filename:$SearchString"
        
        # Add content type filter
        if ($ItemType -eq "Files") {
            $Query += " AND IsDocument:true"
        }
        elseif ($ItemType -eq "Folders") {
            $Query += " AND contentclass:STS_ListItem_DocumentLibrary"
        }
        
        Write-Host "Executing search query: $Query" -ForegroundColor Yellow
        
        # Execute search
        $SearchResults = Submit-PnPSearchQuery -Query $Query -MaxResults 500 -TrimDuplicates:$false
        
        $Results = @()
        
        foreach ($Result in $SearchResults.ResultRows) {
            $Results += [PSCustomObject]@{
                Title            = $Result.Title
                Filename         = $Result.Filename
                Path             = $Result.Path.Replace("$SiteURL/", "")
                Size             = $Result.Size
                LastModifiedTime = $Result.LastModifiedTime
                Author           = $Result.Author
                FileType         = $Result.FileType
                IsFolder         = $Result.IsFolder
                ContentClass     = $Result.ContentClass
            }
        }
        
        # Display results
        if ($Results.Count -gt 0) {
            Write-Host "Found $($Results.Count) matching item(s) via search:" -ForegroundColor Green
            # $Results | Sort-Object IsFolder, Title | Format-Table Title, FileType, LastModifiedTime, Author -AutoSize
            
            if ($ExportToCSV) {
                $FileName = "AdvancedLibrarySearch_$($LibraryName)_$($SearchString)_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv"
                $Results | Export-Csv -Path $FileName -NoTypeInformation
                Write-Host "Results exported to $FileName" -ForegroundColor Green
            }
        }
        else {
            Write-Host "No matching items found via search." -ForegroundColor Red
        }
        
        return $Results
        
    }
    catch {
        Write-Error "Error occurred: $($_.Exception.Message)"
    }
    finally {

    }
}