import type { UseCase, Logger } from '@core/shared/application'; import type { IAvatarGenerationRepository } from '../../domain/repositories/IAvatarGenerationRepository'; import type { FaceValidationPort } from '../ports/FaceValidationPort'; import type { AvatarGenerationPort } from '../ports/AvatarGenerationPort'; import type { IRequestAvatarGenerationPresenter, RequestAvatarGenerationResultDTO } from '../presenters/IRequestAvatarGenerationPresenter'; import { AvatarGenerationRequest } from '../../domain/entities/AvatarGenerationRequest'; import type { RacingSuitColor, AvatarStyle } from '../../domain/types/AvatarGenerationRequest'; export interface RequestAvatarGenerationCommand { userId: string; facePhotoData: string; // Base64 encoded image data suitColor: RacingSuitColor; style?: AvatarStyle; } export class RequestAvatarGenerationUseCase implements UseCase { constructor( private readonly avatarRepository: IAvatarGenerationRepository, private readonly faceValidation: FaceValidationPort, private readonly avatarGeneration: AvatarGenerationPort, private readonly logger: Logger, ) {} async execute(command: RequestAvatarGenerationCommand, presenter: IRequestAvatarGenerationPresenter): Promise { presenter.reset(); this.logger.debug( `Executing RequestAvatarGenerationUseCase for userId: ${command.userId}`, command, ); try { // Create the generation request const requestId = this.generateId(); const request = AvatarGenerationRequest.create({ id: requestId, userId: command.userId, facePhotoUrl: `data:image/jpeg;base64,${command.facePhotoData}`, suitColor: command.suitColor, ...(command.style ? { style: command.style } : {}), }); this.logger.info(`Avatar generation request created with ID: ${requestId}`); // Mark as validating request.markAsValidating(); await this.avatarRepository.save(request); this.logger.debug(`Request ${requestId} marked as validating.`); // Validate the face photo const validationResult = await this.faceValidation.validateFacePhoto(command.facePhotoData); this.logger.debug( `Face validation result for request ${requestId}:`, validationResult, ); if (!validationResult.isValid) { const errorMessage = validationResult.errorMessage || 'Face validation failed'; request.fail(errorMessage); await this.avatarRepository.save(request); this.logger.error(`Face validation failed for request ${requestId}: ${errorMessage}`); presenter.present({ requestId, status: 'failed', errorMessage: validationResult.errorMessage || 'Please upload a clear photo of your face', }); return; } if (!validationResult.hasFace) { const errorMessage = 'No face detected in the image'; request.fail(errorMessage); await this.avatarRepository.save(request); this.logger.error(`No face detected for request ${requestId}: ${errorMessage}`); presenter.present({ requestId, status: 'failed', errorMessage: 'No face detected. Please upload a photo that clearly shows your face.', }); return; } if (validationResult.faceCount > 1) { const errorMessage = 'Multiple faces detected'; request.fail(errorMessage); await this.avatarRepository.save(request); this.logger.error(`Multiple faces detected for request ${requestId}: ${errorMessage}`); presenter.present({ requestId, status: 'failed', errorMessage: 'Multiple faces detected. Please upload a photo with only your face.', }); return; } this.logger.info(`Face validation successful for request ${requestId}.`); // Mark as generating request.markAsGenerating(); await this.avatarRepository.save(request); this.logger.debug(`Request ${requestId} marked as generating.`); // Generate avatars const generationResult = await this.avatarGeneration.generateAvatars({ facePhotoUrl: request.facePhotoUrl.value, prompt: request.buildPrompt(), suitColor: request.suitColor, style: request.style, count: 3, // Generate 3 options }); this.logger.debug( `Avatar generation service result for request ${requestId}:`, generationResult, ); if (!generationResult.success) { const errorMessage = generationResult.errorMessage || 'Avatar generation failed'; request.fail(errorMessage); await this.avatarRepository.save(request); this.logger.error(`Avatar generation failed for request ${requestId}: ${errorMessage}`); presenter.present({ requestId, status: 'failed', errorMessage: generationResult.errorMessage || 'Failed to generate avatars. Please try again.', }); return; } // Complete with generated avatars const avatarUrls = generationResult.avatars.map(a => a.url); request.completeWithAvatars(avatarUrls); await this.avatarRepository.save(request); this.logger.info(`Avatar generation completed successfully for request ${requestId}.`); presenter.present({ requestId, status: 'completed', avatarUrls, }); } catch (error) { this.logger.error( `An unexpected error occurred during avatar generation for userId: ${command.userId}`, error as Error, ); // Re-throw or return a generic error, depending on desired error handling strategy throw error; } } private generateId(): string { if (typeof crypto !== 'undefined' && crypto.randomUUID) { return crypto.randomUUID(); } return 'avatar-' + Date.now().toString(36) + Math.random().toString(36).substr(2, 9); } }