//
//  MVMCoreUITopAlertView+Extension.swift
//  MVMCoreUI
//
//  Created by Scott Pfeil on 9/11/20.
//  Copyright © 2020 Verizon Wireless. All rights reserved.
//

import Foundation

/// Allows top alerts to determine the status bar color and style.
protocol StatusBarUI {
    func getStatusBarUI() -> (color: UIColor, style: UIStatusBarStyle)
}

public extension MVMCoreUITopAlertView {
    
    /// Registers with the notification center to know when json is updated.
    @objc func registerWithNotificationCenter() {
        NotificationCenter.default.addObserver(self, selector: #selector(responseJSONUpdated(notification:)), name: NSNotification.Name(rawValue: NotificationResponseLoaded), object: nil)
    }
    
    private func getDelegateObject() -> MVMCoreUIDelegateObject {
        // TODO: Top alert view is current delegate. Should move to current view controller eventually?
        return MVMCoreUIDelegateObject.create(withDelegateForAll: self)
    }
    
    /// Checks for new top alert json
    @objc private func responseJSONUpdated(notification: Notification) {
        guard let loadObject = (notification.userInfo?[String(describing: MVMCoreLoadObject.self)] as? MVMCoreLoadObject) else { return }
        
        // Dismiss any top alerts that server wants us to dismiss/
        if let disableType = loadObject.responseInfoMap?.optionalStringForKey("disableType") {
            MVMCoreAlertHandler.shared()?.hideTopAlertView(ofType: disableType)
        }
        
        // Show any new top alert.
        guard let responseJSON = loadObject.responseJSON,
              let json = responseJSON.optionalDictionaryForKey(KeyTopAlert),
              let model = decodeTopNotification(with: json, delegateObject: getDelegateObject()) else { return }
        showTopAlert(with: model)
    }
    
    /// Decodes the json into a TopNotificationModel
    private func decodeTopNotification(with json: [AnyHashable: Any], delegateObject: MVMCoreUIDelegateObject?) -> TopNotificationModel? {
        do {
            return try TopNotificationModel.decode(json: json, delegateObject: delegateObject)
        } catch {
            if let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: "\(self)") {
                MVMCoreUILoggingHandler.shared()?.addError(toLog: errorObject)
            }
            return nil
        }
    }
    
    /// Shows the top alert with the model.
    func showTopAlert(with model: TopNotificationModel) {
        let object = model.createTopAlertObject()
        guard !checkAndUpdateExisting(with: object),
              let operation = MVMCoreTopAlertOperation(topAlertObject: object) else { return }
        MVMCoreAlertHandler.shared()?.add(operation)
    }
    
    /// Shows the top alert with the json.
    @objc func showTopAlert(with json: [AnyHashable: Any]) {
        guard let model = decodeTopNotification(with: json, delegateObject: getDelegateObject()) else { return }
        showTopAlert(with: model)
    }
    
    /// Checks for existing top alert object of same type and updates it. Only happens for molecular top alerts. Returns true if we updated.
    private func checkAndUpdateExisting(with topAlertObject: MVMCoreTopAlertObject) -> Bool {
        guard let queue = MVMCoreAlertHandler.shared()?.topAlertQueue.operations else { return false }
        for case let operation as MVMCoreTopAlertOperation in queue {
            guard topAlertObject.json != nil,
               operation.topAlertObject.type == topAlertObject.type else { continue }
            operation.update(with: topAlertObject)
            let pageType = (MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() as? MVMCoreViewControllerProtocol)?.pageType
            operation.updateDisplayable(byPageType: pageType)
            MVMCoreAlertHandler.shared()?.reevaluteQueue()
            return true
        }
        return false
    }
    
    /// Updates the current top alert molecule with the new object
    @objc func updateMolecule(with topAlertObject: MVMCoreTopAlertObject) {
        guard topAlertObject.type == self.topAlertObject?.type else { return }
        let delegateObject = getDelegateObject()
        guard let newJson = topAlertObject.json,
              let newModel = decodeTopNotification(with: newJson, delegateObject: delegateObject),
              let newModelName = ModelRegistry.getMoleculeClass(newModel.molecule)?.nameForReuse(with: newModel.molecule, delegateObject),
              let currentJson = self.topAlertObject?.json,
              let currentModel = decodeTopNotification(with: currentJson, delegateObject: delegateObject),
              let currentModelName = ModelRegistry.getMoleculeClass(currentModel.molecule)?.nameForReuse(with: currentModel.molecule, delegateObject),
              newModelName == currentModelName,
              let molecule = currentAlert as? MoleculeViewProtocol else {
            // Log that we couldn't update.
            if let errorObject = MVMCoreErrorObject(title: nil, message: nil, messageToLog: nil, code: ErrorCode.parsingJSON.rawValue, domain: ErrorDomainNative, location: "TopNotification update \(String(describing: topAlertObject.type))") {
                MVMCoreUILoggingHandler.shared()?.addError(toLog: errorObject)
            }
            return
        }
        MVMCoreDispatchUtility.performBlock(onMainThread: {
            // Update molecule
            molecule.reset()
            molecule.set(with: newModel.molecule, delegateObject, nil)
            (molecule as? MVMCoreViewProtocol)?.updateView(self.bounds.width)
            
            // Update status bar.
            guard let statusBarDelegate = molecule as? StatusBarUI else { return }
            let statusBarUI = statusBarDelegate.getStatusBarUI()
            self.setStatusBarColor(statusBarUI.color, statusBarStyle: statusBarUI.style)
        })
    }
    
    /// Returns the top alert molecule to use and status bar color legacy style.
    @objc func molecule(for topAlertObject: MVMCoreTopAlertObject, statusBarColor: AutoreleasingUnsafeMutablePointer<UIColor?>?, statusBarStyle: UnsafeMutablePointer<UIStatusBarStyle>?) -> UIView? {
        do {
            let delegateObject = MVMCoreUIDelegateObject.create(withDelegateForAll: self)
            guard let json = topAlertObject.json else { return nil }
            let model = try TopNotificationModel.decode(json: json, delegateObject: delegateObject)
            guard let molecule = ModelRegistry.createMolecule(model.molecule, delegateObject: delegateObject, additionalData: nil) else {
                    throw ModelRegistry.Error.decoderOther(message: "Molecule not mapped")
            }
            if let castView = molecule as? StatusBarUI {
                let (color, style) = castView.getStatusBarUI()
                statusBarColor?.pointee = color
                statusBarStyle?.pointee = style
            }
            // TODO: Temporary, waiting for actual restriction from design.
            molecule.heightAnchor.constraint(lessThanOrEqualToConstant: 140).isActive = true
            return molecule
        } catch {
            if let errorObject = MVMCoreErrorObject.createErrorObject(for: error, location: "\(self)") {
                MVMCoreUILoggingHandler.shared()?.addError(toLog: errorObject)
            }
            return nil
        }
    }
}
