//
//  PostInstallationKeychainCleaner.swift
//  ADUtils
//
//  Created by Benjamin Lavialle on 07/07/2021.
//

import Foundation

/**
 A protocol definign the capacity to wipe a keychain,
 his is a proxy to any helper that might be use to access the keychain

 For example for `KeychainAccess` https://github.com/kishikawakatsumi/KeychainAccess
 ```
 extension Keychain: KeychainWiper {

     func wipeKeychain() throws {
         try removeAll()
     }
 }
 ```
 Lets use a `Keychain` object as `KeychainWiper`
 */
public protocol KeychainWiper {
    func wipeKeychain() throws
}

/**
 * A class that can be used to check if the keychain as to be wiped at launch.
 * This ensure the keychain is not persisted across application installation & deletion
 * The test is based on setting a boolean flag in the user defaults.
 */

public class PostInstallationKeychainCleaner {

    private enum Constant {
        static let ApplicationLaunchedFlag = "_PIKC_ALF"
    }

    private let keychainWipers: [KeychainWiper]
    private let userDefaults: UserDefaults

    /**
     Creates a `PostInstallationKeychainCleaner` with the given user defaults and a single keychain wiper
     - parameter keychainWiper: The keychain wiper to call if wipe is necessary
     - parameter userDefaults: The user defaults used to store and check the first launch flag
     */

    public convenience init(keychainWiper: KeychainWiper,
                            userDefaults: UserDefaults) {
        self.init(keychainWipers: [keychainWiper], userDefaults: userDefaults)
    }

    /**
     Creates a `PostInstallationKeychainCleaner` with the given user defaults and a single keychain wiper
     - parameter keychainWipers: The keychain wipers to call if wipe is necessary
     - parameter userDefaults: The user defaults used to store and check the first launch flag
     */

    public init(keychainWipers: [KeychainWiper],
                userDefaults: UserDefaults) {
        self.keychainWipers = keychainWipers
        self.userDefaults = userDefaults
    }

    /**
     Check the presence of the "application already launched after installation flag"
     and trigger keychain wipes if necessary
     */

    public func checkInstallation() throws {
        guard !userDefaults.bool(forKey: Constant.ApplicationLaunchedFlag) else { return }
        try wipeKeychains()
        userDefaults.set(true, forKey: Constant.ApplicationLaunchedFlag)
        userDefaults.synchronize()
    }

    /**
     Convenience method to manually wipe keychains, for example to be used after user logout
     */

    public func wipeKeychains() throws {
        try keychainWipers.forEach { try $0.wipeKeychain() }
    }
}