export type ChatAttachmentKind = 'IMAGE' | 'AUDIO' | 'VOICE' | 'VIDEO' | 'VIDEO_NOTE' | 'FILE'; const EXTENSION_TO_MIME: Record = { jpg: 'image/jpeg', jpeg: 'image/jpeg', png: 'image/png', webp: 'image/webp', gif: 'image/gif', heic: 'image/heic', heif: 'image/heif', mp3: 'audio/mpeg', m4a: 'audio/mp4', ogg: 'audio/ogg', opus: 'audio/ogg', webm: 'audio/webm', wav: 'audio/wav', aac: 'audio/aac', flac: 'audio/flac', mp4: 'video/mp4', mov: 'video/quicktime', mkv: 'video/x-matroska', avi: 'video/x-msvideo', '3gp': 'video/3gpp', pdf: 'application/pdf', doc: 'application/msword', docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', xls: 'application/vnd.ms-excel', xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', ppt: 'application/vnd.ms-powerpoint', pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation', txt: 'text/plain', csv: 'text/csv', zip: 'application/zip', rar: 'application/vnd.rar', '7z': 'application/x-7z-compressed', json: 'application/json', apk: 'application/vnd.android.package-archive' }; const MIME_ALIASES: Record = { 'audio/x-m4a': 'audio/mp4', 'audio/x-aac': 'audio/aac', 'audio/x-wav': 'audio/wav', 'audio/x-flac': 'audio/flac', 'audio/x-mpeg': 'audio/mpeg', 'audio/mp3': 'audio/mpeg', 'image/jpg': 'image/jpeg', 'image/pjpeg': 'image/jpeg', 'application/x-pdf': 'application/pdf', 'application/x-zip-compressed': 'application/zip', 'video/x-matroska': 'video/x-matroska', 'video/3gpp': 'video/3gpp' }; function extractFileExtension(value?: string | null) { if (!value) return ''; const clean = value.split('?')[0]?.split('#')[0] ?? value; const parts = clean.split('.'); if (parts.length < 2) return ''; return parts.pop()?.trim().toLowerCase() ?? ''; } export function resolveChatContentType(file: Pick) { const raw = (file.type ?? '').trim().toLowerCase(); const base = raw.split(';')[0]?.trim() ?? ''; const aliased = MIME_ALIASES[base] ?? base; if (aliased && aliased !== 'application/octet-stream') { return aliased; } const extension = extractFileExtension(file.name); if (extension && EXTENSION_TO_MIME[extension]) { return EXTENSION_TO_MIME[extension]; } return aliased || 'application/octet-stream'; } export function inferChatMessageType(contentType: string, options?: { voice?: boolean; videoNote?: boolean }): ChatAttachmentKind { const normalized = resolveChatContentType({ type: contentType, name: '' }); if (options?.videoNote && normalized.startsWith('video/')) return 'VIDEO_NOTE'; if (normalized.startsWith('image/')) return 'IMAGE'; if (normalized.startsWith('video/')) return 'VIDEO'; if (normalized.startsWith('audio/')) { return options?.voice ? 'VOICE' : 'AUDIO'; } return 'FILE'; } export function detectChatMessageType(file: File, options?: { voice?: boolean; videoNote?: boolean }) { const contentType = resolveChatContentType(file); return inferChatMessageType(contentType, options); } export function isVideoFile(file: Pick) { return resolveChatContentType(file).startsWith('video/'); } export function formatFileSize(bytes?: number) { if (!bytes || bytes <= 0) return ''; if (bytes < 1024) return `${bytes} Б`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} КБ`; return `${(bytes / (1024 * 1024)).toFixed(1)} МБ`; } export function parseMessageMetadata(metadataJson?: string) { if (!metadataJson) return {} as { fileName?: string; fileSize?: number; durationMs?: number; videoNote?: boolean }; try { return JSON.parse(metadataJson) as { fileName?: string; fileSize?: number; durationMs?: number; videoNote?: boolean }; } catch { return {}; } } export function fileIconLabel(fileName?: string, mimeType?: string) { const extension = extractFileExtension(fileName); if (extension === 'pdf' || mimeType === 'application/pdf') return 'PDF'; if (['doc', 'docx'].includes(extension)) return 'DOC'; if (['xls', 'xlsx', 'csv'].includes(extension)) return 'XLS'; if (['ppt', 'pptx'].includes(extension)) return 'PPT'; if (['zip', 'rar', '7z'].includes(extension)) return 'ZIP'; if (extension === 'apk') return 'APK'; if (['mp4', 'mov', 'webm', 'mkv'].includes(extension) || mimeType?.startsWith('video/')) return 'VID'; if (extension) return extension.toUpperCase().slice(0, 4); return 'FILE'; }