# Encryption Module for Password Protection
# This module provides functions to encrypt and decrypt sensitive properties in JSON files
# Usage: Import-Module Helper-Encryption.psm1

# Encryption key - matches migrate.ps1
$script:EncryptionKey = "axiUmMigration2024Key123!"

# Function to create AES key from password
function Get-AesKey {
    param([string]$Password)

    # Create a SHA256 hash of the password
    $sha256 = [System.Security.Cryptography.SHA256]::Create()
    $hash = $sha256.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($Password))

    # Take first 32 bytes for AES-256
    $key = New-Object byte[] 32
    [Array]::Copy($hash, 0, $key, 0, 32)

    return $key
}

# Function to encrypt a string using AES
function Protect-Value {
    param([string]$PlainText)

    if ([string]::IsNullOrEmpty($PlainText)) {
        return $PlainText
    }

    try {
        $aes = [System.Security.Cryptography.Aes]::Create()
        $aes.Key = Get-AesKey -Password $script:EncryptionKey
        $aes.IV = New-Object byte[] 16  # AES block size is 16 bytes

        $encryptor = $aes.CreateEncryptor()

        $plainBytes = [System.Text.Encoding]::UTF8.GetBytes($PlainText)
        $encryptedBytes = $encryptor.TransformFinalBlock($plainBytes, 0, $plainBytes.Length)

        # Combine IV and encrypted data
        $result = $aes.IV + $encryptedBytes

        # Convert to base64 for storage
        $encryptedString = [Convert]::ToBase64String($result)

        return "ENC:$encryptedString"
    }
    catch {
        Write-Host "ERROR: Failed to encrypt value: $($_.Exception.Message)" -ForegroundColor Red
        throw
    }
    finally {
        if ($aes) { $aes.Dispose() }
        if ($encryptor) { $encryptor.Dispose() }
    }
}

# Function to decrypt a string using AES
function Unprotect-Value {
    param([string]$EncryptedText)

    if ([string]::IsNullOrEmpty($EncryptedText)) {
        return $EncryptedText
    }

    # Check if the value is encrypted (starts with ENC:)
    if (-not $EncryptedText.StartsWith("ENC:")) {
        # Return as-is if not encrypted
        return $EncryptedText
    }

    try {
        $aes = [System.Security.Cryptography.Aes]::Create()
        $aes.Key = Get-AesKey -Password $script:EncryptionKey

        # Remove ENC: prefix and decode from base64
        $encryptedData = [Convert]::FromBase64String($EncryptedText.Substring(4))

        # Extract IV (first 16 bytes) and encrypted data
        $aes.IV = $encryptedData[0..15]
        $encryptedBytes = $encryptedData[16..($encryptedData.Length - 1)]

        $decryptor = $aes.CreateDecryptor()

        $decryptedBytes = $decryptor.TransformFinalBlock($encryptedBytes, 0, $encryptedBytes.Length)
        $decryptedString = [System.Text.Encoding]::UTF8.GetString($decryptedBytes)

        return $decryptedString
    }
    catch {
        Write-Host "ERROR: Failed to decrypt value: $($_.Exception.Message)" -ForegroundColor Red
        # Return original value if decryption fails
        return $EncryptedText
    }
    finally {
        if ($aes) { $aes.Dispose() }
        if ($decryptor) { $decryptor.Dispose() }
    }
}

# Function to check if a value is encrypted
function Test-EncryptedValue {
    param([string]$Value)

    return $Value.StartsWith("ENC:")
}

# Function to convert plaintext passwords in JSON file
function Convert-PlaintextPasswordsInJson {
    param(
        [string]$JsonFilePath,
        [bool]$ShowWarnings = $true
    )

    if (-not (Test-Path $JsonFilePath)) {
        return $false
    }

    try {
        $jsonContent = Get-Content $JsonFilePath -Raw -ErrorAction Stop
        $jsonObject = $jsonContent | ConvertFrom-Json
        $modified = $false

        # Check each connection configuration
        if ($jsonObject.connections) {
            foreach ($connName in $jsonObject.connections.PSObject.Properties.Name) {
                $connConfig = $jsonObject.connections.$connName
                if ($connConfig.password -and -not [string]::IsNullOrEmpty($connConfig.password)) {
                    # Check if password is already encrypted
                    if (-not (Test-EncryptedValue -Value $connConfig.password)) {
                        if ($ShowWarnings) {
                            Write-Host "Found plaintext password for connection '$connName', encrypting..." -ForegroundColor Yellow
                        }
                        $connConfig.password = Protect-Value -PlainText $connConfig.password
                        $modified = $true
                    }
                }
            }
        }

        # Check each database configuration
        if ($jsonObject.databases) {
            foreach ($dbName in $jsonObject.databases.PSObject.Properties.Name) {
                $dbConfig = $jsonObject.databases.$dbName
                if ($dbConfig.password -and -not [string]::IsNullOrEmpty($dbConfig.password)) {
                    # Check if password is already encrypted
                    if (-not (Test-EncryptedValue -Value $dbConfig.password)) {
                        if ($ShowWarnings) {
                            Write-Host "Found plaintext password for database '$dbName', encrypting..." -ForegroundColor Yellow
                        }
                        $dbConfig.password = Protect-Value -PlainText $dbConfig.password
                        $modified = $true
                    }
                }
            }
        }

        if ($modified) {
            # Convert back to JSON and save
            $jsonObject | ConvertTo-Json -Depth 10 | Set-Content -Path $JsonFilePath -Encoding UTF8 -Force
            Write-Host "Encrypted passwords in configuration file" -ForegroundColor Green
            return $true
        }
        return $true
    } catch {
        Write-Host "WARNING: Failed to process passwords in JSON file: $($_.Exception.Message)" -ForegroundColor Yellow
        return $false
    }
}

# Function to decrypt passwords in JSON object
function Unprotect-JsonPasswords {
    param(
        [PSCustomObject]$JsonObject
    )

    try {
        # Create a copy to avoid modifying the original
        $jsonString = $JsonObject | ConvertTo-Json -Depth 10
        $decryptedObject = $jsonString | ConvertFrom-Json

        # Decrypt passwords in each connection configuration
        if ($decryptedObject.connections) {
            foreach ($connName in $decryptedObject.connections.PSObject.Properties.Name) {
                $connConfig = $decryptedObject.connections.$connName
                if ($connConfig.password -and -not [string]::IsNullOrEmpty($connConfig.password)) {
                    $connConfig.password = Unprotect-Value -EncryptedText $connConfig.password
                }
            }
        }

        # Decrypt passwords in each database configuration
        if ($decryptedObject.databases) {
            foreach ($dbName in $decryptedObject.databases.PSObject.Properties.Name) {
                $dbConfig = $decryptedObject.databases.$dbName
                if ($dbConfig.password -and -not [string]::IsNullOrEmpty($dbConfig.password)) {
                    $dbConfig.password = Unprotect-Value -EncryptedText $dbConfig.password
                }
            }
        }

        return $decryptedObject
    } catch {
        Write-Host "WARNING: Failed to decrypt passwords: $($_.Exception.Message)" -ForegroundColor Yellow
        return $JsonObject
    }
}

# Export the functions
Export-ModuleMember -Function Protect-Value, Unprotect-Value, Test-EncryptedValue, Convert-PlaintextPasswordsInJson, Unprotect-JsonPasswords

