using Marco.Pms.DataAccess.Data;
using Marco.Pms.Model.Activities;
using Marco.Pms.Model.Dtos.DocumentManager;
using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Filters;
using Marco.Pms.Model.Mapper;
using Marco.Pms.Model.Projects;
using Marco.Pms.Model.Utilities;
using Marco.Pms.Services.Service;
using MarcoBMS.Services.Helpers;
using MarcoBMS.Services.Service;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis;
using Microsoft.EntityFrameworkCore;
using Serilog;
using System.Text.Json;

namespace Marco.Pms.Services.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    [Authorize]
    public class ImageController : ControllerBase
    {
        private readonly IDbContextFactory<ApplicationDbContext> _dbContextFactory;
        private readonly ApplicationDbContext _context;
        private readonly S3UploadService _s3Service;
        private readonly UserHelper _userHelper;
        private readonly ILoggingService _logger;
        private readonly PermissionServices _permission;
        private readonly Guid tenantId;
        public ImageController(IDbContextFactory<ApplicationDbContext> dbContextFactory,
            ApplicationDbContext context,
            S3UploadService s3Service,
            UserHelper userHelper,
            ILoggingService logger,
            PermissionServices permission)
        {
            _dbContextFactory = dbContextFactory ?? throw new ArgumentNullException(nameof(dbContextFactory));
            _context = context ?? throw new ArgumentNullException(nameof(context));
            _s3Service = s3Service ?? throw new ArgumentNullException(nameof(s3Service));
            _userHelper = userHelper ?? throw new ArgumentNullException(nameof(userHelper));
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
            _permission = permission ?? throw new ArgumentNullException(nameof(permission));
            tenantId = userHelper.GetTenantId();
        }

        [HttpGet("images/{projectId}")]

        public async Task<IActionResult> GetImageList(Guid projectId, [FromQuery] string? filter, [FromQuery] int pageNumber = 1, [FromQuery] int pageSize = 10)
        {
            _logger.LogInfo("[GetImageList] Called by Employee for ProjectId: {ProjectId}", projectId);

            var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();

            // Step 1: Validate project existence
            var isProjectExist = await _context.Projects.AnyAsync(p => p.Id == projectId && p.TenantId == tenantId);
            if (!isProjectExist)
            {
                _logger.LogWarning("[GetImageList] ProjectId: {ProjectId} not found", projectId);
                return BadRequest(ApiResponse<object>.ErrorResponse("Project not found", "Project not found in database", 400));
            }

            // Step 2: Check project access permission
            var hasPermission = await _permission.HasProjectPermission(loggedInEmployee, projectId);
            if (!hasPermission)
            {
                _logger.LogWarning("[GetImageList] Access denied for EmployeeId: {EmployeeId} on ProjectId: {ProjectId}", loggedInEmployee.Id, projectId);
                return StatusCode(403, ApiResponse<object>.ErrorResponse("You don't have access", "You don't have access", 403));
            }

            // Step 3: Deserialize filter
            ImageFilter? imageFilter = null;
            if (!string.IsNullOrWhiteSpace(filter))
            {
                try
                {
                    var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
                    //string unescapedJsonString = JsonSerializer.Deserialize<string>(filter, options) ?? "";
                    //imageFilter = JsonSerializer.Deserialize<ImageFilter>(unescapedJsonString, options);
                    imageFilter = JsonSerializer.Deserialize<ImageFilter>(filter, options);
                }
                catch (Exception ex)
                {
                    _logger.LogWarning("[GetImageList] Failed to parse filter: {Message}", ex.Message);
                }
            }

            var taskQuery = _context.TaskAllocations
                .Include(t => t.Employee)
                .Include(t => t.ReportedBy)
                .Include(t => t.ApprovedBy)
                .Include(t => t.WorkStatus)
                .Include(t => t.WorkItem)
                    .ThenInclude(wi => wi!.ActivityMaster)
                        .ThenInclude(a => a!.ActivityGroup)
                            .ThenInclude(ag => ag!.Service)
                .Include(t => t.WorkItem)
                    .ThenInclude(wi => wi!.WorkArea)
                        .ThenInclude(wa => wa!.Floor)
                            .ThenInclude(f => f!.Building)
                .Include(t => t.WorkItem)
                    .ThenInclude(wi => wi!.WorkCategoryMaster)
                .Where(t => t.WorkItem != null &&
                            t.WorkItem.WorkArea != null &&
                            t.WorkItem.WorkArea.Floor != null &&
                            t.WorkItem.WorkArea.Floor.Building != null &&
                            t.WorkItem.WorkArea.Floor.Building.ProjectId == projectId &&
                            t.TenantId == tenantId);

            // Step 4: Extract filter values
            var buildingIds = imageFilter?.BuildingIds;
            var floorIds = imageFilter?.FloorIds;
            var workAreaIds = imageFilter?.WorkAreaIds;
            var activityIds = imageFilter?.ActivityIds;
            var workCategoryIds = imageFilter?.WorkCategoryIds;
            var startDate = imageFilter?.StartDate;
            var endDate = imageFilter?.EndDate;
            var uploadedByIds = imageFilter?.UploadedByIds;
            var serviceIds = imageFilter?.ServiceIds;

            if (buildingIds?.Any() ?? false)
            {

                taskQuery = taskQuery
                .Where(t => t.WorkItem != null &&
                            t.WorkItem.WorkArea != null &&
                            t.WorkItem.WorkArea.Floor != null &&
                            buildingIds.Contains(t.WorkItem.WorkArea.Floor.BuildingId));
            }

            if (floorIds?.Any() ?? false)
            {
                taskQuery = taskQuery
                .Where(t => t.WorkItem != null &&
                            t.WorkItem.WorkArea != null &&
                            floorIds.Contains(t.WorkItem.WorkArea.FloorId));
            }

            if (workAreaIds?.Any() ?? false)
            {
                taskQuery = taskQuery
                .Where(t => t.WorkItem != null &&
                            workAreaIds.Contains(t.WorkItem.WorkAreaId));
            }


            if (activityIds?.Any() == true)
            {
                taskQuery = taskQuery
                    .Where(t => t.WorkItem != null &&
                            activityIds.Contains(t.WorkItem.ActivityId));
            }

            if (workCategoryIds?.Any() == true)
            {
                taskQuery = taskQuery
                    .Where(t => t.WorkItem != null &&
                            t.WorkItem.WorkCategoryId.HasValue &&
                            workCategoryIds.Contains(t.WorkItem.WorkCategoryId.Value));
            }

            if (serviceIds?.Any() ?? false)
            {
                taskQuery = taskQuery.Where(t => t.WorkItem != null &&
                    t.WorkItem.ActivityMaster != null &&
                    t.WorkItem.ActivityMaster.ActivityGroup != null &&
                    serviceIds.Contains(t.WorkItem.ActivityMaster.ActivityGroup.ServiceId));
            }

            // Step 6: Fetch task allocations and comments
            var tasks = await taskQuery.ToListAsync();

            var taskIds = tasks.Select(t => t.Id).ToList();

            var comments = await _context.TaskComments.Include(c => c.Employee)
                .Where(c => taskIds.Contains(c.TaskAllocationId)).ToListAsync();
            var commentIds = comments.Select(c => c.Id).ToList();

            var attachments = await _context.TaskAttachments
                .Where(ta => taskIds.Contains(ta.ReferenceId) || commentIds.Contains(ta.ReferenceId)).ToListAsync();

            var documentIds = attachments.Select(ta => ta.DocumentId).ToList();

            // Step 7: Fetch and filter documents
            List<DocumentBatchDto> documents = new List<DocumentBatchDto>();
            var docQuery = _context.Documents.Include(d => d.UploadedBy)
                .Where(d => documentIds.Contains(d.Id) && d.TenantId == tenantId);
            if (startDate != null && endDate != null)
            {
                docQuery = docQuery.Where(d => d.UploadedAt.Date >= startDate.Value.Date && d.UploadedAt.Date <= endDate.Value.Date);
            }

            int totalRecords = await docQuery.GroupBy(d => d.BatchId).CountAsync();
            int totalPages = (int)Math.Ceiling((double)totalRecords / pageSize);

            documents = await docQuery
            .GroupBy(d => d.BatchId)
            .OrderByDescending(g => g.Max(d => d.UploadedAt))
            .Skip((pageNumber - 1) * pageSize)
            .Take(pageSize)
            .Select(g => new DocumentBatchDto
            {
                BatchId = g.Key,
                Documents = g.ToList()
            })
            .ToListAsync();


            // Step 8: Build response
            var documentVM = documents.Select(d =>
            {
                var docIds = d.Documents?.Select(x => x.Id).ToList() ?? new List<Guid>();
                var refId = attachments.FirstOrDefault(ta => docIds.Contains(ta.DocumentId))?.ReferenceId;
                var task = tasks.FirstOrDefault(t => t.Id == refId);
                var comment = comments.FirstOrDefault(c => c.Id == refId);

                var source = task != null ? "Report" : comment != null ? "Comment" : "";
                var uploadedBy = task?.ReportedBy ?? comment?.Employee;

                if (comment != null)
                {
                    task = tasks.FirstOrDefault(t => t.Id == comment.TaskAllocationId);
                }
                if (task != null)
                {
                    comment = comments.OrderBy(c => c.CommentDate).FirstOrDefault(c => c.TaskAllocationId == task.Id);
                }

                var workItem = task!.WorkItem;
                var workArea = task!.WorkItem!.WorkArea;
                var floor = task!.WorkItem!.WorkArea!.Floor;
                var building = task!.WorkItem!.WorkArea!.Floor!.Building;

                return new
                {

                    BatchId = d.BatchId,
                    Documents = d.Documents?.Select(x => new
                    {
                        Id = x.Id,
                        thumbnailUrl = x.ThumbS3Key != null ? _s3Service.GeneratePreSignedUrl(x.ThumbS3Key) : (x.S3Key != null ? _s3Service.GeneratePreSignedUrl(x.S3Key) : null),
                        Url = x.S3Key != null ? _s3Service.GeneratePreSignedUrl(x.S3Key) : null,
                        UploadedBy = x.UploadedBy?.ToBasicEmployeeVMFromEmployee() ?? uploadedBy?.ToBasicEmployeeVMFromEmployee(),
                        UploadedAt = x.UploadedAt,
                    }).ToList(),
                    Source = source,
                    ProjectId = projectId,
                    BuildingId = building?.Id,
                    BuildingName = building?.Name,
                    FloorIds = floor?.Id,
                    FloorName = floor?.FloorName,
                    WorkAreaId = workArea?.Id,
                    WorkAreaName = workArea?.AreaName,
                    TaskId = task?.Id,
                    ActivityId = workItem?.ActivityMaster?.Id,
                    ActivityName = workItem?.ActivityMaster?.ActivityName,
                    WorkCategoryId = workItem?.WorkCategoryMaster?.Id,
                    WorkCategoryName = workItem?.WorkCategoryMaster?.Name,
                    CommentId = comment?.Id,
                    Comment = comment?.Comment
                };
            }).ToList();

            if (uploadedByIds?.Any() == true)
            {
                documentVM = documentVM.Where(d => d.Documents != null && d.Documents.Any(x => uploadedByIds.Contains(x.UploadedBy?.Id ?? Guid.Empty))).ToList();
            }

            var VM = new
            {
                TotalCount = totalRecords,
                TotalPages = totalPages,
                CurrentPage = pageNumber,
                PageSize = pageSize,
                Data = documentVM
            };

            _logger.LogInfo("[GetImageList] Fetched {Count} documents for ProjectId: {ProjectId}", documentVM.Count, projectId);
            return Ok(ApiResponse<object>.SuccessResponse(VM, $"{documentVM.Count} image records fetched successfully", 200));
        }

        [HttpGet("batch/{batchId}")]
        public async Task<IActionResult> GetImagesByBatch(Guid batchId)
        {
            _logger.LogInfo("GetImagesByBatch called for BatchId: {BatchId}", batchId);

            // Step 1: Get the logged-in employee
            var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();

            // Step 2: Retrieve all documents in the batch
            var documents = await _context.Documents
                .Include(d => d.UploadedBy)
                .Where(d => d.BatchId == batchId)
                .ToListAsync();

            if (!documents.Any())
            {
                _logger.LogWarning("No documents found for BatchId: {BatchId}", batchId);
                return NotFound(ApiResponse<object>.ErrorResponse("No images found", "No images associated with this batch", 404));
            }

            var documentIds = documents.Select(d => d.Id).ToList();

            // Step 3: Get task/comment reference IDs linked to these documents
            var referenceIds = await _context.TaskAttachments
                .Where(ta => documentIds.Contains(ta.DocumentId))
            .Select(ta => ta.ReferenceId)
                .Distinct()
                .ToListAsync();

            // Step 4: Try to identify the source of the attachment (task or comment)
            var task = await _context.TaskAllocations
                .Include(t => t.ReportedBy)
                .FirstOrDefaultAsync(t => referenceIds.Contains(t.Id));

            TaskComment? comment = null;
            WorkItem? workItem = null;
            Employee? uploadedBy = null;
            string source = "";

            if (task != null)
            {
                uploadedBy = task.ReportedBy;
                workItem = await _context.WorkItems
                .Include(wi => wi.ActivityMaster)
                .Include(wi => wi.WorkCategoryMaster)
                    .FirstOrDefaultAsync(wi => wi.Id == task.WorkItemId);
                source = "Report";
            }
            else
            {
                comment = await _context.TaskComments
                    .Include(tc => tc.TaskAllocation)
                    .Include(tc => tc.Employee)
                    .FirstOrDefaultAsync(tc => referenceIds.Contains(tc.Id));
                var workItemId = comment?.TaskAllocation?.WorkItemId;

                uploadedBy = comment?.Employee;
                workItem = await _context.WorkItems
                    .Include(wi => wi.ActivityMaster)
                    .Include(wi => wi.WorkCategoryMaster)
                    .FirstOrDefaultAsync(wi => wi.Id == workItemId);
                source = "Comment";
            }

            // Step 5: Traverse up to building level
            var workAreaId = workItem?.WorkAreaId;
            var workArea = await _context.WorkAreas
                .Include(wa => wa.Floor)
                .FirstOrDefaultAsync(wa => wa.Id == workAreaId);

            var buildingId = workArea?.Floor?.BuildingId;
            var building = await _context.Buildings
                .FirstOrDefaultAsync(b => b.Id == buildingId);

            // Step 6: Construct the response
            var response = new
            {

                BatchId = batchId,
                Documents = documents?.Select(x => new
                {
                    Id = x.Id,
                    thumbnailUrl = x.ThumbS3Key != null ? _s3Service.GeneratePreSignedUrl(x.ThumbS3Key) : (x.S3Key != null ? _s3Service.GeneratePreSignedUrl(x.S3Key) : null),
                    Url = x.S3Key != null ? _s3Service.GeneratePreSignedUrl(x.S3Key) : null,
                    UploadedBy = x.UploadedBy?.ToBasicEmployeeVMFromEmployee() ?? uploadedBy?.ToBasicEmployeeVMFromEmployee(),
                    UploadedAt = x.UploadedAt,
                }).ToList(),
                Source = source,
                ProjectId = building?.ProjectId,
                BuildingId = building?.Id,
                BuildingName = building?.Name,
                FloorIds = workArea?.Floor?.Id,
                FloorName = workArea?.Floor?.FloorName,
                WorkAreaId = workArea?.Id,
                WorkAreaName = workArea?.AreaName,
                TaskId = task?.Id,
                ActivityId = workItem?.ActivityMaster?.Id,
                ActivityName = workItem?.ActivityMaster?.ActivityName,
                WorkCategoryId = workItem?.WorkCategoryMaster?.Id,
                WorkCategoryName = workItem?.WorkCategoryMaster?.Name,
                CommentId = comment?.Id,
                Comment = comment?.Comment
            };

            _logger.LogInfo("Fetched {Count} image(s) for BatchId: {BatchId}", response.Documents?.Count ?? 0, batchId);

            return Ok(ApiResponse<object>.SuccessResponse(response, "Images for provided batchId fetched successfully", 200));
        }

        [HttpGet("filter/{projectId}")]
        public async Task<IActionResult> GetFilterObjectAsync(Guid projectId, CancellationToken ct)
        {
            _logger.LogInfo("GetFilterObject started for ProjectId {ProjectId}", projectId); // start log [memory:1]

            // Validate input early
            if (projectId == Guid.Empty)
            {
                Log.Warning("GetFilterObject received empty ProjectId"); // input validation [memory:1]
                return BadRequest(ApiResponse<object>.ErrorResponse("Invalid project id", 400));
            }

            // Load only what is needed for attachments; project scoping will be enforced downstream
            // NOTE: Select only the columns required to reduce payload
            var taskAttachments = await _context.TaskAttachments
                .AsNoTracking()
                .Select(ta => new { ta.ReferenceId, ta.DocumentId })
                .ToListAsync(ct); // I/O bound work [memory:10]

            if (taskAttachments.Count == 0)
            {
                Log.Information("No task attachments found for ProjectId {ProjectId}", projectId); // early exit [memory:1]
                var emptyResponse = new
                {
                    Buildings = Array.Empty<object>(),
                    Floors = Array.Empty<object>(),
                    WorkAreas = Array.Empty<object>(),
                    WorkCategories = Array.Empty<object>(),
                    Activities = Array.Empty<object>(),
                    UploadedBys = Array.Empty<object>(),
                    Services = Array.Empty<object>()
                };
                return Ok(ApiResponse<object>.SuccessResponse(emptyResponse, "No data found", 200));
            }

            // HashSets for O(1) membership tests and to dedupe upfront
            var referenceIds = new HashSet<Guid>(taskAttachments.Select(x => x.ReferenceId));
            var documentIds = new HashSet<Guid>(taskAttachments.Select(x => x.DocumentId));

            _logger.LogDebug("Collected {ReferenceCount} referenceIds and {DocumentCount} documentIds", referenceIds.Count, documentIds.Count); // metrics [memory:1]

            // Load comments for the references under this tenant
            var comments = await _context.TaskComments
                .AsNoTracking()
                .Where(tc => referenceIds.Contains(tc.Id) && tc.TenantId == tenantId)
                .Select(tc => new { tc.Id, tc.TaskAllocationId })
                .ToListAsync(ct); // filtered selection [memory:10]

            var taskIds = new HashSet<Guid>(comments.Select(c => c.TaskAllocationId));
            _logger.LogDebug("Resolved {CommentCount} comments mapping to {TaskIdCount} taskIds", comments.Count, taskIds.Count); // observation [memory:1]

            // IMPORTANT: Correct project filter should be == not !=
            // Include graph tailored to fields needed in final projection (avoid over-inclusion)
            // Avoid Task.Run for async EF (it doesn’t add value and can harm thread pool)
            var tasks = await _context.TaskAllocations
                .AsNoTracking()
                .Where(t =>
                    t.WorkItem != null &&
                    t.WorkItem.WorkArea != null &&
                    t.WorkItem.WorkArea.Floor != null &&
                    t.WorkItem.WorkArea.Floor.Building != null &&
                    t.WorkItem.WorkArea.Floor.Building.ProjectId == projectId && // fixed filter
                    (taskIds.Contains(t.Id) || referenceIds.Contains(t.Id)))
                .Include(t => t.WorkItem!)
                    .ThenInclude(wi => wi.ActivityMaster!)
                        .ThenInclude(a => a.ActivityGroup!)
                            .ThenInclude(ag => ag.Service)
                .Include(t => t.WorkItem!)
                    .ThenInclude(wi => wi.WorkArea!)
                        .ThenInclude(wa => wa.Floor!)
                            .ThenInclude(f => f.Building)
                .Include(t => t.WorkItem!)
                    .ThenInclude(wi => wi.WorkCategoryMaster)
                // Only select fields used later to reduce tracked object size in memory
                .Select(t => new
                {
                    TaskId = t.Id,
                    Building = new { Id = t.WorkItem!.WorkArea!.Floor!.Building!.Id, Name = t.WorkItem.WorkArea.Floor.Building.Name },
                    Floor = new { Id = t.WorkItem.WorkArea.Floor.Id, Name = t.WorkItem.WorkArea.Floor.FloorName },
                    WorkArea = new { Id = t.WorkItem.WorkArea.Id, Name = t.WorkItem.WorkArea.AreaName },
                    Activity = t.WorkItem.ActivityMaster == null ? null : new { Id = t.WorkItem.ActivityMaster.Id, Name = t.WorkItem.ActivityMaster.ActivityName },
                    WorkCategory = t.WorkItem.WorkCategoryMaster == null ? null : new { Id = t.WorkItem.WorkCategoryMaster.Id, Name = t.WorkItem.WorkCategoryMaster.Name },
                    Service = t.WorkItem.ActivityMaster!.ActivityGroup!.Service == null ? null : new { Id = t.WorkItem.ActivityMaster.ActivityGroup.Service.Id, Name = t.WorkItem.ActivityMaster.ActivityGroup.Service.Name }
                })
                .ToListAsync(ct); // optimized projection [memory:10]

            _logger.LogDebug("Fetched {TaskCount} tasks after filtering and projection", tasks.Count); // metrics [memory:1]

            // Documents query in parallel with tasks is okay; here we’ve already awaited tasks, but both can run together if needed.
            // Only fetch uploader fields needed
            var documents = await _context.Documents
                .AsNoTracking()
                .Where(d => documentIds.Contains(d.Id))
                .Select(d => new
                {
                    d.Id,
                    UploadedBy = d.UploadedBy == null ? null : new
                    {
                        d.UploadedBy.Id,
                        d.UploadedBy.FirstName,
                        d.UploadedBy.LastName
                    }
                })
                .ToListAsync(ct); // minimal shape [memory:10]

            _logger.LogDebug("Fetched {DocumentCount} documents for UploadedBy resolution", documents.Count); // metrics [memory:1]

            // Distinct projections via HashSet to avoid custom equality or anonymous Distinct pitfalls
            static List<T> DistinctBy<T, TKey>(IEnumerable<T> source, Func<T, TKey> keySelector)
                => source.GroupBy(keySelector).Select(g => g.First()).ToList();

            var buildings = DistinctBy(tasks.Select(t => t.Building), b => b.Id);
            var floors = DistinctBy(tasks.Select(t => t.Floor), f => f.Id);
            var workAreas = DistinctBy(tasks.Select(t => t.WorkArea), wa => wa.Id);
            var activities = DistinctBy(tasks.Where(t => t.Activity != null).Select(t => t.Activity!), a => a.Id);
            var workCategories = DistinctBy(tasks.Where(t => t.WorkCategory != null).Select(t => t.WorkCategory!), wc => wc.Id);
            var services = DistinctBy(tasks.Where(t => t.Service != null).Select(t => t.Service!), s => s.Id);

            var uploadedBys = DistinctBy(
                documents.Where(d => d.UploadedBy != null)
                         .Select(d => new
                         {
                             Id = d.UploadedBy!.Id,
                             Name = string.Join(' ', new[] { d.UploadedBy!.FirstName, d.UploadedBy!.LastName }.Where(x => !string.IsNullOrWhiteSpace(x)))
                         }),
                u => u.Id);

            var response = new
            {
                Buildings = buildings,
                Floors = floors,
                WorkAreas = workAreas,
                WorkCategories = workCategories,
                Activities = activities,
                UploadedBys = uploadedBys,
                Services = services
            };

            _logger.LogInfo("GetFilterObject succeeded for ProjectId {ProjectId}. Buildings={Buildings}, Floors={Floors}, WorkAreas={WorkAreas}, Activities={Activities}, WorkCategories={WorkCategories}, Services={Services}, UploadedBys={UploadedBys}",
                projectId, buildings.Count, floors.Count, workAreas.Count, activities.Count, workCategories.Count, services.Count, uploadedBys.Count); // success log [memory:1]

            return Ok(ApiResponse<object>.SuccessResponse(response, "Filter object for image gallery fetched successfully", 200));
        }

        [HttpGet("{documentId}")]
        public async Task<IActionResult> GetImage(Guid documentId)
        {
            // Log the start of the image fetch process
            _logger.LogInfo("GetImage called for DocumentId: {DocumentId}", documentId);

            // Step 1: Get the currently logged-in employee (for future use like permission checks or auditing)
            var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();

            // Step 2: Fetch the document from the database based on the provided ID
            var document = await _context.Documents.FirstOrDefaultAsync(d => d.Id == documentId);

            // Step 3: If document doesn't exist, return a 400 Bad Request response
            if (document == null)
            {
                _logger.LogWarning("Document not found for DocumentId: {DocumentId}", documentId);
                return BadRequest(ApiResponse<object>.ErrorResponse("Document not found", "Document not found", 400));
            }

            // Step 4: Generate pre-signed URLs for thumbnail and full image (if keys exist)
            string? thumbnailUrl = document.ThumbS3Key != null
                ? _s3Service.GeneratePreSignedUrl(document.ThumbS3Key)
                : null;

            string? imageUrl = document.S3Key != null
                ? _s3Service.GeneratePreSignedUrl(document.S3Key)
                : null;

            // Step 5: Prepare the response object
            var response = new
            {
                ThumbnailUrl = thumbnailUrl,
                ImageUrl = imageUrl
            };

            // Step 6: Log successful fetch and return the result
            _logger.LogInfo("Image fetched successfully for DocumentId: {DocumentId}", documentId);
            return Ok(ApiResponse<object>.SuccessResponse(response, "Image fetched successfully", 200));
        }
    }
}
