     }
     
     func fetchEvent(withEventId eventId: String, roomId: String, allowSync: Bool = true) {
-        guard let mxSession = NotificationService.mxSession else {
-            //  there is something wrong, do not change the content
-            NSLog("[NotificationService] fetchEvent: Either originalContent or mxSession is missing.")
-            fallbackToBestAttemptContent(forEventId: eventId)
-            return
-        }
-        
-        /// Inline function to handle decryption failure
-        func handleDecryptionFailure() {
-            if allowSync {
-                NSLog("[NotificationService] fetchEvent: Launch a background sync.")
-                self.launchBackgroundSync(forEventId: eventId, roomId: roomId)
-            } else {
-                NSLog("[NotificationService] fetchEvent: Do not sync anymore.")
-                self.fallbackToBestAttemptContent(forEventId: eventId)
-            }
+        NSLog("[NotificationService] fetchEvent")
+        
+        NotificationService.backgroundSyncService.event(withEventId: eventId,
+                                                        inRoom: roomId,
+                                                        completion: { (response) in
+                                                            switch response {
+                                                            case .success(let event):
+                                                                NSLog("[NotificationService] fetchEvent: Event fetched successfully")
+                                                                self.processEvent(event)
+                                                            case .failure(let error):
+                                                                NSLog("[NotificationService] fetchEvent: error: \(error)")
+                                                                self.fallbackToBestAttemptContent(forEventId: eventId)
+                                                            }
+        })
+    }
+    
+    private func processEvent(_ event: MXEvent) {
+        guard let content = bestAttemptContents[event.eventId], let userAccount = userAccount else {
+            self.fallbackToBestAttemptContent(forEventId: event.eventId)
+            return
         }
 
-        /// Inline function to handle encryption for event, either from cache or from the backend
-        /// - Parameter event: The event to be handled
-        func handleEncryption(forEvent event: MXEvent) {
-            if !event.isEncrypted {
-                //  not encrypted, go on processing
-                NSLog("[NotificationService] fetchEvent: Event not encrypted.")
-                self.processEvent(event)
-                return
-            }
-            
-            //  encrypted
-            if event.clear != nil {
-                //  already decrypted
-                NSLog("[NotificationService] fetchEvent: Event already decrypted.")
-                self.processEvent(event)
-                return
-            }
-            
-            //  should decrypt it first
-            if mxSession.crypto.hasKeys(toDecryptEvent: event) {
-                //  we have keys to decrypt the event
-                NSLog("[NotificationService] fetchEvent: Event needs to be decrpyted, and we have the keys to decrypt it.")
-                if mxSession.decryptEvent(event, inTimeline: nil) {
-                    //  decryption succeeded
-                    NSLog("[NotificationService] fetchEvent: Event decrypted successfully.")
-                    self.processEvent(event)
-                } else {
-                    //  decryption failed
-                    NSLog("[NotificationService] fetchEvent: Decryption failed even crypto claimed it has the keys.")
-                    handleDecryptionFailure()
-                }
-            } else {
-                //  we don't have keys to decrypt the event
-                NSLog("[NotificationService] fetchEvent: Event needs to be decrpyted, but we don't have the keys to decrypt it.")
-                handleDecryptionFailure()
-            }
-        }
-        
-        //  check if we've fetched the event before
-        if let cachedEvent = self.cachedEvents[eventId] {
-            //  use cached event
-            handleEncryption(forEvent: cachedEvent)
-        } else {
-            //  do not call the /event api and just check if the event exists in the store
-            if let event = mxSession.store.event(withEventId: eventId, inRoom: roomId) {
-                NSLog("[NotificationService] fetchEvent: We have the event in store.")
-                //  cache this event
-                self.cachedEvents[eventId] = event
-                
-                //  handle encryption for this event
-                handleEncryption(forEvent: event)
-            } else if allowSync {
-                NSLog("[NotificationService] fetchEvent: We don't have the event in store. Launch a background sync to fetch it.")
-                self.launchBackgroundSync(forEventId: eventId, roomId: roomId)
-            } else {
-                NSLog("[NotificationService] fetchEvent: We don't have the event in store. Do not sync anymore.")
-                self.fallbackToBestAttemptContent(forEventId: eventId)
-            }
-        }
-    }
-    
-    func launchBackgroundSync(forEventId eventId: String, roomId: String) {
-        guard let mxSession = NotificationService.mxSession else {
-            NSLog("[NotificationService] launchBackgroundSync: mxSession is missing.")
-            self.fallbackToBestAttemptContent(forEventId: eventId)
-            return
-        }
-        
-        //  launch a background sync, set serverTimeout as 0.1 to avoid session to decide continue catching up
-        mxSession.backgroundSync(withTimeout: 20, serverTimeout: 0.1, ignoreSessionState: true) { [weak self] (response) in
-            switch response {
-            case .success(let syncResponse):
-                guard let self = self else {
-                    NSLog("[NotificationService] launchBackgroundSync: MXSession.initialBackgroundSync returned too late successfully")
-                    return
-                }
-
-                NotificationService.syncResponseStore?.update(with: syncResponse)
-
-                //  do not allow to sync anymore
-                self.fetchEvent(withEventId: eventId, roomId: roomId, allowSync: false)
-                break
-            case .failure(let error):
-                guard let self = self else {
-                    NSLog("[NotificationService] launchBackgroundSync: MXSession.initialBackgroundSync returned too late with error: \(String(describing: error))")
-                    return
-                }
-                NSLog("[NotificationService] launchBackgroundSync: MXSession.initialBackgroundSync returned with error: \(String(describing: error))")
-                self.fallbackToBestAttemptContent(forEventId: eventId)
-                break
-            }
-        }
-    }
-    
-    func processEvent(_ event: MXEvent) {
-        guard let content = bestAttemptContents[event.eventId], let mxSession = NotificationService.mxSession else {
-            self.fallbackToBestAttemptContent(forEventId: event.eventId)
-            return
-        }
-
-        self.notificationContent(forEvent: event, inSession: mxSession) { (notificationContent) in
+        self.notificationContent(forEvent: event, forAccount: userAccount) { (notificationContent) in
             var isUnwantedNotification = false
             
             // Modify the notification content here...

    }
    
    func setup(withRoomId roomId: String, eventId: String, completion: @escaping () -> Void) {
        if let userAccount = MXKAccountManager.shared()?.activeAccounts.first {
            if NotificationService.syncResponseStore == nil {
                NotificationService.syncResponseStore = SyncResponseFileStore()
                NotificationService.syncResponseStore?.open(withCredentials: userAccount.mxCredentials)
            }
            if NotificationService.mxSession == nil {
                let store = NSEMemoryStore(withCredentials: userAccount.mxCredentials)
                NotificationService.mxSession = MXSession(matrixRestClient: MXRestClient(credentials: userAccount.mxCredentials, unrecognizedCertificateHandler: nil))
                NotificationService.mxSession?.setStore(store, completion: { (response) in
                    switch response {
                    case .success:
                        completion()
                        break
                    case .failure(let error):
                        NSLog("[NotificationService] setup: MXSession.setStore method returned error: \(String(describing: error))")
                        self.fallbackToBestAttemptContent(forEventId: eventId)
                        break
                    }
                })
            } else {
                NSLog("[NotificationService] Instance: Reusing session")
                completion()
            }
        } else {
            NSLog("[NotificationService] setup: No active accounts")
            fallbackToBestAttemptContent(forEventId: eventId)

        contentHandlers[eventId]?(content)
    }
    
    private func notificationContent(forEvent event: MXEvent, forAccount account: MXKAccount, onComplete: @escaping (UNNotificationContent?) -> Void) {
        guard let content = event.content, content.count > 0 else {
            NSLog("[NotificationService] notificationContentForEvent: empty event content")
            onComplete(nil)
            return
        }
        
        let roomId = event.roomId!
        let isRoomMentionsOnly = NotificationService.backgroundSyncService.isRoomMentionsOnly(roomId)
        let roomSummary = NotificationService.backgroundSyncService.roomSummary(forRoomId: roomId)
        
        NSLog("[NotificationService] notificationContentForEvent: Attempt to fetch the room state")
        
        NotificationService.backgroundSyncService.roomState(forRoomId: roomId, completion: { (response) in
            switch response {
            case .success(let roomState):
                var notificationTitle: String?
                var notificationBody: String?
                
                var threadIdentifier: String? = roomId
                let eventSenderName = roomState.members.memberName(event.sender)
                let currentUserId = account.mxCredentials.userId
                let roomDisplayName = roomSummary?.displayname
                let pushRule = NotificationService.backgroundSyncService.pushRule(matching: event, roomState: roomState)
                
                switch event.eventType {
                case .callInvite:
                    let offer = event.content["offer"] as? [AnyHashable: Any]
                    let sdp = offer?["sdp"] as? String
                    let isVideoCall = sdp?.contains("m=video") ?? false
                    
                    if isVideoCall {
                        notificationBody = NSString.localizedUserNotificationString(forKey: "VIDEO_CALL_FROM_USER", arguments: [eventSenderName as Any])
                    } else {
                        notificationBody = NSString.localizedUserNotificationString(forKey: "VOICE_CALL_FROM_USER", arguments: [eventSenderName as Any])
                    }
                    
                    // call notifications should stand out from normal messages, so we don't stack them
                    threadIdentifier = nil
                    self.sendVoipPush(forEvent: event)
                case .roomMessage, .roomEncrypted:
                    if isRoomMentionsOnly {
                        // A local notification will be displayed only for highlighted notification.
                        var isHighlighted = false
                        
                        // Check whether is there an highlight tweak on it
                        for ruleAction in pushRule?.actions ?? [] {
                            guard let action = ruleAction as? MXPushRuleAction else { continue }
                            guard action.actionType == MXPushRuleActionTypeSetTweak else { continue }
                            guard action.parameters["set_tweak"] as? String == "highlight" else { continue }
                            // Check the highlight tweak "value"
                            // If not present, highlight. Else check its value before highlighting
                            if nil == action.parameters["value"] || true == (action.parameters["value"] as? Bool) {
                                isHighlighted = true
                                break
                            }
                        }
                        
                        if !isHighlighted {
                            // Ignore this notif.
                            NSLog("[NotificationService] notificationContentForEvent: Ignore non highlighted notif in mentions only room")
                            onComplete(nil)
                            return
                        }
                    }
                    
                    var msgType = event.content["msgtype"] as? String
                    let messageContent = event.content["body"] as? String
                    
                    if event.isEncrypted && !self.showDecryptedContentInNotifications {
                        // Hide the content
                        msgType = nil
                    }
                    
                    let isIncomingEvent = event.sender != currentUserId
                    
                    // Display the room name only if it is different than the sender name
                    if roomDisplayName != nil && roomDisplayName != eventSenderName {
                        notificationTitle = NSString.localizedUserNotificationString(forKey: "MSG_FROM_USER_IN_ROOM_TITLE", arguments: [eventSenderName as Any, roomDisplayName as Any])
                        
                        if msgType == kMXMessageTypeText {
                            notificationBody = messageContent
                        } else if msgType == kMXMessageTypeEmote {
                            notificationBody = NSString.localizedUserNotificationString(forKey: "ACTION_FROM_USER", arguments: [eventSenderName as Any, messageContent as Any])
                        } else if msgType == kMXMessageTypeImage {
                            notificationBody = NSString.localizedUserNotificationString(forKey: "IMAGE_FROM_USER", arguments: [eventSenderName as Any, messageContent as Any])
                        } else {
                            // Encrypted messages falls here
                            notificationBody = NSString.localizedUserNotificationString(forKey: "MESSAGE", arguments: [])
                        }
                    } else {
                        notificationTitle = eventSenderName
                        
                        switch msgType {
                        case kMXMessageTypeText:
                            notificationBody = messageContent
                            break
                        case kMXMessageTypeEmote:
                            notificationBody = NSString.localizedUserNotificationString(forKey: "ACTION_FROM_USER", arguments: [eventSenderName as Any, messageContent as Any])
                            break
                        case kMXMessageTypeImage:
                            notificationBody = NSString.localizedUserNotificationString(forKey: "IMAGE_FROM_USER", arguments: [eventSenderName as Any, messageContent as Any])
                            break
                        default:
                            // Encrypted messages falls here
                            notificationBody = NSString.localizedUserNotificationString(forKey: "MESSAGE", arguments: [])
                            break
                        }
                    }
                    break
                case .roomMember:
                    if roomDisplayName != nil && roomDisplayName != eventSenderName {
                        notificationBody = NSString.localizedUserNotificationString(forKey: "USER_INVITE_TO_NAMED_ROOM", arguments: [eventSenderName as Any, roomDisplayName as Any])
                    } else {
                        notificationBody = NSString.localizedUserNotificationString(forKey: "USER_INVITE_TO_CHAT", arguments: [eventSenderName as Any])
                    }
                case .sticker:
                    if roomDisplayName != nil && roomDisplayName != eventSenderName {
                        notificationTitle = NSString.localizedUserNotificationString(forKey: "MSG_FROM_USER_IN_ROOM_TITLE", arguments: [eventSenderName as Any, roomDisplayName as Any])
                    } else {
                        notificationTitle = eventSenderName
                    }
                    
                    notificationBody = NSString.localizedUserNotificationString(forKey: "STICKER_FROM_USER", arguments: [eventSenderName as Any])
                default:
                    break
                }
                
                if self.localAuthenticationService.isProtectionSet {
                    NSLog("[NotificationService] notificationContentForEvent: Resetting title and body because app protection is set")
                    notificationBody = NSString.localizedUserNotificationString(forKey: "MESSAGE_PROTECTED", arguments: [])
                    notificationTitle = nil
                }
                
                guard notificationBody != nil else {
                    NSLog("[NotificationService] notificationContentForEvent: notificationBody is nil")
                    onComplete(nil)
                    return
                }
                
                let notificationContent = self.notificationContent(withTitle: notificationTitle,
                                                                   body: notificationBody,
                                                                   threadIdentifier: threadIdentifier,
                                                                   userId: currentUserId,
                                                                   event: event,
                                                                   pushRule: pushRule)
                
                NSLog("[NotificationService] notificationContentForEvent: Calling onComplete.")
                onComplete(notificationContent)
            case .failure(let error):
                NSLog("[NotificationService] notificationContentForEvent: error: \(error)")
                onComplete(nil)
            }
        })
    }
    
    func notificationContent(withTitle title: String?,
