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

import Foundation
import SignalServiceKit

extension ConversationViewController {
    func checkPermissionsAndStartRecordingVoiceMessage() {
        AssertIsOnMainThread()

        // Cancel any ongoing audio playback.
        cvAudioPlayer.stopAll()

        let inProgressVoiceMessage = VoiceMessageInProgressDraft(
            thread: thread,
            audioSession: audioSession,
            sleepManager: deviceSleepManager
        )
        viewState.inProgressVoiceMessage = inProgressVoiceMessage

        // Delay showing the voice memo UI for N ms to avoid a jarring transition
        // when you just tap and don't hold.
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak self] in
            guard let self = self else { return }
            guard self.viewState.inProgressVoiceMessage === inProgressVoiceMessage else { return }
            self.configureScrollDownButtons()
            self.inputToolbar?.showVoiceMemoUI()
        }

        ows_askForMicrophonePermissions { [weak self] granted in
            guard let self = self else { return }
            guard self.viewState.inProgressVoiceMessage === inProgressVoiceMessage else { return }

            guard granted else {
                self.cancelRecordingVoiceMessage()
                self.ows_showNoMicrophonePermissionActionSheet()
                return
            }

            self.startRecordingVoiceMessage(inProgressVoiceMessage)
        }
    }

    private func startRecordingVoiceMessage(_ inProgressVoiceMessage: VoiceMessageInProgressDraft) {
        AssertIsOnMainThread()

        ImpactHapticFeedback.impactOccurred(style: .light)

        do {
            try inProgressVoiceMessage.startRecording()
        } catch {
            owsFailDebug("Failed to start recording voice message \(error)")
            cancelRecordingVoiceMessage()
        }
    }

    func cancelRecordingVoiceMessage() {
        AssertIsOnMainThread()

        viewState.inProgressVoiceMessage?.stopRecordingAsync()
        viewState.inProgressVoiceMessage = nil

        NotificationHapticFeedback().notificationOccurred(.warning)

        clearVoiceMessageDraft()
        inputToolbar?.hideVoiceMemoUI(animated: true)
        configureScrollDownButtons()
    }

    private static let minimumVoiceMessageDuration: TimeInterval = 1

    func finishRecordingVoiceMessage(sendImmediately: Bool = false) {
        AssertIsOnMainThread()

        guard let inProgressVoiceMessage = viewState.inProgressVoiceMessage else { return }
        viewState.inProgressVoiceMessage = nil

        inProgressVoiceMessage.stopRecording()

        guard let duration = inProgressVoiceMessage.duration, duration >= Self.minimumVoiceMessageDuration else {
            inputToolbar?.showVoiceMemoTooltip()
            cancelRecordingVoiceMessage()
            return
        }

        ImpactHapticFeedback.impactOccurred(style: .medium)

        configureScrollDownButtons()

        if sendImmediately {
            sendVoiceMessageDraft(inProgressVoiceMessage)
        } else {
            databaseStorage.asyncWrite {
                let interruptedDraft = inProgressVoiceMessage.convertToDraft(transaction: $0)
                DispatchQueue.main.async {
                    self.inputToolbar?.showVoiceMemoDraft(interruptedDraft)
                }
            }
        }
    }

    func sendVoiceMessageDraft(_ voiceMemoDraft: VoiceMessageSendableDraft) {
        inputToolbar?.hideVoiceMemoUI(animated: true)

        do {
            tryToSendAttachments([try voiceMemoDraft.prepareAttachment()], messageBody: nil)
            clearVoiceMessageDraft()
        } catch {
            owsFailDebug("Failed to send prepare voice message for sending \(error)")
        }
    }

    func clearVoiceMessageDraft() {
        databaseStorage.asyncWrite { [thread] in
            VoiceMessageInterruptedDraftStore.clearDraft(for: thread, transaction: $0)
        }
    }
}
