import Foundation
import UIKit
import AsyncDisplayKit
import Display
import SwiftSignalKit
import Postbox
import TelegramCore
import TelegramPresentationData
import AccountContext
import ChatPresentationInterfaceState
import ChatInterfaceState
import TelegramNotices
import PresentationDataUtils
import TelegramCallsUI
import AttachmentUI

func updateChatPresentationInterfaceStateImpl(
    selfController: ChatControllerImpl,
    transition: ContainedViewLayoutTransition,
    interactive: Bool,
    saveInterfaceState: Bool,
    _ f: (ChatPresentationInterfaceState) -> ChatPresentationInterfaceState,
    completion externalCompletion: @escaping (ContainedViewLayoutTransition) -> Void
) {
    var completion = externalCompletion
    var temporaryChatPresentationInterfaceState = f(selfController.presentationInterfaceState)
    
    if selfController.presentationInterfaceState.keyboardButtonsMessage?.visibleButtonKeyboardMarkup != temporaryChatPresentationInterfaceState.keyboardButtonsMessage?.visibleButtonKeyboardMarkup || selfController.presentationInterfaceState.keyboardButtonsMessage?.id != temporaryChatPresentationInterfaceState.keyboardButtonsMessage?.id {
        if let keyboardButtonsMessage = temporaryChatPresentationInterfaceState.keyboardButtonsMessage, let keyboardMarkup = keyboardButtonsMessage.visibleButtonKeyboardMarkup {
            if selfController.presentationInterfaceState.interfaceState.editMessage == nil && selfController.presentationInterfaceState.interfaceState.composeInputState.inputText.length == 0 && keyboardButtonsMessage.id != temporaryChatPresentationInterfaceState.interfaceState.messageActionsState.closedButtonKeyboardMessageId && keyboardButtonsMessage.id != temporaryChatPresentationInterfaceState.interfaceState.messageActionsState.dismissedButtonKeyboardMessageId && temporaryChatPresentationInterfaceState.botStartPayload == nil {
                temporaryChatPresentationInterfaceState = temporaryChatPresentationInterfaceState.updatedInputMode({ _ in
                    return .inputButtons(persistent: keyboardMarkup.flags.contains(.persistent))
                })
            }
            
            if case let .peer(peerId) = selfController.chatLocation, peerId.namespace == Namespaces.Peer.CloudChannel || peerId.namespace == Namespaces.Peer.CloudGroup {
                if temporaryChatPresentationInterfaceState.interfaceState.replyMessageSubject == nil && temporaryChatPresentationInterfaceState.interfaceState.messageActionsState.processedSetupReplyMessageId != keyboardButtonsMessage.id  {
                    temporaryChatPresentationInterfaceState = temporaryChatPresentationInterfaceState.updatedInterfaceState({
                        $0.withUpdatedReplyMessageSubject(ChatInterfaceState.ReplyMessageSubject(
                            messageId: keyboardButtonsMessage.id,
                            quote: nil
                        )).withUpdatedMessageActionsState({ value in
                        var value = value
                        value.processedSetupReplyMessageId = keyboardButtonsMessage.id
                        return value
                    }) })
                }
            }
        } else {
            temporaryChatPresentationInterfaceState = temporaryChatPresentationInterfaceState.updatedInputMode({ mode in
                if case .inputButtons = mode {
                    return .text
                } else {
                    return mode
                }
            })
        }
    }
    
    if let keyboardButtonsMessage = temporaryChatPresentationInterfaceState.keyboardButtonsMessage, keyboardButtonsMessage.requestsSetupReply {
        if temporaryChatPresentationInterfaceState.interfaceState.replyMessageSubject == nil && temporaryChatPresentationInterfaceState.interfaceState.messageActionsState.processedSetupReplyMessageId != keyboardButtonsMessage.id  {
            temporaryChatPresentationInterfaceState = temporaryChatPresentationInterfaceState.updatedInterfaceState({ $0.withUpdatedReplyMessageSubject(ChatInterfaceState.ReplyMessageSubject(
                messageId: keyboardButtonsMessage.id,
                quote: nil
            )).withUpdatedMessageActionsState({ value in
                var value = value
                value.processedSetupReplyMessageId = keyboardButtonsMessage.id
                return value
            }) })
        }
    }
    
    let inputTextPanelState = inputTextPanelStateForChatPresentationInterfaceState(temporaryChatPresentationInterfaceState, context: selfController.context)
    var updatedChatPresentationInterfaceState = temporaryChatPresentationInterfaceState.updatedInputTextPanelState({ _ in return inputTextPanelState })
    
    let contextQueryUpdates = contextQueryResultStateForChatInterfacePresentationState(updatedChatPresentationInterfaceState, context: selfController.context, currentQueryStates: &selfController.contextQueryStates, requestBotLocationStatus: { [weak selfController] peerId in
        guard let selfController else {
            return
        }
        let _ = (ApplicationSpecificNotice.updateInlineBotLocationRequestState(accountManager: selfController.context.sharedContext.accountManager, peerId: peerId, timestamp: Int32(Date().timeIntervalSince1970 + 10 * 60))
        |> deliverOnMainQueue).startStandalone(next: { [weak selfController] value in
            guard let selfController, value else {
                return
            }
            selfController.present(textAlertController(context: selfController.context, updatedPresentationData: selfController.updatedPresentationData, title: nil, text: selfController.presentationData.strings.Conversation_ShareInlineBotLocationConfirmation, actions: [TextAlertAction(type: .defaultAction, title: selfController.presentationData.strings.Common_Cancel, action: {
            }), TextAlertAction(type: .defaultAction, title: selfController.presentationData.strings.Common_OK, action: { [weak selfController] in
                guard let selfController else {
                    return
                }
                let _ = ApplicationSpecificNotice.setInlineBotLocationRequest(accountManager: selfController.context.sharedContext.accountManager, peerId: peerId, value: 0).startStandalone()
            })]), in: .window(.root))
        })
    })
    
    for (kind, update) in contextQueryUpdates {
        switch update {
        case .remove:
            if let (_, disposable) = selfController.contextQueryStates[kind] {
                disposable.dispose()
                selfController.contextQueryStates.removeValue(forKey: kind)
                
                updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedInputQueryResult(queryKind: kind, { _ in
                    return nil
                })
            }
            if case .contextRequest = kind {
                selfController.performingInlineSearch.set(false)
            }
        case let .update(query, signal):
            let currentQueryAndDisposable = selfController.contextQueryStates[kind]
            currentQueryAndDisposable?.1.dispose()
            
            var inScope = true
            var inScopeResult: ((ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?)?
            selfController.contextQueryStates[kind] = (query, (signal
            |> deliverOnMainQueue).startStrict(next: { [weak selfController] result in
                guard let selfController else {
                    return
                }
                if Thread.isMainThread && inScope {
                    inScope = false
                    inScopeResult = result
                } else {
                    selfController.updateChatPresentationInterfaceState(animated: true, interactive: false, {
                        $0.updatedInputQueryResult(queryKind: kind, { previousResult in
                            return result(previousResult)
                        })
                    })
                }
            }, error: { [weak selfController] error in
                guard let selfController else {
                    return
                }
                if case .contextRequest = kind {
                    selfController.performingInlineSearch.set(false)
                }
                
                switch error {
                case .generic:
                    break
                case let .inlineBotLocationRequest(peerId):
                    selfController.present(textAlertController(context: selfController.context, updatedPresentationData: selfController.updatedPresentationData, title: nil, text: selfController.presentationData.strings.Conversation_ShareInlineBotLocationConfirmation, actions: [TextAlertAction(type: .defaultAction, title: selfController.presentationData.strings.Common_Cancel, action: { [weak selfController] in
                        guard let selfController else {
                            return
                        }
                        let _ = ApplicationSpecificNotice.setInlineBotLocationRequest(accountManager: selfController.context.sharedContext.accountManager, peerId: peerId, value: Int32(Date().timeIntervalSince1970 + 10 * 60)).startStandalone()
                    }), TextAlertAction(type: .defaultAction, title: selfController.presentationData.strings.Common_OK, action: { [weak selfController] in
                        guard let selfController else {
                            return
                        }
                        let _ = ApplicationSpecificNotice.setInlineBotLocationRequest(accountManager: selfController.context.sharedContext.accountManager, peerId: peerId, value: 0).startStandalone()
                    })]), in: .window(.root))
                }
            }, completed: { [weak selfController] in
                guard let selfController else {
                    return
                }
                if case .contextRequest = kind {
                    selfController.performingInlineSearch.set(false)
                }
            }))
            inScope = false
            if let inScopeResult = inScopeResult {
                updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedInputQueryResult(queryKind: kind, { previousResult in
                    return inScopeResult(previousResult)
                })
            } else {
                if case .contextRequest = kind {
                    selfController.performingInlineSearch.set(true)
                }
            }
        
            if case let .peer(peerId) = selfController.chatLocation, peerId.namespace == Namespaces.Peer.SecretChat {
                if case .contextRequest = query {
                    let _ = (ApplicationSpecificNotice.getSecretChatInlineBotUsage(accountManager: selfController.context.sharedContext.accountManager)
                    |> deliverOnMainQueue).startStandalone(next: { [weak selfController] value in
                        guard let selfController, !value else {
                            return
                        }
                        let _ = ApplicationSpecificNotice.setSecretChatInlineBotUsage(accountManager: selfController.context.sharedContext.accountManager).startStandalone()
                        selfController.present(textAlertController(context: selfController.context, updatedPresentationData: selfController.updatedPresentationData, title: nil, text: selfController.presentationData.strings.Conversation_SecretChatContextBotAlert, actions: [TextAlertAction(type: .defaultAction, title: selfController.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
                    })
                }
            }
        }
    }
    
    var isBot = false
    if let peer = updatedChatPresentationInterfaceState.renderedPeer?.peer as? TelegramUser, peer.botInfo != nil {
        isBot = true
    } else {
        isBot = false
    }
    selfController.chatDisplayNode.historyNode.chatHasBots = updatedChatPresentationInterfaceState.hasBots || isBot
    
    if let (updatedSearchQuerySuggestionState, updatedSearchQuerySuggestionSignal) = searchQuerySuggestionResultStateForChatInterfacePresentationState(updatedChatPresentationInterfaceState, context: selfController.context, currentQuery: selfController.searchQuerySuggestionState?.0) {
        selfController.searchQuerySuggestionState?.1.dispose()
        var inScope = true
        var inScopeResult: ((ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?)?
        selfController.searchQuerySuggestionState = (updatedSearchQuerySuggestionState, (updatedSearchQuerySuggestionSignal |> deliverOnMainQueue).startStrict(next: { [weak selfController] result in
            guard let selfController else {
                return
            }
            if Thread.isMainThread && inScope {
                inScope = false
                inScopeResult = result
            } else {
                selfController.updateChatPresentationInterfaceState(animated: true, interactive: false, {
                    $0.updatedSearchQuerySuggestionResult { previousResult in
                        return result(previousResult)
                    }
                })
            }
        }))
        inScope = false
        if let inScopeResult = inScopeResult {
            updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedSearchQuerySuggestionResult { previousResult in
                return inScopeResult(previousResult)
            }
        }
    }
    
    var canHaveUrlPreview = true
    if case let .customChatContents(customChatContents) = updatedChatPresentationInterfaceState.subject {
        switch customChatContents.kind {
        case .hashTagSearch:
            break
        case .quickReplyMessageInput:
            break
        case .businessLinkSetup:
            canHaveUrlPreview = false
        }
    }
    
    if canHaveUrlPreview, let (updatedUrlPreviewState, updatedUrlPreviewSignal) = urlPreviewStateForInputText(updatedChatPresentationInterfaceState.interfaceState.composeInputState.inputText, context: selfController.context, currentQuery: selfController.urlPreviewQueryState?.0, forPeerId: selfController.chatLocation.peerId) {
        selfController.urlPreviewQueryState?.1.dispose()
        var inScope = true
        var inScopeResult: ((TelegramMediaWebpage?) -> (TelegramMediaWebpage, String)?)?
        let linkPreviews: Signal<Bool, NoError>
        if case let .peer(peerId) = selfController.chatLocation, peerId.namespace == Namespaces.Peer.SecretChat {
            linkPreviews = interactiveChatLinkPreviewsEnabled(accountManager: selfController.context.sharedContext.accountManager, displayAlert: { [weak selfController] f in
                guard let selfController else {
                    return
                }
                selfController.present(textAlertController(context: selfController.context, updatedPresentationData: selfController.updatedPresentationData, title: nil, text: selfController.presentationData.strings.Conversation_SecretLinkPreviewAlert, actions: [
                    TextAlertAction(type: .defaultAction, title: selfController.presentationData.strings.Common_Yes, action: {
                    f.f(true)
                }), TextAlertAction(type: .genericAction, title: selfController.presentationData.strings.Common_No, action: {
                    f.f(false)
                })]), in: .window(.root))
            })
        } else {
            var bannedEmbedLinks = false
            if let channel = selfController.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.hasBannedPermission(.banEmbedLinks) != nil {
                bannedEmbedLinks = true
            } else if let group = selfController.presentationInterfaceState.renderedPeer?.peer as? TelegramGroup, group.hasBannedPermission(.banEmbedLinks) {
                bannedEmbedLinks = true
            }
            if bannedEmbedLinks {
                linkPreviews = .single(false)
            } else {
                linkPreviews = .single(true)
            }
        }
        let filteredPreviewSignal = linkPreviews
        |> take(1)
        |> mapToSignal { value -> Signal<(TelegramMediaWebpage?) -> (TelegramMediaWebpage, String)?, NoError> in
            if value {
                return updatedUrlPreviewSignal
            } else {
                return .single({ _ in return nil })
            }
        }
        
        selfController.urlPreviewQueryState = (updatedUrlPreviewState, (filteredPreviewSignal |> deliverOnMainQueue).startStrict(next: { [weak selfController] result in
            guard let selfController else {
                return
            }
            if Thread.isMainThread && inScope {
                inScope = false
                inScopeResult = result
            } else {
                selfController.updateChatPresentationInterfaceState(animated: true, interactive: false, {
                    if let (webpage, webpageUrl) = result($0.urlPreview?.webPage) {
                        let updatedPreview = ChatPresentationInterfaceState.UrlPreview(
                            url: webpageUrl,
                            webPage: webpage,
                            positionBelowText: $0.urlPreview?.positionBelowText ?? true,
                            largeMedia: $0.urlPreview?.largeMedia
                        )
                        return $0.updatedUrlPreview(updatedPreview)
                    } else {
                        return $0.updatedUrlPreview(nil)
                    }
                })
            }
        }))
        inScope = false
        if let inScopeResult = inScopeResult {
            if let (webpage, webpageUrl) = inScopeResult(updatedChatPresentationInterfaceState.urlPreview?.webPage) {
                let updatedPreview = ChatPresentationInterfaceState.UrlPreview(
                    url: webpageUrl,
                    webPage: webpage,
                    positionBelowText: updatedChatPresentationInterfaceState.urlPreview?.positionBelowText ?? true,
                    largeMedia: updatedChatPresentationInterfaceState.urlPreview?.largeMedia
                )
                updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedUrlPreview(updatedPreview)
            } else {
                updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedUrlPreview(nil)
            }
        }
    }
    
    let isEditingMedia: Bool = updatedChatPresentationInterfaceState.editMessageState?.content != .plaintext
    let editingUrlPreviewText: NSAttributedString? = isEditingMedia ? nil : updatedChatPresentationInterfaceState.interfaceState.editMessage?.inputState.inputText
    if let (updatedEditingUrlPreviewState, updatedEditingUrlPreviewSignal) = urlPreviewStateForInputText(editingUrlPreviewText, context: selfController.context, currentQuery: selfController.editingUrlPreviewQueryState?.0, forPeerId: selfController.chatLocation.peerId) {
        selfController.editingUrlPreviewQueryState?.1.dispose()
        var inScope = true
        var inScopeResult: ((TelegramMediaWebpage?) -> (TelegramMediaWebpage, String)?)?
        selfController.editingUrlPreviewQueryState = (updatedEditingUrlPreviewState, (updatedEditingUrlPreviewSignal |> deliverOnMainQueue).startStrict(next: { [weak selfController] result in
            guard let selfController else {
                return
            }
            if Thread.isMainThread && inScope {
                inScope = false
                inScopeResult = result
            } else {
                selfController.updateChatPresentationInterfaceState(animated: true, interactive: false, {
                    if let (webpage, webpageUrl) = result($0.editingUrlPreview?.webPage) {
                        let updatedPreview = ChatPresentationInterfaceState.UrlPreview(
                            url: webpageUrl,
                            webPage: webpage,
                            positionBelowText: $0.editingUrlPreview?.positionBelowText ?? true,
                            largeMedia: $0.editingUrlPreview?.largeMedia
                        )
                        return $0.updatedEditingUrlPreview(updatedPreview)
                    } else {
                        return $0.updatedEditingUrlPreview(nil)
                    }
                })
            }
        }))
        inScope = false
        if let inScopeResult = inScopeResult {
            if let (webpage, webpageUrl) = inScopeResult(updatedChatPresentationInterfaceState.editingUrlPreview?.webPage) {
                let updatedPreview = ChatPresentationInterfaceState.UrlPreview(
                    url: webpageUrl,
                    webPage: webpage,
                    positionBelowText: updatedChatPresentationInterfaceState.editingUrlPreview?.positionBelowText ?? true,
                    largeMedia: updatedChatPresentationInterfaceState.editingUrlPreview?.largeMedia
                )
                updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedEditingUrlPreview(updatedPreview)
            } else {
                updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedEditingUrlPreview(nil)
            }
        }
    }
    
    if let replyMessageId = updatedChatPresentationInterfaceState.interfaceState.replyMessageSubject?.messageId {
        if selfController.replyMessageState?.0 != replyMessageId {
            selfController.replyMessageState?.1.dispose()
            updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedReplyMessage(nil)
            let disposable = MetaDisposable()
            selfController.replyMessageState = (replyMessageId, disposable)
            disposable.set((selfController.context.engine.data.subscribe(TelegramEngine.EngineData.Item.Messages.Message(id: replyMessageId))
            |> deliverOnMainQueue).start(next: { [weak selfController] message in
                guard let selfController else {
                    return
                }
                if message != selfController.presentationInterfaceState.replyMessage.flatMap(EngineMessage.init) {
                    selfController.updateChatPresentationInterfaceState(interactive: false, { presentationInterfaceState in
                        return presentationInterfaceState.updatedReplyMessage(message?._asMessage())
                    })
                }
            }))
        }
    } else {
        if let replyMessageState = selfController.replyMessageState {
            selfController.replyMessageState = nil
            replyMessageState.1.dispose()
            updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedReplyMessage(nil)
        }
    }
    
    if let updated = selfController.updateSearch(updatedChatPresentationInterfaceState) {
        updatedChatPresentationInterfaceState = updated
    }
    
    let recordingActivityValue: ChatRecordingActivity
    if let mediaRecordingState = updatedChatPresentationInterfaceState.inputTextPanelState.mediaRecordingState {
        switch mediaRecordingState {
            case .audio:
                recordingActivityValue = .voice
            case .video(ChatVideoRecordingStatus.recording, _):
                recordingActivityValue = .instantVideo
            default:
                recordingActivityValue = .none
        }
    } else {
        recordingActivityValue = .none
    }
    if recordingActivityValue != selfController.recordingActivityValue {
        selfController.recordingActivityValue = recordingActivityValue
        selfController.recordingActivityPromise.set(recordingActivityValue)
    }
    
    if (selfController.presentationInterfaceState.interfaceState.selectionState == nil) != (updatedChatPresentationInterfaceState.interfaceState.selectionState == nil) {
        selfController.isSelectingMessagesUpdated?(updatedChatPresentationInterfaceState.interfaceState.selectionState != nil)
        selfController.updateNextChannelToReadVisibility()
    }
    
    if updatedChatPresentationInterfaceState.displayHistoryFilterAsList {
        var canDisplayAsList = false
        if updatedChatPresentationInterfaceState.search != nil {
            if updatedChatPresentationInterfaceState.search?.resultsState != nil {
                canDisplayAsList = true
            }
            if updatedChatPresentationInterfaceState.historyFilter != nil {
                canDisplayAsList = true
            }
            if case .peer(selfController.context.account.peerId) = updatedChatPresentationInterfaceState.chatLocation {
                canDisplayAsList = true
            }
        }
        if selfController.alwaysShowSearchResultsAsList {
            canDisplayAsList = true
        }
        
        if !canDisplayAsList {
            updatedChatPresentationInterfaceState = updatedChatPresentationInterfaceState.updatedDisplayHistoryFilterAsList(false)
        }
    }
    
    selfController.presentationInterfaceState = updatedChatPresentationInterfaceState
    
    selfController.updateSlowmodeStatus()
    
    switch updatedChatPresentationInterfaceState.inputMode {
    case .media:
        break
    default:
        selfController.chatDisplayNode.collapseInput()
    }
    
    selfController.tempHideAccessoryPanels = selfController.presentationInterfaceState.search != nil
    
    if selfController.isNodeLoaded {
        selfController.chatDisplayNode.updateChatPresentationInterfaceState(updatedChatPresentationInterfaceState, transition: transition, interactive: interactive, completion: completion)
    } else {
        completion(.immediate)
    }
    
    let updatedServiceTasks = serviceTasksForChatPresentationIntefaceState(context: selfController.context, chatPresentationInterfaceState: updatedChatPresentationInterfaceState, updateState: { [weak selfController] f in
        guard let selfController else {
            return
        }
        selfController.updateChatPresentationInterfaceState(animated: false, interactive: false, f)
        
        //selfController.chatDisplayNode.updateChatPresentationInterfaceState(f(selfController.chatDisplayNode.chatPresentationInterfaceState), transition: transition, interactive: false, completion: { _ in })
    })
    for (id, begin) in updatedServiceTasks {
        if selfController.stateServiceTasks[id] == nil {
            selfController.stateServiceTasks[id] = begin()
        }
    }
    var removedServiceTaskIds: [AnyHashable] = []
    for (id, _) in selfController.stateServiceTasks {
        if updatedServiceTasks[id] == nil {
            removedServiceTaskIds.append(id)
        }
    }
    for id in removedServiceTaskIds {
        selfController.stateServiceTasks.removeValue(forKey: id)?.dispose()
    }
    
    if let button = leftNavigationButtonForChatInterfaceState(updatedChatPresentationInterfaceState, subject: selfController.subject, strings: updatedChatPresentationInterfaceState.strings, currentButton: selfController.leftNavigationButton, target: selfController, selector: #selector(selfController.leftNavigationButtonAction)) {
        if selfController.leftNavigationButton != button {
            var animated = transition.isAnimated
            if let currentButton = selfController.leftNavigationButton?.action, currentButton == button.action {
                animated = false
            }
            animated = false
            selfController.navigationItem.setLeftBarButton(button.buttonItem, animated: animated)
            selfController.leftNavigationButton = button
        }
    } else if let _ = selfController.leftNavigationButton {
        selfController.navigationItem.setLeftBarButton(nil, animated: transition.isAnimated)
        selfController.leftNavigationButton = nil
    }
    
    var buttonsAnimated = transition.isAnimated
    if let button = rightNavigationButtonForChatInterfaceState(context: selfController.context, presentationInterfaceState: updatedChatPresentationInterfaceState, strings: updatedChatPresentationInterfaceState.strings, currentButton: selfController.rightNavigationButton, target: selfController, selector: #selector(selfController.rightNavigationButtonAction), chatInfoNavigationButton: selfController.chatInfoNavigationButton, moreInfoNavigationButton: selfController.moreInfoNavigationButton) {
        if selfController.rightNavigationButton != button {
            if let currentButton = selfController.rightNavigationButton?.action, currentButton == button.action {
                buttonsAnimated = false
            }
            if case .replyThread = selfController.chatLocation {
                buttonsAnimated = false
            }
            selfController.rightNavigationButton = button
        }
    } else if let _ = selfController.rightNavigationButton {
        selfController.rightNavigationButton = nil
    }
    
    if let button = secondaryRightNavigationButtonForChatInterfaceState(context: selfController.context, presentationInterfaceState: updatedChatPresentationInterfaceState, strings: updatedChatPresentationInterfaceState.strings, currentButton: selfController.secondaryRightNavigationButton, target: selfController, selector: #selector(selfController.secondaryRightNavigationButtonAction), chatInfoNavigationButton: selfController.chatInfoNavigationButton, moreInfoNavigationButton: selfController.moreInfoNavigationButton) {
        if selfController.secondaryRightNavigationButton != button {
            if let currentButton = selfController.secondaryRightNavigationButton?.action, currentButton == button.action {
                buttonsAnimated = false
            }
            if case .replyThread = selfController.chatLocation {
                buttonsAnimated = false
            }
            selfController.secondaryRightNavigationButton = button
        }
    } else if let _ = selfController.secondaryRightNavigationButton {
        selfController.secondaryRightNavigationButton = nil
    }
    
    var rightBarButtons: [UIBarButtonItem] = []
    if let rightNavigationButton = selfController.rightNavigationButton {
        rightBarButtons.append(rightNavigationButton.buttonItem)
    }
    if let secondaryRightNavigationButton = selfController.secondaryRightNavigationButton {
        rightBarButtons.append(secondaryRightNavigationButton.buttonItem)
    }
    var rightBarButtonsUpdated = false
    let currentRightBarButtons = selfController.navigationItem.rightBarButtonItems ?? []
    if rightBarButtons.count != currentRightBarButtons.count {
        rightBarButtonsUpdated = true
    } else {
        for i in 0 ..< rightBarButtons.count {
            if rightBarButtons[i] !== currentRightBarButtons[i] {
                rightBarButtonsUpdated = true
                break
            }
        }
    }
    if rightBarButtonsUpdated {
        selfController.navigationItem.setRightBarButtonItems(rightBarButtons, animated: buttonsAnimated)
    }
    
    if let controllerInteraction = selfController.controllerInteraction {
        if updatedChatPresentationInterfaceState.interfaceState.selectionState != controllerInteraction.selectionState {
            controllerInteraction.selectionState = updatedChatPresentationInterfaceState.interfaceState.selectionState
            let isBlackout = controllerInteraction.selectionState != nil
            let previousCompletion = completion
            completion = { [weak selfController] transition in
                previousCompletion(transition)
                
                guard let selfController else {
                    return
                }
                (selfController.navigationController as? NavigationController)?.updateMasterDetailsBlackout(isBlackout ? .master : nil, transition: transition)
            }
            selfController.updateItemNodesSelectionStates(animated: transition.isAnimated)
        }
    }
    
    if saveInterfaceState {
        selfController.saveInterfaceState(includeScrollState: false)
    }
    
    if let navigationController = selfController.navigationController as? NavigationController, isTopmostChatController(selfController) {
        var voiceChatOverlayController: VoiceChatOverlayController?
        for controller in navigationController.globalOverlayControllers {
            if let controller = controller as? VoiceChatOverlayController {
                voiceChatOverlayController = controller
                break
            }
        }
        
        if let controller = voiceChatOverlayController {
            controller.updateVisibility()
        }
    }
 
    if let currentMenuWebAppController = selfController.currentMenuWebAppController, !selfController.presentationInterfaceState.showWebView {
        selfController.currentMenuWebAppController = nil
        if let currentMenuWebAppController = currentMenuWebAppController as? AttachmentController {
            currentMenuWebAppController.ensureUnfocused = false
        }
        currentMenuWebAppController.dismiss(animated: true, completion: nil)
    }
    
    selfController.presentationInterfaceStatePromise.set(selfController.presentationInterfaceState)
    
    if case .tag = selfController.chatDisplayNode.historyNode.tag {
    } else {
        if let historyFilter = selfController.presentationInterfaceState.historyFilter, historyFilter.isActive {
            selfController.chatDisplayNode.historyNode.updateTag(tag: .customTag(historyFilter.customTag, nil))
        } else {
            selfController.chatDisplayNode.historyNode.updateTag(tag: nil)
        }
    }
    
    selfController.updateDownButtonVisibility()
    
    if selfController.presentationInterfaceState.hasBirthdayToday {
        selfController.displayBirthdayTooltip()
    }
    
    if case .standard(.embedded) = selfController.presentationInterfaceState.mode, let controllerInteraction = selfController.controllerInteraction, let interfaceInteraction = selfController.interfaceInteraction {
        if let titleAccessoryPanelNode = titlePanelForChatPresentationInterfaceState(selfController.presentationInterfaceState, context: selfController.context, currentPanel: selfController.customNavigationPanelNode as? ChatTitleAccessoryPanelNode, controllerInteraction: controllerInteraction, interfaceInteraction: interfaceInteraction, force: true) {
            selfController.customNavigationPanelNode = titleAccessoryPanelNode as? ChatControllerCustomNavigationPanelNode
        } else {
            selfController.customNavigationPanelNode = nil
        }
    }
    
    selfController.stateUpdated?(transition)
}