using System;
using System.IO;
using System.Threading.Tasks;
using Serilog;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Interfaces;
namespace Wino.Services;
///
/// Stores contact pictures as JPEG files under {ApplicationDataFolderPath}/contacts/{fileId}.jpg.
/// This avoids base64 inline storage in SQLite that bloats all AccountContact queries.
///
public class ContactPictureFileService : BaseDatabaseService, IContactPictureFileService
{
private sealed class LegacyAccountContactPictureRow
{
public string Address { get; set; }
public string Base64ContactPicture { get; set; }
}
private const string ContactsSubFolder = "contacts";
private readonly string _contactPicturesFolder;
private readonly ILogger _logger = Log.ForContext();
public ContactPictureFileService(IDatabaseService databaseService, IApplicationConfiguration applicationConfiguration)
: base(databaseService)
{
_contactPicturesFolder = Path.Combine(applicationConfiguration.ApplicationDataFolderPath, ContactsSubFolder);
Directory.CreateDirectory(_contactPicturesFolder);
}
public string GetContactPicturePath(Guid fileId)
{
var path = BuildFilePath(fileId);
return File.Exists(path) ? path : null;
}
public async Task SaveContactPictureAsync(byte[] imageData)
{
var fileId = Guid.NewGuid();
var filePath = BuildFilePath(fileId);
await File.WriteAllBytesAsync(filePath, imageData).ConfigureAwait(false);
return fileId;
}
public Task DeleteContactPictureAsync(Guid fileId)
{
var filePath = BuildFilePath(fileId);
if (File.Exists(filePath))
File.Delete(filePath);
return Task.CompletedTask;
}
public async Task MigrateBase64PicturesAsync()
{
try
{
var contacts = await Connection
.QueryAsync(
"SELECT Address, Base64ContactPicture FROM AccountContact WHERE Base64ContactPicture IS NOT NULL AND ContactPictureFileId IS NULL")
.ConfigureAwait(false);
foreach (var contact in contacts)
{
try
{
var base64 = contact.Base64ContactPicture;
if (string.IsNullOrEmpty(base64))
continue;
var bytes = Convert.FromBase64String(base64);
var fileId = await SaveContactPictureAsync(bytes).ConfigureAwait(false);
await Connection.ExecuteAsync(
"UPDATE AccountContact SET ContactPictureFileId = ?, Base64ContactPicture = NULL WHERE Address = ?",
fileId,
contact.Address).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.Error(ex, "Failed to migrate Base64ContactPicture for contact {Address}.", contact.Address);
}
}
}
catch (Exception ex)
{
_logger.Error(ex, "Failed to migrate contact pictures from base64 to file system.");
}
}
private string BuildFilePath(Guid fileId) => Path.Combine(_contactPicturesFolder, $"{fileId}.jpg");
}