import { faker } from '@faker-js/faker'; /** * Core Domain Service: MediaGenerationService * * Encapsulates business logic for generating media assets (SVGs) using Faker. * Ensures deterministic results by seeding Faker with entity IDs. */ export class MediaGenerationService { /** * Generates a deterministic SVG avatar for a driver */ generateDriverAvatar(driverId: string): string { faker.seed(this.hashCode(driverId)); const firstName = faker.person.firstName(); const lastName = faker.person.lastName(); const initials = ((firstName?.[0] || 'D') + (lastName?.[0] || 'R')).toUpperCase(); const primaryColor = faker.color.rgb({ format: 'hex' }); const secondaryColor = faker.color.rgb({ format: 'hex' }); const patterns = ['gradient', 'stripes', 'circles', 'diamond']; const pattern = faker.helpers.arrayElement(patterns); let patternSvg = ''; switch (pattern) { case 'gradient': patternSvg = ` `; break; case 'stripes': patternSvg = ` `; break; case 'circles': patternSvg = ` `; break; case 'diamond': patternSvg = ` `; break; } return ` ${patternSvg} ${initials} `; } /** * Generates a deterministic SVG logo for a team * Now includes team name initials for better branding */ generateTeamLogo(teamId: string): string { faker.seed(this.hashCode(teamId)); const primaryColor = faker.color.rgb({ format: 'hex' }); const secondaryColor = faker.color.rgb({ format: 'hex' }); // Generate deterministic initials from seeded faker data // This creates consistent initials for the same teamId const adjective = faker.company.buzzAdjective(); const noun = faker.company.catchPhraseNoun(); const initials = ((adjective?.[0] || 'T') + (noun?.[0] || 'M')).toUpperCase(); const shapes = ['circle', 'square', 'triangle', 'hexagon']; const shape = faker.helpers.arrayElement(shapes); let shapeSvg = ''; switch (shape) { case 'circle': shapeSvg = ``; break; case 'square': shapeSvg = ``; break; case 'triangle': shapeSvg = ``; break; case 'hexagon': shapeSvg = ``; break; } return ` ${shapeSvg} ${initials} `; } /** * Generates a deterministic SVG logo for a league * Updated to use the same faker style as team logos for consistency */ generateLeagueLogo(leagueId: string): string { faker.seed(this.hashCode(leagueId)); const primaryColor = faker.color.rgb({ format: 'hex' }); const secondaryColor = faker.color.rgb({ format: 'hex' }); // Generate deterministic initials from seeded faker data // This creates consistent initials for the same leagueId const adjective = faker.company.buzzAdjective(); const noun = faker.company.catchPhraseNoun(); const initials = ((adjective?.[0] || 'L') + (noun?.[0] || 'G')).toUpperCase(); const shapes = ['circle', 'square', 'triangle', 'hexagon']; const shape = faker.helpers.arrayElement(shapes); let shapeSvg = ''; switch (shape) { case 'circle': shapeSvg = ``; break; case 'square': shapeSvg = ``; break; case 'triangle': shapeSvg = ``; break; case 'hexagon': shapeSvg = ``; break; } return ` ${shapeSvg} ${initials} `; } /** * Generates a deterministic SVG cover for a league */ generateLeagueCover(leagueId: string): string { faker.seed(this.hashCode(leagueId)); const primaryColor = faker.color.rgb({ format: 'hex' }); const secondaryColor = faker.color.rgb({ format: 'hex' }); return ` `; } /** * Generates a simple PNG placeholder (base64 encoded) * In production, this would serve actual PNG files from public assets */ generateDefaultPNG(variant: string): Buffer { // For now, generate a simple colored square as PNG placeholder // In production, this would read actual PNG files faker.seed(this.hashCode(variant)); const color = faker.color.rgb({ format: 'hex' }); // Parse the hex color const r = parseInt(color.slice(1, 3), 16); const g = parseInt(color.slice(3, 5), 16); const b = parseInt(color.slice(5, 7), 16); // Create a minimal valid PNG (1x1 pixel) with the variant color // This is a very basic PNG - in production you'd serve real files // PNG header and minimal data for a 1x1 RGB pixel const pngHeader = Buffer.from([ // PNG signature 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, // IHDR chunk (13 bytes) 0x00, 0x00, 0x00, 0x0D, // Length: 13 0x49, 0x48, 0x44, 0x52, // Type: IHDR 0x00, 0x00, 0x00, 0x01, // Width: 1 0x00, 0x00, 0x00, 0x01, // Height: 1 0x08, // Bit depth: 8 0x02, // Color type: RGB 0x00, // Compression method 0x00, // Filter method 0x00, // Interlace method 0x00, 0x00, 0x00, 0x00, // CRC (placeholder, simplified) // IDAT chunk (image data) 0x00, 0x00, 0x00, 0x07, // Length: 7 0x49, 0x44, 0x41, 0x54, // Type: IDAT 0x08, 0x1D, // Zlib header 0x01, // Deflate block header r, g, b, // RGB pixel data 0x00, 0x00, 0x00, 0x00, // CRC (placeholder) // IEND chunk 0x00, 0x00, 0x00, 0x00, // Length: 0 0x49, 0x45, 0x4E, 0x44, // Type: IEND 0xAE, 0x42, 0x60, 0x82 // CRC (placeholder) ]); return pngHeader; } private hashCode(str: string): number { let hash = 0; for (let i = 0; i < str.length; i++) { const char = str.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; } return Math.abs(hash); } }