//
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//

import Foundation
import LibSignalClient

/// Just a container for simple static helper methods on SVR
/// that can be shared with other classes (incl. objc classes).
@objc
public final class SVRUtil: NSObject {

    @objc
    public static func normalizePin(_ pin: String) -> String {
        // Trim leading and trailing whitespace
        var normalizedPin = pin.ows_stripped()

        // If this pin contains only numerals, ensure they are arabic numerals.
        if pin.digitsOnly() == normalizedPin { normalizedPin = normalizedPin.ensureArabicNumerals }

        // NFKD unicode normalization.
        return normalizedPin.decomposedStringWithCompatibilityMapping
    }

    internal enum Constants {
        static let pinSaltLengthBytes: UInt = 16
    }

    internal static func deriveEncodedPINVerificationString(pin: String) throws -> String {
        guard let pinData = normalizePin(pin).data(using: .utf8) else { throw SVR.SVRError.assertion }
        return try LibSignalClient.hashLocalPin(pinData)
    }

    internal static func verifyPIN(
        pin: String,
        againstEncodedPINVerificationString encodedPINVerificationString: String
    ) -> Bool {
        guard let pinData = normalizePin(pin).data(using: .utf8) else {
            owsFailDebug("failed to determine pin data")
            return false
        }

        do {
            return try LibSignalClient.verifyLocalPin(pinData, againstEncodedHash: encodedPINVerificationString)
        } catch {
            owsFailDebug("Failed to validate encodedVerificationString with error: \(error)")
            return false
        }
    }

    internal static func deriveSVR1EncryptionKeyAndAccessKey(
        pin: String,
        backupId: Data
    ) throws -> (encryptionKey: Data, accessKey: Data) {
        guard let pinData = normalizePin(pin).data(using: .utf8) else { throw SVR.SVRError.assertion }
        guard backupId.count == 32 else { throw SVR.SVRError.assertion }

        // From method documentation:
        // Note: This should be used with SVR1 only. For SVR1, the salt should be the backup id.
        // For SVR2 clients, use ``PinHash/init(pin:username:mrenclave:)`` which handles salt construction.
        let pinHash = try LibSignalClient.PinHash(normalizedPin: pinData, salt: backupId)

        return (encryptionKey: Data(pinHash.encryptionKey), accessKey: Data(pinHash.accessKey))
    }
}
