using MySqlConnector; using System.Collections.Concurrent; using System.Net.Http; using System.Text; namespace UtopiaCanteenSystem.Services; /// /// Fetches employee photo from hrms.employee_photo by parent_document_id (employee document id from lookup). /// Handles data URI (data:image/...;base64,...) or raw image bytes. Caches by key to avoid repeated DB calls. /// public class EmployeePhotoService : IEmployeePhotoService { private readonly IConfigService _configService; private readonly ConcurrentDictionary _cache = new(StringComparer.OrdinalIgnoreCase); private const int MaxCacheEntries = 50; private readonly HttpClient _httpClient; public EmployeePhotoService(IConfigService configService, HttpClient httpClient) { _configService = configService; _httpClient = httpClient; _httpClient = httpClient; } /// //public async Task GetPhotoBytesAsync(string parentDocumentId, CancellationToken cancellationToken = default) //{ // if (string.IsNullOrWhiteSpace(parentDocumentId)) // return null; // var key = parentDocumentId.Trim(); // if (_cache.TryGetValue(key, out var cached)) // return cached; // var connectionString = _configService.GetHrmsLookupConnectionString(); // if (string.IsNullOrWhiteSpace(connectionString)) // return null; // try // { // await using var conn = new MySqlConnection(connectionString); // await conn.OpenAsync(cancellationToken).ConfigureAwait(false); // // employee_photo.employee_id stores the employee document id (parent_document_id from employee lookup) // const string sql = "SELECT photo_blob FROM hrms.employee_photo WHERE employee_id = @parentDocumentId LIMIT 1"; // await using var cmd = new MySqlCommand(sql, conn); // cmd.Parameters.AddWithValue("@parentDocumentId", key); // await using var reader = await cmd.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false); // if (!await reader.ReadAsync(cancellationToken).ConfigureAwait(false)) // return null; // if (reader.IsDBNull(0)) // return null; // byte[] blob; // try // { // var len = reader.GetBytes(0, 0, null, 0, 0); // blob = new byte[len]; // reader.GetBytes(0, 0, blob, 0, (int)len); // } // catch // { // return null; // } // var imageBytes = DecodeBlobToImageBytes(blob); // if (imageBytes == null || imageBytes.Length == 0) // return null; // // Cache (evict old if needed) // while (_cache.Count >= MaxCacheEntries && _cache.Count > 0) // { // var first = _cache.Keys.FirstOrDefault(); // if (first != null) // _cache.TryRemove(first, out _); // else // break; // } // _cache[key] = imageBytes; // return imageBytes; // } // catch // { // return null; // } //} public async Task GetPhotoBytesAsync(string parentDocumentId, CancellationToken cancellationToken = default) { if (string.IsNullOrWhiteSpace(parentDocumentId)) return null; var key = parentDocumentId.Trim(); if (_cache.TryGetValue(key, out var cached)) return cached; try { var url = $"https://portal.utopiaindustries.pk/uind/employee-photo/{Uri.EscapeDataString(key)}.jpeg"; using var response = await _httpClient.GetAsync(url, cancellationToken).ConfigureAwait(false); if (!response.IsSuccessStatusCode) return null; var contentType = response.Content.Headers.ContentType?.MediaType; if (!string.IsNullOrWhiteSpace(contentType) && !contentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase)) { return null; } var imageBytes = await response.Content.ReadAsByteArrayAsync(cancellationToken).ConfigureAwait(false); if (imageBytes == null || imageBytes.Length == 0) return null; while (_cache.Count >= MaxCacheEntries && _cache.Count > 0) { var first = _cache.Keys.FirstOrDefault(); if (first != null) _cache.TryRemove(first, out _); else break; } _cache[key] = imageBytes; return imageBytes; } catch { return null; } } /// /// Converts blob to image bytes: if blob is UTF-8 "data:image...;base64,<payload>", decodes base64; else treats as raw image. /// internal static byte[]? DecodeBlobToImageBytes(byte[] blob) { if (blob == null || blob.Length == 0) return null; try { var str = Encoding.UTF8.GetString(blob); if (str.StartsWith("data:image", StringComparison.OrdinalIgnoreCase)) { var commaIndex = str.IndexOf(','); if (commaIndex < 0) return null; var base64 = str.Substring(commaIndex + 1).Trim(); return Convert.FromBase64String(base64); } return blob; } catch { return null; } } }