using Microsoft.Extensions.Logging.Abstractions; using SteUp.Shared.Core.Dto; using SteUp.Shared.Core.Entities; using SteUp.Shared.Core.Helpers; using SteUp.Shared.Core.Interface.System; namespace SteUp.Maui.Core.Services; public class AttachedService : IAttachedService { private static string AttachedRoot => Path.Combine(FileSystem.CacheDirectory, "attached"); public async Task SelectImageFromCamera() { var cameraPerm = await Permissions.RequestAsync(); var storagePerm = await Permissions.RequestAsync(); if (cameraPerm != PermissionStatus.Granted || storagePerm != PermissionStatus.Granted) return null; FileResult? result; try { result = await MediaPicker.Default.CapturePhotoAsync(); result?.FileName = $"img_{DateTime.Now:ddMMyyy_hhmmss}{result.FileName[result.FileName.IndexOf('.')..]}"; } catch (Exception ex) { Console.WriteLine($"Errore cattura foto: {ex.Message}"); SentrySdk.CaptureException(ex); return null; } return result is null ? null : await ConvertToDto(result, AttachedDto.TypeAttached.Image); } public async Task?> SelectImageFromGallery() { List? resultList; var storagePerm = await Permissions.RequestAsync(); if (storagePerm != PermissionStatus.Granted) return null; try { resultList = await MediaPicker.Default.PickPhotosAsync(); } catch (Exception ex) { Console.WriteLine($"Errore selezione galleria: {ex.Message}"); SentrySdk.CaptureException(ex); return null; } if (resultList.IsNullOrEmpty()) return null; List returnList = []; foreach (var fileResult in resultList) { returnList.Add(await ConvertToDto(fileResult, AttachedDto.TypeAttached.Image)); } return returnList; } private static async Task ConvertToDto(FileResult file, AttachedDto.TypeAttached type) { var stream = await file.OpenReadAsync(); using var ms = new MemoryStream(); await stream.CopyToAsync(ms); return new AttachedDto { Name = file.FileName, Path = file.FullPath, MimeType = file.ContentType, DimensionBytes = ms.Length, FileBytes = ms.ToArray(), Type = type }; } private async Task ConvertToDto(FileInfo file, AttachedDto.TypeAttached type) { var (origUrl, thumbUrl) = await SaveAndCreateThumbAsync( await File.ReadAllBytesAsync(file.FullName), file.Name ); return new AttachedDto { Name = file.Name, Path = file.FullName, TempPath = origUrl, ThumbPath = thumbUrl, Type = type, SavedOnAppData = true }; } public async Task?> GetInspectionFiles(Ispezione ispezione) { var baseDir = FileSystem.AppDataDirectory; var inspectionDir = Path.Combine(baseDir, $"attached_{GetInspectionKey(ispezione)}"); var directory = new DirectoryInfo(inspectionDir); if (!directory.Exists) return null; var fileList = directory.GetFiles().ToList(); var returnList = new List(); foreach (var file in fileList) { returnList.Add(await ConvertToDto(file, AttachedDto.TypeAttached.Image)); } return returnList; } public async Task SaveInspectionFile(Ispezione ispezione, byte[] file, string fileName, CancellationToken ct) { ArgumentNullException.ThrowIfNull(ispezione); ArgumentNullException.ThrowIfNull(file); ArgumentException.ThrowIfNullOrWhiteSpace(fileName); var baseDir = FileSystem.AppDataDirectory; var inspectionDir = Path.Combine(baseDir, $"attached_{GetInspectionKey(ispezione)}"); if (!Directory.Exists(inspectionDir)) Directory.CreateDirectory(inspectionDir); var filePath = Path.Combine(inspectionDir, fileName); await File.WriteAllBytesAsync(filePath, file, ct); return filePath; } public bool RemoveInspectionFile(Ispezione ispezione, string fileName) { var baseDir = FileSystem.AppDataDirectory; var inspectionDir = Path.Combine(baseDir, $"attached_{GetInspectionKey(ispezione)}"); if (!Directory.Exists(inspectionDir)) return false; if (string.IsNullOrWhiteSpace(fileName)) return false; var filePath = Path.Combine(inspectionDir, fileName); if (!File.Exists(filePath)) return false; File.Delete(filePath); if (!Directory.EnumerateFileSystemEntries(inspectionDir).Any()) Directory.Delete(inspectionDir); return true; } public async Task SaveToTempStorage(Stream file, string fileName, CancellationToken ct = default) { ArgumentNullException.ThrowIfNull(file); if (file.CanSeek) file.Position = 0; fileName = Path.GetFileName(fileName); var dir = FileSystem.CacheDirectory; var filePath = Path.Combine(dir, fileName); await using var fileStream = File.Create(filePath); await file.CopyToAsync(fileStream, ct); return filePath; } public Task CleanTempStorageAsync(CancellationToken ct = default) { return Task.Run(() => { if (Directory.Exists(AttachedRoot)) Directory.Delete(AttachedRoot, true); }, ct); } public Task OpenFile(string fileName, string filePath) { #if IOS throw new NotImplementedException(); #else return Launcher.OpenAsync(new OpenFileRequest { Title = "Apri file", File = new ReadOnlyFile(filePath) }); #endif } public async Task<(string originalUrl, string thumbUrl)> SaveAndCreateThumbAsync( byte[] bytes, string fileName, CancellationToken ct = default) { Directory.CreateDirectory(AttachedRoot); var id = Guid.NewGuid().ToString("N"); var safeName = SanitizeFileName(fileName); var originalFile = $"{id}_{safeName}"; var thumbFile = $"{id}_thumb.jpg"; var originalPath = Path.Combine(AttachedRoot, originalFile); await File.WriteAllBytesAsync(originalPath, bytes, ct); var thumbPath = Path.Combine(AttachedRoot, thumbFile); await ImageThumb.CreateThumbnailAsync(originalPath, thumbPath, maxSide: 320, quality: 70, ct); return ($"https://localfiles/attached/{originalFile}", $"https://localfiles/attached/{thumbFile}"); } private static string SanitizeFileName(string fileName) { var name = Path.GetFileName(fileName); return Path.GetInvalidFileNameChars().Aggregate(name, (current, c) => current.Replace(c, '_')); } private static string GetInspectionKey(Ispezione ispezione) => $"{ispezione.CodMdep}_{ispezione.Data:ddMMyyyy}_{ispezione.Rilevatore.ToLower()}"; }