// Copyright © 2024 Rangeproof Pty Ltd. All rights reserved.

import UIKit

// MARK: - Singleton

public extension Singleton {
    // FIXME: This will be reworked to be part of dependencies in the Groups Rebuild branch
    fileprivate static var _appContext: Atomic<AppContext?> = Atomic(nil)
    static var appContext: AppContext { _appContext.wrappedValue! }
    static var hasAppContext: Bool { _appContext.wrappedValue != nil }
    
    static func setup(appContext: AppContext) { _appContext.mutate { $0 = appContext } }
}

// MARK: - AppContext

public protocol AppContext: AnyObject {
    var _temporaryDirectory: String? { get set }
    var isMainApp: Bool { get }
    var isMainAppAndActive: Bool { get }
    var isShareExtension: Bool { get }
    var reportedApplicationState: UIApplication.State { get }
    var mainWindow: UIWindow? { get }
    var isRTL: Bool { get }
    var frontmostViewController: UIViewController? { get }
    var backgroundTimeRemaining: TimeInterval { get }
    
    func setMainWindow(_ mainWindow: UIWindow)
    func ensureSleepBlocking(_ shouldBeBlocking: Bool, blockingObjects: [Any])
    func beginBackgroundTask(expirationHandler: @escaping () -> ()) -> UIBackgroundTaskIdentifier
    func endBackgroundTask(_ backgroundTaskIdentifier: UIBackgroundTaskIdentifier)
    
    /// **Note:** We need to call this method on launch _and_ every time the app becomes active,
    /// since file protection may prevent it from succeeding in the background.
    func clearOldTemporaryDirectories()
}

// MARK: - Defaults

public extension AppContext {
    var isMainApp: Bool { false }
    var isMainAppAndActive: Bool { false }
    var isShareExtension: Bool { false }
    var mainWindow: UIWindow? { nil }
    var frontmostViewController: UIViewController? { nil }
    var backgroundTimeRemaining: TimeInterval { 0 }
    
    // Note: CallKit will make the app state as .inactive
    var isInBackground: Bool { reportedApplicationState == .background }
    var isNotInForeground: Bool { reportedApplicationState != .active }
    var isAppForegroundAndActive: Bool { reportedApplicationState == .active }
    
    // MARK: - Paths
    
    var appUserDefaults: UserDefaults {
        return (UserDefaults.sharedLokiProject ?? UserDefaults.standard)
    }
    
    var temporaryDirectory: String {
        if let dir: String = _temporaryDirectory { return dir }
        
        let dirName: String = "ows_temp_\(UUID().uuidString)"   // stringlint:ignore
        let dirPath: String = URL(fileURLWithPath: NSTemporaryDirectory())
            .appendingPathComponent(dirName)
            .path
        _temporaryDirectory = dirPath
        FileSystem.temporaryDirectory.mutate { $0 = dirPath }
        try? FileSystem.ensureDirectoryExists(at: dirPath, fileProtectionType: .complete)
        
        return dirPath
    }
    
    var temporaryDirectoryAccessibleAfterFirstAuth: String {
        let dirPath: String = NSTemporaryDirectory()
        try? FileSystem.ensureDirectoryExists(at: dirPath, fileProtectionType: .completeUntilFirstUserAuthentication)
        
        return dirPath;
    }
    
    var appDocumentDirectoryPath: String {
        let targetPath: String? = FileManager.default
            .urls(for: .documentDirectory, in: .userDomainMask)
            .last?
            .path
        Log.assert(targetPath != nil)
        
        return (targetPath ?? "")
    }
    
    // MARK: - Functions
    
    func setMainWindow(_ mainWindow: UIWindow) {}
    func ensureSleepBlocking(_ shouldBeBlocking: Bool, blockingObjects: [Any]) {}
    func beginBackgroundTask(expirationHandler: @escaping () -> ()) -> UIBackgroundTaskIdentifier { return .invalid }
    func endBackgroundTask(_ backgroundTaskIdentifier: UIBackgroundTaskIdentifier) {}
    
    func clearOldTemporaryDirectories() {}
}

// MARK: - Objective C Support

// FIXME: Remove this once the OWSFileSystem has been refactored to Swift
@objc public class OWSCurrentAppContext: NSObject {
    @objc public static var isRTL: Bool { Singleton.appContext.isRTL }
    @objc public static var isMainApp: Bool { Singleton.appContext.isMainApp }
    @objc public static var isMainAppAndActive: Bool { Singleton.appContext.isMainAppAndActive }
    @objc public static var isAppForegroundAndActive: Bool { Singleton.appContext.isAppForegroundAndActive }
    @objc public static var temporaryDirectory: String { Singleton.appContext.temporaryDirectory }
    @objc public static var appUserDefaults: UserDefaults { Singleton.appContext.appUserDefaults }
    @objc public static var appDocumentDirectoryPath: String { Singleton.appContext.appDocumentDirectoryPath }
    
    // FIXME: This will be reworked to be part of dependencies in the Groups Rebuild branch
    @objc static var appSharedDataDirectoryPath: String {
        let targetPath: String? = FileManager.default
            .containerURL(forSecurityApplicationGroupIdentifier: UserDefaults.applicationGroup)?
            .path
        Log.assert(targetPath != nil)
        
        return (targetPath ?? "")
    }
    
    @objc static func beginBackgroundTask(expirationHandler: @escaping () -> ()) -> UIBackgroundTaskIdentifier {
        return Singleton.appContext.beginBackgroundTask { expirationHandler() }
    }
    
    @objc static func endBackgroundTask(_ backgroundTaskIdentifier: UIBackgroundTaskIdentifier) {
        Singleton.appContext.endBackgroundTask(backgroundTaskIdentifier)
    }
}
