//
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//

import LibSignalClient
import SignalServiceKit
import SignalUI

extension ConversationViewController {
    public func updateNavigationTitle() {
        AssertIsOnMainThread()

        self.title = nil

        if thread.isNoteToSelf {
            headerView.titleIcon = Theme.iconImage(.official)
            headerView.titleIconSize = 16
        } else {
            headerView.titleIcon = nil
        }

        let attributedName = NSMutableAttributedString(
            string: threadViewModel.name,
            attributes: [
                .foregroundColor: Theme.primaryTextColor
            ]
        )

        if conversationViewModel.isSystemContact {
            let contactIcon = SignalSymbol.personCircle.attributedString(
                dynamicTypeBaseSize: 14,
                weight: .bold,
                withLeadingSpace: true
            )
            attributedName.append(contactIcon)
        }

        if headerView.attributedTitle != attributedName {
            headerView.attributedTitle = attributedName
        }
    }

    public func createHeaderViews() {
        AssertIsOnMainThread()

        headerView.configure(threadViewModel: threadViewModel)
        headerView.accessibilityLabel = OWSLocalizedString("CONVERSATION_SETTINGS",
                                                          comment: "title for conversation settings screen")
        headerView.accessibilityIdentifier = "headerView"
        headerView.delegate = self
        navigationItem.titleView = headerView

        if shouldUseDebugUI() {
            headerView.addGestureRecognizer(UILongPressGestureRecognizer(
                target: self,
                action: #selector(navigationTitleLongPressed)
            ))
        }

        updateNavigationBarSubtitleLabel()
    }

    @objc
    private func navigationTitleLongPressed(_ gestureRecognizer: UIGestureRecognizer) {
        AssertIsOnMainThread()

        if gestureRecognizer.state == .began {
            showDebugUIForThread(thread, fromViewController: self)
        }
    }

    public var unreadCountViewDiameter: CGFloat { 16 }

    public func updateBarButtonItems() {
        AssertIsOnMainThread()

        // Don't include "Back" text on view controllers pushed above us, just use the arrow.
        navigationItem.backBarButtonItem = UIBarButtonItem(title: "",
                                                           style: .plain,
                                                           target: nil,
                                                           action: nil)

        navigationItem.hidesBackButton = false
        navigationItem.leftBarButtonItem = nil
        self.groupCallBarButtonItem = nil

        switch uiMode {
        case .search:
            if self.userLeftGroup {
                navigationItem.rightBarButtonItems = []
                return
            }
            owsAssertDebug(navigationItem.searchController != nil)
            return
        case .selection:
            navigationItem.rightBarButtonItems = [ self.cancelSelectionBarButtonItem ]
            navigationItem.leftBarButtonItem = self.deleteAllBarButtonItem
            navigationItem.hidesBackButton = true
            return
        case .normal:
            if self.userLeftGroup {
                navigationItem.rightBarButtonItems = []
                return
            }
            var barButtons = [UIBarButtonItem]()
            if self.canCall {
                if self.isGroupConversation {
                    let videoCallButton = UIBarButtonItem()

                    if conversationViewModel.groupCallInProgress {
                        let pill = JoinGroupCallPill()
                        pill.addTarget(
                            self,
                            action: #selector(showGroupLobbyOrActiveCall),
                            for: .touchUpInside
                        )
                        let returnString = OWSLocalizedString(
                            "RETURN_CALL_PILL_BUTTON",
                            comment: "Button to return to current group call"
                        )
                        let joinString = OWSLocalizedString(
                            "JOIN_CALL_PILL_BUTTON",
                            comment: "Button to join an active group call"
                        )
                        pill.buttonText = self.isCurrentCallForThread ? returnString : joinString
                        videoCallButton.customView = pill
                    } else {
                        videoCallButton.image = Theme.iconImage(.buttonVideoCall)
                        videoCallButton.target = self
                        videoCallButton.action = #selector(showGroupLobbyOrActiveCall)
                    }

                    videoCallButton.isEnabled = (
                        AppEnvironment.shared.callService.callServiceState.currentCall == nil
                        || self.isCurrentCallForThread
                    )
                    videoCallButton.accessibilityLabel = OWSLocalizedString(
                        "VIDEO_CALL_LABEL",
                        comment: "Accessibility label for placing a video call"
                    )
                    self.groupCallBarButtonItem = videoCallButton
                    barButtons.append(videoCallButton)
                } else {
                    let audioCallButton = UIBarButtonItem(
                        image: Theme.iconImage(.buttonVoiceCall),
                        style: .plain,
                        target: self,
                        action: #selector(startIndividualAudioCall)
                    )
                    audioCallButton.isEnabled = AppEnvironment.shared.callService.callServiceState.currentCall == nil
                    audioCallButton.accessibilityLabel = OWSLocalizedString(
                        "VOICE_CALL_LABEL",
                        comment: "Accessibility label for placing a voice call"
                    )
                    barButtons.append(audioCallButton)

                    let videoCallButton = UIBarButtonItem(
                        image: Theme.iconImage(.buttonVideoCall),
                        style: .plain,
                        target: self,
                        action: #selector(startIndividualVideoCall)
                    )
                    videoCallButton.isEnabled = AppEnvironment.shared.callService.callServiceState.currentCall == nil
                    videoCallButton.accessibilityLabel = OWSLocalizedString(
                        "VIDEO_CALL_LABEL",
                        comment: "Accessibility label for placing a video call"
                    )
                    barButtons.append(videoCallButton)
                }
            }

            navigationItem.rightBarButtonItems = barButtons
            showGroupCallTooltipIfNecessary()
            return
        }
    }

    public func updateNavigationBarSubtitleLabel() {
        AssertIsOnMainThread()

        let hasCompactHeader = self.traitCollection.verticalSizeClass == .compact
        if hasCompactHeader {
            self.headerView.attributedSubtitle = nil
            return
        }

        let subtitleText = NSMutableAttributedString()
        let subtitleFont = self.headerView.subtitleFont
        let attributes: [NSAttributedString.Key: Any] = [
            .font: subtitleFont,
            .foregroundColor: Theme.navbarTitleColor.withAlphaComponent(0.9)
        ]
        let hairSpace = "\u{200a}"
        let thinSpace = "\u{2009}"
        let iconSpacer = UIDevice.current.isNarrowerThanIPhone6 ? hairSpace : thinSpace
        let betweenItemSpacer = UIDevice.current.isNarrowerThanIPhone6 ? " " : "  "

        let isMuted = threadViewModel.isMuted
        let hasTimer = disappearingMessagesConfiguration.isEnabled
        let isVerified = conversationViewModel.shouldShowVerifiedBadge

        if isMuted {
            subtitleText.appendTemplatedImage(named: "bell-slash-compact", font: subtitleFont)
            if !isVerified {
                subtitleText.append(iconSpacer, attributes: attributes)
                subtitleText.append(OWSLocalizedString("MUTED_BADGE",
                                                      comment: "Badge indicating that the user is muted."),
                                    attributes: attributes)
            }
        }

        if hasTimer {
            if isMuted {
                subtitleText.append(betweenItemSpacer, attributes: attributes)
            }

            subtitleText.appendTemplatedImage(named: Theme.iconName(.timer16), font: subtitleFont)
            subtitleText.append(iconSpacer, attributes: attributes)
            subtitleText.append(DateUtil.formatDuration(
                seconds: disappearingMessagesConfiguration.durationSeconds,
                useShortFormat: true
            ),
            attributes: attributes)
        }

        if isVerified {
            if hasTimer || isMuted {
                subtitleText.append(betweenItemSpacer, attributes: attributes)
            }

            subtitleText.appendTemplatedImage(named: "check-extra-small", font: subtitleFont)
            subtitleText.append(iconSpacer, attributes: attributes)
            subtitleText.append(
                SafetyNumberStrings.verified,
                attributes: attributes
            )
        }

        headerView.attributedSubtitle = subtitleText
    }

    public var safeContentHeight: CGFloat {
        // Don't use self.collectionView.contentSize.height as the collection view's
        // content size might not be set yet.
        //
        // We can safely call prepareLayout to ensure the layout state is up-to-date
        // since our layout uses a dirty flag internally to debounce redundant work.
        collectionView.collectionViewLayout.collectionViewContentSize.height
    }

    func buildInputToolbar(
        conversationStyle: ConversationStyle,
        messageDraft: MessageBody?,
        draftReply: ThreadReplyInfo?,
        voiceMemoDraft: VoiceMessageInterruptedDraft?,
        editTarget: TSOutgoingMessage?
    ) -> ConversationInputToolbar {
        AssertIsOnMainThread()
        owsAssertDebug(hasViewWillAppearEverBegun)

        let quotedReply: DraftQuotedReplyModel?
        if let draftReply = draftReply {
            quotedReply = buildDraftQuotedReply(draftReply)
        } else {
            quotedReply = nil
        }

        let inputToolbar = ConversationInputToolbar(
            conversationStyle: conversationStyle,
            spoilerState: viewState.spoilerState,
            mediaCache: mediaCache,
            messageDraft: messageDraft,
            quotedReplyDraft: quotedReply,
            editTarget: editTarget,
            inputToolbarDelegate: self,
            inputTextViewDelegate: self,
            mentionDelegate: self
        )
        inputToolbar.accessibilityIdentifier = "inputToolbar"
        if let voiceMemoDraft = voiceMemoDraft {
            inputToolbar.showVoiceMemoDraft(voiceMemoDraft)
        }

        return inputToolbar
    }

    func buildDraftQuotedReply(_ draftReply: ThreadReplyInfo) -> DraftQuotedReplyModel? {
        return Self.databaseStorage.read { transaction in
            guard let interaction = try? InteractionFinder.interactions(
                withTimestamp: draftReply.timestamp,
                filter: { candidate in
                    if let incoming = candidate as? TSIncomingMessage {
                        return incoming.authorAddress.aci == draftReply.author
                    }
                    if candidate is TSOutgoingMessage {
                        return DependenciesBridge.shared.tsAccountManager
                            .localIdentifiers(tx: transaction.asV2Read)?.aci == draftReply.author
                    }
                    return false
                },
                transaction: transaction
            ).first as? TSMessage else {
                return nil
            }
            if interaction is OWSPaymentMessage {
                return DraftQuotedReplyModel.fromOriginalPaymentMessage(interaction, tx: transaction)
            }
            return DependenciesBridge.shared.quotedReplyManager.buildDraftQuotedReply(
                originalMessage: interaction,
                tx: transaction.asV2Read
            )

        }
    }

}
// MARK: - Keyboard Shortcuts

public extension ConversationViewController {
    func focusInputToolbar() {
        AssertIsOnMainThread()

        guard hasViewWillAppearEverBegun else {
            owsFailDebug("InputToolbar not yet ready.")
            return
        }
        guard let inputToolbar = inputToolbar else {
            owsFailDebug("Missing inputToolbar.")
            return
        }

        inputToolbar.clearDesiredKeyboard()
        self.popKeyBoard()
    }

    func openAllMedia() {
        AssertIsOnMainThread()

        guard hasViewWillAppearEverBegun else {
            owsFailDebug("InputToolbar not yet ready.")
            return
        }

        self.showConversationSettingsAndShowAllMedia()
    }

    func openStickerKeyboard() {
        AssertIsOnMainThread()

        guard hasViewWillAppearEverBegun else {
            owsFailDebug("InputToolbar not yet ready.")
            return
        }
        guard let inputToolbar = inputToolbar else {
            owsFailDebug("Missing inputToolbar.")
            return
        }

        inputToolbar.showStickerKeyboard()
    }

    func openAttachmentKeyboard() {
        AssertIsOnMainThread()

        guard hasViewWillAppearEverBegun else {
            owsFailDebug("InputToolbar not yet ready.")
            return
        }
        guard let inputToolbar = inputToolbar else {
            owsFailDebug("Missing inputToolbar.")
            return
        }

        inputToolbar.showAttachmentKeyboard()
    }

    func openGifSearch() {
        AssertIsOnMainThread()

        guard hasViewWillAppearEverBegun else {
            owsFailDebug("InputToolbar not yet ready.")
            return
        }
        guard nil != inputToolbar else {
            owsFailDebug("Missing inputToolbar.")
            return
        }

        self.showGifPicker()
    }
}
