//
//  AlertHandler.swift
//  MVMCore
//
//  Created by Scott Pfeil on 4/10/23.
//  Copyright © 2023 myverizon. All rights reserved.
//

import MVMCore

public class AlertHandler {
    
    /// Returns the handler stored in the CoreUIObject
    public static func shared() -> Self {
        return MVMCoreActionUtility.fatalClassCheck(object: CoreUIObject.sharedInstance()?.alertHandler)
    }
    
    /// The operation queue of alert operations.
    private var queue = {
        let queue = OperationQueue()
        queue.maxConcurrentOperationCount = 1
        return queue
    }()
    
    public init() {}
    
    /// Returns if an alert is currently showing in the hierarchy, even if it is not the top presented view.
    public func isAlertShowing() -> Bool {
        return queue.operations.contains(where: { operation in
            return !operation.isCancelled &&
            !operation.isFinished &&
            operation.isExecuting
        })
    }
    
    /// Returns if a greedy alert is currently showing in the hierarchy, even if it is not the top presented view.
    public func isGreedyAlertShowing() -> Bool {
        return queue.operations.contains(where: { operation in
            return !operation.isCancelled &&
            !operation.isFinished &&
            operation.isExecuting &&
            (operation as? AlertOperation)?.alertObject.isGreedy ?? false
        })
    }
    
    @MainActor
    public func createAlertController(with alertModel: AlertModelProtocol) -> AlertController {
        // ActionSheets are not supported on iPad interfaces without a source rect (i.e. a source element) which isn't currently supported for our generic handling.
        // TODO: Find a way to support this.
        var alertStyle = alertModel.preferredStyle
        if alertStyle == .actionSheet, UIDevice.current.userInterfaceIdiom != .phone {
            alertStyle = .alert
        }
        
        // Create the alert. Adds the actions one by one.
        let alertController = AlertController(title: alertModel.title, message: alertModel.message, preferredStyle: alertStyle)
        for action in alertModel.actions {
            alertController.addAction(action)
        }
        if let index = alertModel.preferredActionIndex {
            alertController.preferredAction = alertModel.actions[index]
        }
        return alertController
    }
    
    /// Shows an alert using the alert object.
    @MainActor
    public func queueAlertToShow(with alertObject: AlertObject) -> UIAlertController {

        // It's a greedy alert! Clear all alerts that are queued up and the one that is showing
        if alertObject.isGreedy {
            removeAllAlertViews()
        }
        
        let alertController = createAlertController(with: alertObject.alertModel)
        let alertOperation = AlertOperation(with: alertController, alertObject: alertObject)
        queue.addOperation(alertOperation)
        return alertController
    }
    
    /** Iterates through all scheduled alerts and cancels any that match the provided predicate.
    *  @param predicate       The predicate block to decide whether to cancel an alert.
    */
    public func cancelAlert(using predicate: ((AlertObject) -> Bool)) {
        for case let operation as AlertOperation in queue.operations {
            if predicate(operation.alertObject) {
                operation.cancel()
            }
        }
    }
    
    /// Cancels all current alerts
    public func removeAllAlertViews() {
        queue.cancelAllOperations()
    }
}
