import AVFoundation import Foundation import SwiftData @Observable @MainActor final class RecordingViewModel { var isRecording = false var recordingDuration: TimeInterval = 0 var error: Error? private let recorder = AudioRecorderService() private let transcriptionService = TranscriptionService() private var currentRecordingURL: URL? var formattedDuration: String { let minutes = Int(recorder.recordingDuration) / 60 let seconds = Int(recorder.recordingDuration) % 60 return String(format: "%d:%02d", minutes, seconds) } func startRecording() { do { currentRecordingURL = try recorder.startRecording() isRecording = true error = nil } catch { self.error = error } } func stopRecording(context: ModelContext) { guard let result = recorder.stopRecording() else { return } isRecording = false let today = Calendar.current.startOfDay(for: .now) let entry = fetchOrCreateEntry(for: today, context: context) let memo = VoiceMemo( audioFileName: result.url.lastPathComponent, duration: result.duration ) context.insert(memo) memo.entry = entry if entry.memos == nil { entry.memos = [] } entry.memos?.append(memo) entry.updatedAt = .now try? context.save() let audioURL = memo.audioURL Task { await transcribeMemo(memo, audioURL: audioURL) } } private func fetchOrCreateEntry(for date: Date, context: ModelContext) -> DiaryEntry { let descriptor = FetchDescriptor() if let entries = try? context.fetch(descriptor) { let match = entries.first { entry in Calendar.current.isDate(entry.date, inSameDayAs: date) } if let match { return match } } let entry = DiaryEntry(date: date) context.insert(entry) return entry } private func transcribeMemo(_ memo: VoiceMemo, audioURL: URL) async { if transcriptionService.authorizationStatus == .notDetermined { await transcriptionService.requestAuthorization() } guard transcriptionService.authorizationStatus == .authorized else { return } memo.isTranscribing = true do { let transcript = try await transcriptionService.transcribe(audioURL: audioURL) memo.transcript = transcript memo.isTranscribing = false memo.entry?.updatedAt = .now } catch { memo.isTranscribing = false self.error = error } } }