import Foundation public final class SongProjectGenerationDirector: Sendable { private let aiService: any AIService public init(aiService: any AIService) { self.aiService = aiService } public func generateCompleteProject( fromIdea idea: String, seedProject: SongProject? = nil, discussionMode: ConversationMode = .auto, conversation: [AIConversationMessage] = [], localeIdentifier: String? = nil, generatedAt: Date = Date() ) async throws -> SongProjectGenerationResult { let trimmedIdea = idea.trimmingCharacters(in: .whitespacesAndNewlines) guard !trimmedIdea.isEmpty else { throw SongProjectGenerationDirectorError.emptyIdea } let request = SongProjectGenerationRequest( context: AIRequestContext( userInstruction: trimmedIdea, conversation: conversation, localeIdentifier: localeIdentifier ), seedProject: seedProject, discussionMode: discussionMode ) let result = try await aiService.generateSongProject(from: request) let project = normalizeGeneratedProject( result.project, idea: trimmedIdea, seedProject: seedProject, discussionMode: discussionMode, generatedAt: generatedAt ) return SongProjectGenerationResult( project: project, notes: result.notes, followUpQuestions: result.followUpQuestions ) } private func normalizeGeneratedProject( _ generatedProject: SongProject, idea: String, seedProject: SongProject?, discussionMode: ConversationMode, generatedAt: Date ) -> SongProject { let normalizedIdea = generatedProject.idea.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ? idea : generatedProject.idea guard let seedProject else { var project = generatedProject project.idea = normalizedIdea project.conversationMode = discussionMode project.updatedAt = generatedAt return project } return SongProject( id: seedProject.id, title: generatedProject.title, idea: normalizedIdea, duration: generatedProject.duration, conversationMode: discussionMode, languages: generatedProject.languages, dialects: generatedProject.dialects, arabicPronunciationSettings: generatedProject.arabicPronunciationSettings, genres: generatedProject.genres, moods: generatedProject.moods, emotionalArc: generatedProject.emotionalArc, bpm: generatedProject.bpm, key: generatedProject.key, scale: generatedProject.scale, maqam: generatedProject.maqam, sections: generatedProject.sections, instruments: generatedProject.instruments, vocalists: generatedProject.vocalists, lyrics: generatedProject.lyrics, productionDirections: generatedProject.productionDirections, sunoOutput: generatedProject.sunoOutput, versions: seedProject.versions, createdAt: seedProject.createdAt, updatedAt: generatedAt ) } } public enum SongProjectGenerationDirectorError: Error, Equatable, Sendable { case emptyIdea }