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

import AVFAudio
import SignalServiceKit
import SignalUI

extension ConversationViewController: MessageActionsDelegate {
    func messageActionsEditItem(_ itemViewModel: CVItemViewModelImpl) {
        let hasUnsavedDraft: Bool

        if let inputToolbar {
            hasUnsavedDraft = inputToolbar.hasUnsavedDraft
        } else {
            owsFailDebug("Missing inputToolbar.")
            hasUnsavedDraft = false
        }

        if hasUnsavedDraft {
            let sheet = ActionSheetController(
                title: OWSLocalizedString("DISCARD_DRAFT_CONFIRMATION_TITLE", comment: "Title for confirmation prompt when discarding a draft before editing a message"),
                message: OWSLocalizedString("DISCARD_DRAFT_CONFIRMATION_MESSAGE", comment: "Message/subtitle for confirmation prompt when discarding a draft before editing a message")
            )
            sheet.addAction(
                ActionSheetAction(title: CommonStrings.discardButton, style: .destructive) { [self] _ in
                    populateMessageEdit(itemViewModel)
                }
            )
            sheet.addAction(.cancel)
            present(sheet, animated: true)
        } else {
            populateMessageEdit(itemViewModel)
        }
    }

    func populateMessageEdit(_ itemViewModel: CVItemViewModelImpl) {
        guard let message = itemViewModel.interaction as? TSOutgoingMessage else {
            return owsFailDebug("Invalid interaction.")
        }

        var editValidationError: EditSendValidationError?
        var quotedReplyModel: DraftQuotedReplyModel?
        Self.databaseStorage.read { transaction in

            // If edit send validation fails (timeframe expired,
            // too many edits, etc), display a message here.
            if let error = context.editManager.validateCanSendEdit(
                targetMessageTimestamp: message.timestamp,
                thread: self.thread,
                tx: transaction.asV2Read
            ) {
                editValidationError = error
                return
            }

            if let quotedMessage = message.quotedMessage {
                let originalMessage = (quotedMessage.timestampValue?.uint64Value).flatMap {
                    return InteractionFinder.findMessage(
                        withTimestamp: $0,
                        threadId: message.uniqueThreadId,
                        author: quotedMessage.authorAddress,
                        transaction: transaction
                    )
                }
                if
                    let originalMessage,
                    originalMessage is OWSPaymentMessage
                {
                    quotedReplyModel = DraftQuotedReplyModel.forEditingOriginalPaymentMessage(
                        originalMessage: originalMessage,
                        replyMessage: message,
                        quotedReply: quotedMessage,
                        tx: transaction
                    )
                } else {
                    quotedReplyModel = DependenciesBridge.shared.quotedReplyManager.buildDraftQuotedReplyForEditing(
                        quotedReplyMessage: message,
                        quotedReply: quotedMessage,
                        originalMessage: originalMessage,
                        tx: transaction.asV2Read
                    )
                }
            }
        }

        if let editValidationError {
            OWSActionSheets.showActionSheet(message: editValidationError.localizedDescription)
        } else {
            inputToolbar?.quotedReplyDraft = quotedReplyModel
            inputToolbar?.editTarget = message

            inputToolbar?.editThumbnail = nil
            let imageStream = itemViewModel.bodyMediaAttachmentStreams.first(where: {
                $0.computeContentType().isImage
            })
            if let imageStream {
                Task {
                    guard let image = await imageStream.thumbnailImage(quality: .small) else {
                        owsFailDebug("Could not load thumnail.")
                        return
                    }
                    guard let inputToolbar,
                          inputToolbar.shouldShowEditUI else { return }
                    inputToolbar.editThumbnail = image
                }
            }

            inputToolbar?.beginEditingMessage()
        }
    }

    func messageActionsShowDetailsForItem(_ itemViewModel: CVItemViewModelImpl) {
        showDetailView(itemViewModel)
    }

    func prepareDetailViewForInteractivePresentation(_ itemViewModel: CVItemViewModelImpl) {
        AssertIsOnMainThread()

        guard let message = itemViewModel.interaction as? TSMessage else {
            return owsFailDebug("Invalid interaction.")
        }

        guard let panHandler = panHandler else {
            return owsFailDebug("Missing panHandler")
        }

        let detailVC = MessageDetailViewController(
            message: message,
            spoilerState: self.viewState.spoilerState,
            editManager: self.context.editManager,
            thread: thread
        )
        detailVC.detailDelegate = self
        conversationSplitViewController?.navigationTransitionDelegate = detailVC
        panHandler.messageDetailViewController = detailVC
    }

    func showDetailView(_ itemViewModel: CVItemViewModelImpl) {
        AssertIsOnMainThread()

        guard let message = itemViewModel.interaction as? TSMessage else {
            owsFailDebug("Invalid interaction.")
            return
        }

        let panHandler = viewState.panHandler

        let detailVC: MessageDetailViewController
        if let panHandler = panHandler,
           let messageDetailViewController = panHandler.messageDetailViewController,
           messageDetailViewController.message.uniqueId == message.uniqueId {
            detailVC = messageDetailViewController
            detailVC.pushPercentDrivenTransition = panHandler.percentDrivenTransition
        } else {
            detailVC = MessageDetailViewController(
                message: message,
                spoilerState: self.viewState.spoilerState,
                editManager: self.context.editManager,
                thread: thread
            )
            detailVC.detailDelegate = self
            conversationSplitViewController?.navigationTransitionDelegate = detailVC
        }

        navigationController?.pushViewController(detailVC, animated: true)
    }

    func messageActionsReplyToItem(_ itemViewModel: CVItemViewModelImpl) {
        populateReplyForMessage(itemViewModel)
    }

    public func populateReplyForMessage(_ itemViewModel: CVItemViewModelImpl) {
        AssertIsOnMainThread()

        guard let inputToolbar = inputToolbar else {
            owsFailDebug("Missing inputToolbar.")
            return
        }

        self.uiMode = .normal

        let load: () -> DraftQuotedReplyModel? = {
            guard let message = itemViewModel.interaction as? TSMessage else {
                return nil
            }
            return Self.databaseStorage.read { transaction in
                if message is OWSPaymentMessage {
                    return DraftQuotedReplyModel.fromOriginalPaymentMessage(message, tx: transaction)
                }
                return DependenciesBridge.shared.quotedReplyManager.buildDraftQuotedReply(
                    originalMessage: message,
                    tx: transaction.asV2Read
                )
            }
        }
        guard let quotedReply = load() else {
            owsFailDebug("Could not build quoted reply.")
            return
        }

        inputToolbar.editTarget = nil
        inputToolbar.quotedReplyDraft = quotedReply
        inputToolbar.beginEditingMessage()
    }

    func messageActionsForwardItem(_ itemViewModel: CVItemViewModelImpl) {
        AssertIsOnMainThread()

        ForwardMessageViewController.present(forItemViewModels: [itemViewModel],
                                             from: self,
                                             delegate: self)
    }

    func messageActionsStartedSelect(initialItem itemViewModel: CVItemViewModelImpl) {
        uiMode = .selection

        selectionState.add(itemViewModel: itemViewModel, selectionType: .allContent)
    }

    func messageActionsDeleteItem(_ itemViewModel: CVItemViewModelImpl) {
        itemViewModel.interaction.presentDeletionActionSheet(from: self)
    }

    func messageActionsSpeakItem(_ itemViewModel: CVItemViewModelImpl) {
        guard let textValue = itemViewModel.displayableBodyText?.fullTextValue else {
            return
        }

        let utterance: AVSpeechUtterance = {
            switch textValue {
            case .text(let text):
                return AVSpeechUtterance(string: text)
            case .attributedText(let attributedText):
                return AVSpeechUtterance(attributedString: attributedText)
            case .messageBody(let messageBody):
                return messageBody.utterance
            }
        }()

        self.speechManager.speak(utterance)
    }

    func messageActionsStopSpeakingItem(_ itemViewModel: CVItemViewModelImpl) {
        self.speechManager.stop()
    }

    func messageActionsShowPaymentDetails(_ itemViewModel: CVItemViewModelImpl) {
        guard let model = itemViewModel.paymentAttachment?.model else {
            owsFailDebug("We should have a matching TSPaymentModel at this point")
            return
        }

        guard let contactAddress = (thread as? TSContactThread)?.contactAddress else {
            owsFailDebug("Should be contact thread")
            return
        }
        let contactName = databaseStorage.read { tx in
            return self.contactsManager.displayName(for: contactAddress, tx: tx).resolvedValue()
        }

        let paymentHistoryItem = PaymentsHistoryItem(paymentModel: model, displayName: contactName)
        let paymentsDetailViewController = PaymentsDetailViewController(
            paymentItem: paymentHistoryItem
        )
        navigationController?.pushViewController(paymentsDetailViewController, animated: true)
    }
}
