# governance-integrity-check.ps1 -- SessionStart hook
# SHA-256 verifies all governance files on session start.
# Alerts if any files have been tampered with.
# Never blocks session start (always exits 0), but reports issues.
#
# Part of @connexum/ai-governance

# Find hash manifest
$hashManifest = $null
foreach ($candidate in @(
    '.claude/governance/file-hashes.json',
    '.claude/rules/file-hashes.json',
    'GOVERNANCE/file-hashes.json'
)) {
    if (Test-Path $candidate) {
        $hashManifest = $candidate
        break
    }
}

# If no manifest exists, create one from current files
if (-not $hashManifest) {
    $manifestDir = '.claude/governance'
    try { New-Item -ItemType Directory -Path $manifestDir -Force | Out-Null } catch { exit 0 }
    $hashManifest = Join-Path $manifestDir 'file-hashes.json'

    # Scan for governance files
    $governanceFiles = @()
    foreach ($pattern in @(
        '.claude/rules/*.md',
        '.claude/governance/*.md',
        'GOVERNANCE/*.md'
    )) {
        $governanceFiles += Get-ChildItem -Path $pattern -ErrorAction SilentlyContinue
    }

    if ($governanceFiles.Count -gt 0) {
        $manifest = @{}
        foreach ($f in $governanceFiles) {
            try {
                $hash = (Get-FileHash -Path $f.FullName -Algorithm SHA256).Hash.ToLower()
                $relativePath = $f.FullName.Replace("$PWD\", '').Replace("$PWD/", '').Replace('\', '/')
                $manifest[$relativePath] = $hash
            } catch { continue }
        }
        $manifest | ConvertTo-Json | Set-Content $hashManifest
        [Console]::Error.WriteLine("Governance integrity manifest created with $($governanceFiles.Count) files.")
    }

    exit 0
}

# Verify each file in the manifest
$tampered = 0
$missing = 0
$report = @()

try {
    $manifestData = Get-Content $hashManifest -Raw | ConvertFrom-Json
} catch {
    [Console]::Error.WriteLine("WARNING: Could not parse governance manifest.")
    exit 0
}

foreach ($prop in $manifestData.PSObject.Properties) {
    $file = $prop.Name
    $expectedHash = $prop.Value

    if (-not (Test-Path $file)) {
        $missing++
        $report += "MISSING: $file"
        continue
    }

    try {
        $currentHash = (Get-FileHash -Path $file -Algorithm SHA256).Hash.ToLower()
    } catch {
        $report += "ERROR: Could not hash $file"
        continue
    }

    if ($currentHash -ne $expectedHash) {
        $tampered++
        $report += "TAMPERED: $file (expected: $($expectedHash.Substring(0,16))..., got: $($currentHash.Substring(0,16))...)"
    }
}

if ($tampered -gt 0 -or $missing -gt 0) {
    [Console]::Error.WriteLine("GOVERNANCE INTEGRITY ALERT")
    [Console]::Error.WriteLine("=========================")
    [Console]::Error.WriteLine("$tampered file(s) tampered, $missing file(s) missing.")
    foreach ($line in $report) {
        [Console]::Error.WriteLine($line)
    }
    [Console]::Error.WriteLine("Review immediately. Run 'git diff' to see changes.")
    [Console]::Error.WriteLine("To update manifest after authorized changes: update file-hashes.json")
} else {
    [Console]::Error.WriteLine("Governance integrity verified. All files match expected hashes.")
}

exit 0
