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

import AVFoundation
import Foundation
import SignalServiceKit
import SignalUI

/// Represents a voice note that was "interrupted" while being recorded.
///
/// An interrupted voice note will appear in the compose box, and it will
/// offer you a choice to play, delete, and/or send the voice note.
///
/// The easiest way to interrupt a recording is to use the "lock" mechanism
/// and then tap the back button in a chat. The voice note feature itself
/// doesn't expose a "stop recording but don't send" mechanism -- you either
/// cancel the recording or send the message.
///
/// This class works in tandem with ``VoiceMessageInProgressDraft``.
final class VoiceMessageInterruptedDraft: VoiceMessageSendableDraft {
    typealias Constants = VoiceMessageInterruptedDraftStore.Constants

    private let threadUniqueId: String
    private let audioFileUrl: URL
    private let waveformFileUrl: URL

    init(threadUniqueId: String, directoryUrl: URL) {
        self.threadUniqueId = threadUniqueId
        self.audioFileUrl = URL(fileURLWithPath: Constants.audioFilename, relativeTo: directoryUrl)
        self.waveformFileUrl = URL(fileURLWithPath: Constants.waveformFilename, relativeTo: directoryUrl)
    }

    public static func currentDraft(for thread: TSThread, transaction: SDSAnyReadTransaction) -> VoiceMessageInterruptedDraft? {
        let directoryUrl = VoiceMessageInterruptedDraftStore.directoryUrl(
            threadUniqueId: thread.uniqueId,
            transaction: transaction
        )
        return directoryUrl.map { VoiceMessageInterruptedDraft(threadUniqueId: thread.uniqueId, directoryUrl: $0) }
    }

    // MARK: -

    func clearDraft(transaction: SDSAnyWriteTransaction) {
        VoiceMessageInterruptedDraftStore.clearDraft(for: threadUniqueId, transaction: transaction)
    }

    // MARK: -

    public private(set) lazy var audioWaveformTask: Task<AudioWaveform, Error> = {
        // The file at `waveformPath` is created lazily by accessing this property.
        // It's used solely for UI and thus isn't created until it's needed.
        DependenciesBridge.shared.audioWaveformManager.audioWaveform(
            forAudioPath: audioFileUrl.path,
            waveformPath: waveformFileUrl.path
        )
    }()

    public private(set) lazy var audioPlayer: AudioPlayer = {
        AudioPlayer(decryptedFileUrl: audioFileUrl, audioBehavior: .audioMessagePlayback)
    }()

    public private(set) lazy var duration: TimeInterval? = {
        guard OWSFileSystem.fileOrFolderExists(url: audioFileUrl) else { return nil }
        return try? AVAudioPlayer(
            contentsOf: audioFileUrl
        ).duration
    }()

    // MARK: -

    public func prepareForSending() throws -> URL {
        let temporaryAudioFileUrl = OWSFileSystem.temporaryFileUrl()
        try FileManager.default.copyItem(at: audioFileUrl, to: temporaryAudioFileUrl)
        return temporaryAudioFileUrl
    }
}
