using Fitness_core_BE.Interfaces; using Fitness_core_BE.Interfaces.Services; using Fitness_core_BE.Models; using Fitness_core_BE.Infra.UoW; using System.Text.Json; using Fitness_core_BE.Helpers; using Fitness_core_BE.Exceptions; using Fitness_core_BE.DTOs; namespace Fitness_core_BE.Services.Document { public class DocumentService : IDocumentService { private readonly IDocumentRepository _docRepository; private readonly IUnitOfWork _uow; public DocumentService(IDocumentRepository docRepository, IUnitOfWork uow) { _docRepository = docRepository; _uow = uow; } public async Task UploadDocument(object payload, HttpContext httpContext) { await _uow.BeginAsync(); try { Guid userId = DataExtractor.GetUserIdFromContext(httpContext); var data = DeserializePayload.Deserialize(payload); var files = data["document"].EnumerateArray(); bool isTicketDoc = data.TryGetValue("IsTicketDoc", out JsonElement isTicketDocElement) && isTicketDocElement.GetBoolean(); var uploadResults = new List(); Guid? ticketId = null; if (isTicketDoc && data.TryGetValue("TicketId", out JsonElement ticketIdElement)) { ticketId = ticketIdElement.GetGuid(); } string uploadsRoot = "./desktop"; if (isTicketDoc && ticketId.HasValue) { // uploadsRoot = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "Uploads", userId.ToString(), ticketId.ToString())); uploadsRoot = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), "..", "Uploads", userId.ToString(), ticketId.Value.ToString())); } else { // uploadsRoot = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "Uploads", userId.ToString())); uploadsRoot = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), "..", "Uploads", userId.ToString())); } if (!Directory.Exists(uploadsRoot)) { Directory.CreateDirectory(uploadsRoot); } foreach (var file in files) { string lastnum = await _docRepository.GetLastSequnce(); string originalFileName = file.GetProperty("FileName").GetString() ?? "document"; string extension = Path.GetExtension(originalFileName).ToLowerInvariant(); string[] allowedExtensions = { ".pdf", ".jpg", ".jpeg", ".png", ".docx", ".mp4", ".mov", ".avi", ".mkv", ".webm", ".3gp" }; if (!allowedExtensions.Contains(extension)) throw new BadRequestException("File type not allowed."); string safeTimestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss"); string filename = $"{lastnum}_{userId}_{safeTimestamp}{extension}"; string baseFile = file.GetProperty("Base64").GetString(); if (baseFile.Contains(",")) baseFile = baseFile.Substring(baseFile.IndexOf(",") + 1); byte[] fileBytes = Convert.FromBase64String(baseFile); if (fileBytes.Length > 10 * 1024 * 1024) throw new BadRequestException("File too large."); string filePath = Path.Combine(uploadsRoot, filename); await File.WriteAllBytesAsync(filePath, fileBytes); // Extract FileType from the file element var fileData = new Dictionary { { "FileType", file.GetProperty("FileType") } }; int docTypeId = DataExtractor.GetRequiredInt(fileData, "FileType"); UserDocument documentMeta = new UserDocument { }; if (isTicketDoc && ticketId.HasValue) { documentMeta = new UserDocument { UserId = userId, DocumentName = originalFileName, DocumentUrl = $"/Uploads/{userId}/{ticketId.Value}/{Uri.EscapeDataString(filename)}", FkDocumentTypeId = docTypeId, }; } else { documentMeta = new UserDocument { UserId = userId, DocumentName = originalFileName, DocumentUrl = $"/Uploads/{userId}/{Uri.EscapeDataString(filename)}", FkDocumentTypeId = docTypeId, }; } var savedDocument = await _docRepository.UploadDocumentAsync(documentMeta); await _uow.SaveChangesAsync(); uploadResults.Add(new { savedDocument.DocumentId, savedDocument.DocumentName, DocumentURL = savedDocument.DocumentUrl }); } await _uow.CommitAsync(); return new { Message = "Documents uploaded successfully.", uploadResults }; } catch { await _uow.RollbackAsync(); throw; } } public async Task GetDocumentsByUserId(object payload, HttpContext httpContext) { // TODO: Uncomment for production - Currently using static userId for testing // if (!Guid.TryParse(httpContext.User.FindFirst("UserId")?.Value, out Guid userId)) // { // throw new InvalidOperationException("Invalid UserId in token."); // } Guid userId = Guid.Parse("5b664468-9ba5-4e6e-b73a-97c9a791c9a0"); List docResults = await _docRepository.GetDocumentsByUserId(userId); if (docResults == null || docResults.Count == 0) return new List(); string uploadsRoot = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), "..")); var documents = new List(); foreach (var doc in docResults) { if (string.IsNullOrEmpty(doc.DocumentUrl)) continue; string relativePath = doc.DocumentUrl.Replace("/", Path.DirectorySeparatorChar.ToString()); Console.WriteLine("Relative Path: " + relativePath); string filePath = uploadsRoot + relativePath; Console.WriteLine("File Path: " + filePath); if (!File.Exists(filePath)) continue; // Skip missing files safely byte[] fileBytes = await File.ReadAllBytesAsync(filePath); documents.Add(new { FileName = doc.DocumentName, FileType = Path.GetExtension(doc.DocumentName), doc.DocumentUrl, doc.DocumentType.DocumentType, Data = Convert.ToBase64String(fileBytes) }); } return documents; } public async Task GetDocumentById(object payload, HttpContext httpContext) { var data = DeserializePayload.Deserialize(payload); var documentId = DataExtractor.GetRequiredInt(data, "documentId"); var document = await _docRepository.GetDocumentById(documentId); if (document != null && !string.IsNullOrEmpty(document.DocumentUrl)) { string uploadsRoot = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), "..")); Console.WriteLine("Uploads Root: " + uploadsRoot); string relativePath = document.DocumentUrl.Replace("/", Path.DirectorySeparatorChar.ToString()); Console.WriteLine("Relative Path: " + relativePath); string filePath = uploadsRoot + relativePath; Console.WriteLine("File Path: " + filePath); if (!File.Exists(filePath)) throw new FileNotFoundException("Document file not found."); byte[] fileBytes = await File.ReadAllBytesAsync(filePath); return new { FileName = document.DocumentName, FileType = Path.GetExtension(document.DocumentName), document.DocumentUrl, document.DocumentType.DocumentType, Data = Convert.ToBase64String(fileBytes) }; } throw new InvalidOperationException("Document not found or invalid document data."); } public async Task RemoveDocument(object payload, HttpContext httpContext) { await _uow.BeginAsync(); try { var data = DeserializePayload.Deserialize(payload); var documentId = DataExtractor.GetRequiredInt(data, "documentId"); var result = await _docRepository.RemoveDocument(documentId); if (!result) { throw new NotFoundException("Document not found"); } await _uow.CommitAsync(); return new { success = true, message = "Document removed successfully" }; } catch { await _uow.RollbackAsync(); throw; } } public async Task GetFileBytesById(int documentId) { var document = await _docRepository.GetDocumentById(documentId); if (document == null || string.IsNullOrEmpty(document.DocumentUrl)) throw new NotFoundException("Document record not found."); string uploadsRoot = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), "..")); string relativePath = document.DocumentUrl.Replace("/", Path.DirectorySeparatorChar.ToString()); string filePath = uploadsRoot + relativePath; if (!File.Exists(filePath)) throw new FileNotFoundException("Physical file is missing on the server."); byte[] fileBytes = await File.ReadAllBytesAsync(filePath); return new DocumentDownloadModel { FileName = document.DocumentName, FileType = Path.GetExtension(document.DocumentName), FileData = fileBytes }; } } }