# _Common.ps1 - shared helpers for analysis (Windows PowerShell 5.1 compatible, no external modules)

$script:PFUA = 'analysis/1.0 (mailto:anonymous@example.com)'

function Set-AnalysisMailto {
    param([string]$Mailto)
    if ($Mailto) { $script:PFUA = "analysis/1.0 (mailto:$Mailto)" }
}

function Get-RawText {
    <# Downloads a URL as UTF-8 text. Used instead of Invoke-RestMethod because PS 5.1
       ConvertFrom-Json throws on OpenAlex abstract_inverted_index (case-duplicate keys). #>
    param([Parameter(Mandatory = $true)][string]$Url, [int]$Retries = 3)
    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
    for ($i = 1; $i -le $Retries; $i++) {
        try {
            $wc = New-Object System.Net.WebClient
            $wc.Encoding = [Text.Encoding]::UTF8
            $wc.Headers.Add('Accept', 'application/json')
            $txt = $wc.DownloadString($Url)
            $wc.Dispose()
            Start-Sleep -Milliseconds 120   # polite pool rate limiting
            return $txt
        }
        catch {
            if ($i -eq $Retries) { throw "GET failed after $Retries tries: $Url`n$($_.Exception.Message)" }
            Start-Sleep -Seconds ($i * 2)
        }
    }
}

function Get-Json {
    param([Parameter(Mandatory = $true)][string]$Url)
    return (Get-RawText -Url $Url | ConvertFrom-Json)
}

function Get-MetaCount {
    <# $obj.meta.count collides with PowerShell's intrinsic Count property. #>
    param($MetaObject)
    if ($null -eq $MetaObject) { return 0 }
    $p = $MetaObject.PSObject.Properties['count']
    if ($p) { return [int]$p.Value }
    return 0
}

function Get-NextCursor {
    param($MetaObject)
    if ($null -eq $MetaObject) { return $null }
    $p = $MetaObject.PSObject.Properties['next_cursor']
    if ($p) { return $p.Value }
    return $null
}

function ConvertFrom-InvertedIndexText {
    <# Rebuilds a readable abstract from a raw OpenAlex abstract_inverted_index JSON fragment. #>
    param([string]$Fragment)
    if ([string]::IsNullOrWhiteSpace($Fragment)) { return '' }
    $pairs = [regex]::Matches($Fragment, '"((?:[^"\\]|\\.)*)"\s*:\s*\[([0-9,\s]+)\]')
    if ($pairs.Count -eq 0) { return '' }
    $items = New-Object System.Collections.ArrayList
    foreach ($m in $pairs) {
        $w = $m.Groups[1].Value -replace '\\"', '"' -replace '\\\\', '\' -replace '\\/', '/'
        foreach ($p in ($m.Groups[2].Value -split ',')) {
            $p = $p.Trim()
            if ($p -ne '') { [void]$items.Add([pscustomobject]@{ P = [int]$p; W = $w }) }
        }
    }
    return (($items | Sort-Object P | ForEach-Object { $_.W }) -join ' ')
}

function Get-Slug {
    param([string]$Text, [int]$MaxLen = 40)
    $s = ($Text -replace '[^a-zA-Z0-9]+', '-').Trim('-').ToLower()
    if ($s.Length -gt $MaxLen) { $s = $s.Substring(0, $MaxLen).Trim('-') }
    if (-not $s) { $s = 'run' }
    return $s
}

function Get-Stopwords {
    $w = @'
a an the and or but if then than that this these those of in on at to for from by with without within
into over under about against between during through above below up down out off again further once
is are was were be been being am do does did doing have has had having will would shall should can
could may might must not no nor only own same so such too very s t don now also more most other some
any each few both all as we our us their they it its his her he she you your i me my one two three
using used use based study studies paper article research approach method methods results result
analysis new novel via toward towards among however therefore thus which where when who whom whose
what how why while due per among across via et al abstract background objective objectives aim aims
purpose conclusion conclusions significant significantly show shows shown showed found find finding
findings propose proposed proposes present presents presented investigate investigated examine
examined evaluate evaluated compared comparison different various several many high low large small
better best improve improved improvement performance data model models system systems approach
approaches framework proposed work works
review reviews survey surveys literature overview chapter introduction section table figure appendix
arxiv preprint doi http https www elsevier springer wiley ieee mdpi taylor francis sage license
copyright author authors journal journals publication publications published publisher issue volume
pages page open access rights reserved corresponding email university department received accepted
'@
    $set = @{}
    foreach ($t in ($w -split '\s+')) { if ($t) { $set[$t.ToLower()] = $true } }
    return $set
}

function Get-Terms {
    <# Tokenises text into unigrams+bigrams, dropping stopwords, numbers and 1-2 char tokens. #>
    param([string]$Text, [hashtable]$Stop, [switch]$Bigrams)
    if (-not $Text) { return @() }
    $clean = ($Text.ToLower() -replace '[^a-z0-9\- ]', ' ') -replace '\s+', ' '
    $toks = @($clean.Trim() -split ' ' | Where-Object { $_.Length -ge 3 -and -not $Stop.ContainsKey($_) -and $_ -notmatch '^[0-9\-]+$' })
    if (-not $Bigrams) { return $toks }
    $out = New-Object System.Collections.ArrayList
    foreach ($t in $toks) { [void]$out.Add($t) }
    for ($i = 0; $i -lt ($toks.Count - 1); $i++) { [void]$out.Add($toks[$i] + ' ' + $toks[$i + 1]) }
    return $out.ToArray()
}

function New-BibKey {
    param([string]$Author, [int]$Year, [string]$Title, [hashtable]$Used)
    $last = 'anon'
    if ($Author) {
        $parts = @(($Author -split '\s+') | Where-Object { $_ })
        if ($parts.Count -gt 0) { $last = $parts[-1] }
    }
    $last = ($last -replace '[^A-Za-z]', '').ToLower()
    if (-not $last) { $last = 'anon' }
    $first = 'x'
    $stop = Get-Stopwords
    foreach ($t in (Get-Terms -Text $Title -Stop $stop)) { $first = $t; break }
    $first = ($first -replace '[^a-z0-9]', '')
    $base = "$last$Year$first"
    $key = $base; $n = 1
    while ($Used.ContainsKey($key)) { $n++; $key = "$base$n" }
    $Used[$key] = $true
    return $key
}

function Protect-Bib {
    param([string]$Text)
    if (-not $Text) { return '' }
    return ($Text -replace '[{}]', '' -replace '\\', '' -replace '&', '\&' -replace '%', '\%' -replace '#', '\#' -replace '_', '\_')
}
