//
// Copyright 2023, 2024 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.
//

import Foundation

/// A timer that adds a grace-period to the app before locking it.
class AppLockTimer {
    /// The amount of time the app should remain unlocked for whilst backgrounded.
    let gracePeriod: TimeInterval
    
    /// Whether the timer considers the app to be locked or not. Always starts with a locked app.
    ///
    /// Internally this value may be incorrect, always call `needsUnlock` to get the correct value.
    private var isLocked = true
    /// The date when the app was last backgrounded whilst in an unlocked state.
    private var lastUnlockedBackground: Date?
    
    /// Whether or not the app has moved to the background.
    ///
    /// This allows us to distinguish between `willResignActive` and `didEnterBackground`.
    private var isInBackground = false
    
    /// Creates a new timer.
    /// - Parameter gracePeriod: The amount of time the app should remain unlocked for whilst backgrounded.
    init(gracePeriod: TimeInterval) {
        self.gracePeriod = gracePeriod
    }
    
    /// Signals to the timer to track how long the app will be backgrounded for.
    func applicationDidEnterBackground(date: Date = .now) {
        isInBackground = true
        // Only update the last background date if the app is currently unlocked.
        guard !isLocked else { return }
        lastUnlockedBackground = date
    }
    
    /// Asks the timer to recompute the lock state when active. If ``applicationDidEnterBackground`` hasn't been
    /// called since the last computation, then this method won't recompute it, instead directly returning the previous state.
    func computeLockState(didBecomeActiveAt date: Date) -> Bool {
        guard isInBackground else { return isLocked }
        isInBackground = false
        
        guard !isLocked, let lastUnlockedBackground else { return true }
        
        isLocked = date.timeIntervalSince(lastUnlockedBackground) >= gracePeriod
        
        // Don't allow changing the device's clock to unlock the app.
        if date < lastUnlockedBackground {
            isLocked = true
        }
        
        return isLocked
    }
    
    /// Registers a successful unlock with the timer.
    func registerUnlock() {
        isLocked = false
    }
}
