using Marco.Pms.DataAccess.Data;
using Marco.Pms.Model.DocumentManager;
using Marco.Pms.Model.Dtos.Forum;
using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Forum;
using Marco.Pms.Model.Mapper;
using Marco.Pms.Model.Master;
using Marco.Pms.Model.Projects;
using Marco.Pms.Model.Utilities;
using Marco.Pms.Model.ViewModels.Forum;
using Marco.Pms.Services.Service;
using MarcoBMS.Services.Helpers;
using MarcoBMS.Services.Service;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace Marco.Pms.Services.Controllers
{
    [Authorize]
    [ApiController]
    [Route("api/[controller]")]
    public class ForumController : ControllerBase
    {
        private readonly ApplicationDbContext _context;
        private readonly UserHelper _userHelper;
        private readonly S3UploadService _s3Service;
        private readonly ILoggingService _logger;
        //string tenentId = "1";
        public ForumController(ApplicationDbContext context, S3UploadService s3Service, UserHelper userHelper, ILoggingService logger)
        {
            _context = context;
            _userHelper = userHelper;
            _s3Service = s3Service;
            _logger = logger;
        }

        [HttpPost("ticket")]
        public async Task<IActionResult> CreateNewTicket([FromBody] CreateTicketDto createTicketDto)
        {
            if (!ModelState.IsValid)
            {
                var errors = ModelState.Values
                    .SelectMany(v => v.Errors)
                    .Select(e => e.ErrorMessage)
                    .ToList();
                _logger.LogWarning("{error}", errors);
                return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
            }
            Guid tenantId = _userHelper.GetTenantId();
            var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
            var batchId = Guid.NewGuid();
            TicketForum ticketForum = createTicketDto.ToTicketForumFromCreateTicketDto(tenantId);
            _context.Tickets.Add(ticketForum);
            await _context.SaveChangesAsync();

            List<TicketAttachment> attachments = new List<TicketAttachment>();
            List<TicketTag> ticketTags = new List<TicketTag>();
            List<Document> documents = new List<Document>();
            List<TicketTagVM> tagVMs = new List<TicketTagVM>();

            if (createTicketDto.Attachments != null)
            {
                foreach (var attachmentDto in createTicketDto.Attachments)
                {
                    var Image = attachmentDto;
                    if (string.IsNullOrEmpty(Image.Base64Data))
                    {
                        _logger.LogWarning("Base64 data is missing");
                        return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
                    }

                    //If base64 has a data URI prefix, strip it
                    var base64 = Image.Base64Data.Contains(",")
                        ? Image.Base64Data.Substring(Image.Base64Data.IndexOf(",") + 1)
                        : Image.Base64Data;

                    string fileType = _s3Service.GetContentTypeFromBase64(base64);
                    string fileName = _s3Service.GenerateFileName(fileType, tenantId, "forum");

                    string objectKey = $"tenant-{tenantId}/project-{createTicketDto.LinkedProjectId}/froum/{fileName}";
                    await _s3Service.UploadFileAsync(base64, fileType, objectKey);

                    Document document = attachmentDto.ToDocumentFromForumAttachmentDto(objectKey, objectKey, createTicketDto.CreatedAt, tenantId, batchId, loggedInEmployee.Id);
                    _context.Documents.Add(document);
                    await _context.SaveChangesAsync();

                    documents.Add(document);

                    var attachment = attachmentDto.ToTicketAttachmentFromForumAttachmentDto(ticketForum.Id, document.Id);
                    attachments.Add(attachment);
                }
                _context.TicketAttachments.AddRange(attachments);

            }
            if (createTicketDto.TagIds != null)
            {
                List<TicketTagMaster>? tagMasters = await _context.TicketTagMasters.Where(t => createTicketDto.TagIds.Contains(t.Id)).ToListAsync();
                foreach (var ticketTag in tagMasters)
                {
                    TicketTagVM tagVM = ticketTag.ToTicketTagVMFromTicketTagMaster();

                    TicketTag tag = new TicketTag
                    {
                        TicketId = ticketForum.Id,
                        TagId = ticketTag.Id
                    };
                    tagVMs.Add(tagVM);
                    ticketTags.Add(tag);
                }

                _context.TicketTags.AddRange(ticketTags);
            }
            await _context.SaveChangesAsync();

            var ticket = await _context.Tickets.Include(t => t.TicketTypeMaster).Include(t => t.TicketStatusMaster).Include(t => t.Priority).FirstOrDefaultAsync(t => t.Id == ticketForum.Id);

            Employee? employee = await _context.Employees.FirstOrDefaultAsync(e => e.Id == ticketForum.CreatedById);


            ForumTicketVM ticketVM = ticket != null && employee != null ? ticket.ToForumTicketVMFromTicketForum(employee) : new ForumTicketVM();

            ticketVM.Tags = tagVMs;

            List<TicketAttachmentVM> attachmentVMs = new List<TicketAttachmentVM>();
            foreach (var attachment in attachments)
            {
                var document = documents.Find(d => d.Id == attachment.FileId);
                string preSignedUrl = string.Empty;
                if (document != null)
                {
                    preSignedUrl = _s3Service.GeneratePreSignedUrl(document.S3Key);
                }
                attachmentVMs.Add(attachment.ToTicketAttachmentVMFromTicketAttachment(preSignedUrl, preSignedUrl));
            }
            ticketVM.Attachments = attachmentVMs;
            Project project = await _context.Projects.FirstOrDefaultAsync(p => p.Id == ticketForum.LinkedProjectId) ?? new Project();
            ticketVM.ProjectName = project.Name;
            if (createTicketDto.LinkedActivityId != null && createTicketDto.LinkedActivityId != null)
            {
                WorkItem workItem = await _context.WorkItems.Include(w => w.ActivityMaster).FirstOrDefaultAsync(w => w.Id == ticketForum.LinkedActivityId) ?? new WorkItem();
                if (workItem.ActivityMaster != null)
                {
                    ticketVM.ActivityName = workItem.ActivityMaster.ActivityName;
                }
                else
                {
                    ticketVM.ActivityName = "";
                }
            }
            _logger.LogInfo("Ticket created by Employee {EmployeeId} for project {ProjectId}", ticketForum.CreatedById, project.Id);
            return Ok(ApiResponse<object>.SuccessResponse(ticketVM, "Ticket Created Successfully", 200));
        }
        [HttpPost("ticket/edit")]
        public async Task<IActionResult> UpdateNewTicket([FromBody] UpdateTicketDto updateTicketDto)
        {
            if (!ModelState.IsValid)
            {
                var errors = ModelState.Values
                    .SelectMany(v => v.Errors)
                    .Select(e => e.ErrorMessage)
                    .ToList();
                _logger.LogWarning("{error}", errors);
                return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
            }
            Guid tenantId = _userHelper.GetTenantId();
            var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
            var batchId = Guid.NewGuid();

            var existingTicket = await _context.Tickets
                .Include(t => t.TicketTypeMaster)
                .Include(t => t.TicketStatusMaster)
                .Include(t => t.Priority)
                .AsNoTracking()
                .FirstOrDefaultAsync(t => t.Id == updateTicketDto.Id);
            if (existingTicket != null)
            {
                TicketForum ticketForum = updateTicketDto.ToTicketForumFromUpdateTicketDto(existingTicket);
                _context.Tickets.Update(ticketForum);
                await _context.SaveChangesAsync();


                List<TicketAttachment> attachments = new List<TicketAttachment>();
                List<TicketTag> ticketTags = new List<TicketTag>();
                List<TicketTagVM> tagVMs = new List<TicketTagVM>();

                List<TicketAttachment> existingAttachments = await _context.TicketAttachments.Where(a => a.TicketId == updateTicketDto.Id).ToListAsync();
                var existingattachmentids = existingAttachments.Select(a => a.Id).ToList();
                List<Guid> attachmentDtoids = new List<Guid>();
                if (updateTicketDto.Attachments != null)
                {
                    attachmentDtoids = updateTicketDto.Attachments.Select(a => a.Id).ToList();
                    foreach (var attachmentDto in updateTicketDto.Attachments)
                    {
                        if (!existingattachmentids.Contains(attachmentDto.Id) && attachmentDto.TicketId != updateTicketDto.Id)
                        {
                            var Image = attachmentDto;
                            if (string.IsNullOrEmpty(Image.Base64Data))
                            {
                                _logger.LogWarning("Base64 data is missing");
                                return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
                            }

                            //If base64 has a data URI prefix, strip it
                            var base64 = Image.Base64Data.Contains(",")
                                ? Image.Base64Data.Substring(Image.Base64Data.IndexOf(",") + 1)
                                : Image.Base64Data;

                            string fileType = _s3Service.GetContentTypeFromBase64(base64);
                            string fileName = _s3Service.GenerateFileName(fileType, tenantId, "forum");

                            string objectKey = $"tenant-{tenantId}/project-{updateTicketDto.LinkedProjectId}/froum/{fileName}";
                            await _s3Service.UploadFileAsync(base64, fileType, objectKey);

                            Document document = attachmentDto.ToDocumentFromUpdateAttachmentDto(objectKey, objectKey, updateTicketDto.CreatedAt, tenantId, batchId, loggedInEmployee.Id);
                            _context.Documents.Add(document);
                            await _context.SaveChangesAsync();

                            var attachment = attachmentDto.ToTicketAttachmentFromUpdateAttachmentDto(ticketForum.Id, document.Id);
                            attachments.Add(attachment);
                        }
                    }
                }
                if (attachments.Count != 0)
                {
                    _context.TicketAttachments.AddRange(attachments);
                }
                var deletedAttachments = existingAttachments.Where(a => !attachmentDtoids.Contains(a.Id) && a.CommentId == null).ToList();
                var deletedFileIds = deletedAttachments.Select(a => a.FileId).ToList();
                if (deletedFileIds.Count != 0)
                {
                    List<Document> existingDocuments = await _context.Documents.Where(d => deletedFileIds.Contains(d.Id)).ToListAsync();
                    foreach (var existingDocument in existingDocuments)
                    {
                        await _s3Service.DeleteFileAsync(existingDocument.S3Key);
                        //await _s3Service.DeleteFileAsync(existingDocument.ThumbS3Key);
                    }
                    _context.TicketAttachments.RemoveRange(deletedAttachments);
                }

                List<TicketTag> existingTicketTags = await _context.TicketTags.Where(t => t.TicketId == updateTicketDto.Id).ToListAsync();
                List<TicketTagMaster>? tagMasters = await _context.TicketTagMasters.Where(t => updateTicketDto.TagIds != null ? updateTicketDto.TagIds.Contains(t.Id) : false).ToListAsync();
                var existingTicketTagIds = existingTicketTags.Select(t => t.TagId).ToList();

                foreach (var ticketTag in tagMasters)
                {
                    TicketTagVM tagVM = ticketTag.ToTicketTagVMFromTicketTagMaster();

                    TicketTag tag = new TicketTag
                    {
                        TicketId = ticketForum.Id,
                        TagId = ticketTag.Id
                    };
                    tagVMs.Add(tagVM);
                    var demo = !existingTicketTagIds.Contains(tag.TagId);
                    if (!existingTicketTagIds.Contains(tag.TagId))
                    {
                        ticketTags.Add(tag);
                    }
                }

                if (ticketTags != null)
                {
                    _context.TicketTags.AddRange(ticketTags);
                }
                var deletedTicketTags = existingTicketTags.Where(t => updateTicketDto.TagIds != null ? !updateTicketDto.TagIds.Contains(t.TagId) : true).ToList();
                if (deletedTicketTags.Count != 0)
                {
                    _context.TicketTags.RemoveRange(deletedTicketTags);
                }
                await _context.SaveChangesAsync();

                var ticket = await _context.Tickets.Include(t => t.TicketTypeMaster).Include(t => t.TicketStatusMaster).Include(t => t.Priority).FirstOrDefaultAsync(t => t.Id == ticketForum.Id);
                ForumTicketVM ticketVM = new ForumTicketVM();
                if (ticket != null)
                {
                    List<TicketComment> comments = await _context.TicketComments.Where(c => c.TicketId == ticket.Id).ToListAsync();
                    var authorIds = comments.Select(c => c.AuthorId.ToString()).ToList();

                    List<Employee>? employees = await _context.Employees.Where(e => e.Id == ticketForum.CreatedById || authorIds.Contains(e.ApplicationUserId ?? "")).ToListAsync();

                    Employee employee = employees.Find(e => e.Id == ticketForum.CreatedById) ?? new Employee();
                    ticketVM = ticket.ToForumTicketVMFromTicketForum(employee);

                    ticketVM.Tags = tagVMs;

                    List<TicketCommentVM> commentVMs = new List<TicketCommentVM>();
                    foreach (var comment in comments)
                    {
                        employee = employees.Find(e => e.ApplicationUserId == comment.AuthorId.ToString()) ?? new Employee();
                        commentVMs.Add(comment.ToTicketCommentVMFromTicketComment(employee));
                    }

                    attachments = await _context.TicketAttachments.Where(a => a.TicketId == updateTicketDto.Id).ToListAsync();
                    var fileIds = attachments.Select(a => a.FileId).ToList();
                    List<Document> documents = await _context.Documents.Where(d => fileIds.Contains(d.Id)).ToListAsync();
                    List<TicketAttachmentVM> ticketAttachmentVMs = new List<TicketAttachmentVM>();
                    foreach (var attachment in attachments)
                    {
                        var document = documents.Find(d => d.Id == attachment.FileId);
                        string preSignedUrl = string.Empty;
                        if (document != null)
                        {
                            preSignedUrl = _s3Service.GeneratePreSignedUrl(document.S3Key);
                        }
                        if (attachment.CommentId == null)
                        {
                            ticketAttachmentVMs.Add(attachment.ToTicketAttachmentVMFromTicketAttachment(preSignedUrl, preSignedUrl));
                        }
                        else
                        {
                            var commentVM = commentVMs.Find(c => c.Id == attachment.CommentId);
                            if (commentVM != null && commentVM.Attachments != null)
                            {
                                commentVM.Attachments.Add(attachment.ToTicketAttachmentVMFromTicketAttachment(preSignedUrl, preSignedUrl));
                            }
                        }
                    }
                    ticketVM.Comments = commentVMs;
                    ticketVM.Attachments = ticketAttachmentVMs;
                    Project project = await _context.Projects.FirstOrDefaultAsync(p => p.Id == ticketForum.LinkedProjectId) ?? new Project();
                    ticketVM.ProjectName = project.Name;
                    if (updateTicketDto.LinkedActivityId != null && updateTicketDto.LinkedActivityId != null)
                    {
                        WorkItem workItem = await _context.WorkItems.Include(w => w.ActivityMaster).FirstOrDefaultAsync(w => w.Id == ticketForum.LinkedActivityId) ?? new WorkItem();
                        if (workItem.ActivityMaster != null)
                        {
                            ticketVM.ActivityName = workItem.ActivityMaster.ActivityName;
                        }
                        else
                        {
                            ticketVM.ActivityName = "";
                        }
                    }
                }
                _logger.LogInfo("Ticket {TicketId} updated", updateTicketDto.Id);
                return Ok(ApiResponse<object>.SuccessResponse(ticketVM, "Ticket Updated Successfully", 200));
            }
            _logger.LogWarning("Ticket {TicketId} not Found in database", updateTicketDto.Id);
            return NotFound(ApiResponse<object>.ErrorResponse("Ticket not Found", "Ticket not Found", 404));
        }

        [HttpPost("ticket/comment")]
        public async Task<IActionResult> AddComment([FromBody] AddCommentDto addCommentDto)
        {
            if (!ModelState.IsValid)
            {
                var errors = ModelState.Values
                    .SelectMany(v => v.Errors)
                    .Select(e => e.ErrorMessage)
                    .ToList();
                _logger.LogWarning("{error}", errors);
                return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
            }

            Guid tenantId = _userHelper.GetTenantId();
            var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
            var batchId = Guid.NewGuid();

            List<TicketAttachment> attachments = new List<TicketAttachment>();
            List<Document> documents = new List<Document>();

            TicketForum? ticket = await _context.Tickets.FirstOrDefaultAsync(t => t.Id == addCommentDto.TicketId);

            if (ticket == null)
            {
                _logger.LogWarning("Ticket {TicketId} not Found in database", addCommentDto.TicketId);
                return NotFound(ApiResponse<object>.ErrorResponse("Ticket not Found", "Ticket not Found", 404));
            }

            TicketComment comment = addCommentDto.ToTicketCommentFromAddCommentDto(tenantId);
            _context.TicketComments.Add(comment);
            await _context.SaveChangesAsync();

            if (addCommentDto.Attachments != null)
            {
                foreach (var attachmentDto in addCommentDto.Attachments)
                {
                    var Image = attachmentDto;
                    if (string.IsNullOrEmpty(Image.Base64Data))
                    {
                        _logger.LogWarning("Base64 data is missing");
                        return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
                    }

                    //If base64 has a data URI prefix, strip it
                    var base64 = Image.Base64Data.Contains(",")
                        ? Image.Base64Data.Substring(Image.Base64Data.IndexOf(",") + 1)
                        : Image.Base64Data;

                    string fileType = _s3Service.GetContentTypeFromBase64(base64);
                    string fileName = _s3Service.GenerateFileName(fileType, tenantId, "forum");

                    string objectKey = $"tenant-{tenantId}/project-{ticket.LinkedProjectId}/froum/{fileName}";
                    await _s3Service.UploadFileAsync(base64, fileType, objectKey);

                    Document document = attachmentDto.ToDocumentFromForumAttachmentDto(objectKey, objectKey, addCommentDto.SentAt, tenantId, batchId, loggedInEmployee.Id);
                    _context.Documents.Add(document);
                    await _context.SaveChangesAsync();

                    documents.Add(document);

                    var attachment = attachmentDto.ToTicketAttachmentFromForumAttachmentDto(addCommentDto.TicketId, document.Id, comment.Id);
                    attachments.Add(attachment);
                }
                _context.TicketAttachments.AddRange(attachments);
                await _context.SaveChangesAsync();
            }
            Employee employee = await _context.Employees.FirstOrDefaultAsync(e => e.Id == addCommentDto.AuthorId) ?? new Employee();
            TicketCommentVM commentVM = comment.ToTicketCommentVMFromTicketComment(employee);

            List<TicketAttachmentVM> attachmentVMs = new List<TicketAttachmentVM>();
            if (attachments != null)
            {
                foreach (var attachment in attachments)
                {
                    var document = documents.Find(d => d.Id == attachment.FileId);
                    string preSignedUrl = string.Empty;
                    if (document != null)
                    {
                        preSignedUrl = _s3Service.GeneratePreSignedUrl(document.S3Key);
                    }
                    attachmentVMs.Add(attachment.ToTicketAttachmentVMFromTicketAttachment(preSignedUrl, preSignedUrl));
                }
            }
            commentVM.Attachments = attachmentVMs;

            _logger.LogInfo("User {ApplicationUserId} commented on a ticket{TicketId}", comment.AuthorId, comment.TicketId);
            return Ok(ApiResponse<object>.SuccessResponse(commentVM, "Comment Created Successfully", 200));
        }
        [HttpPost("ticket/comment/edit")]
        public async Task<IActionResult> EditComment([FromBody] UpdateCommentDto updateCommentDto)
        {
            if (!ModelState.IsValid)
            {
                var errors = ModelState.Values
                    .SelectMany(v => v.Errors)
                    .Select(e => e.ErrorMessage)
                    .ToList();
                _logger.LogWarning("{error}", errors);
                return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
            }

            Guid tenantId = _userHelper.GetTenantId();
            var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
            var batchId = Guid.NewGuid();

            List<TicketAttachment> attachments = new List<TicketAttachment>();

            TicketForum? ticket = await _context.Tickets.FirstOrDefaultAsync(t => t.Id == updateCommentDto.TicketId);

            if (ticket == null)
            {
                _logger.LogWarning("Ticket {TicketId} not Found in database", updateCommentDto.TicketId);
                return NotFound(ApiResponse<object>.ErrorResponse("Ticket not Found", "Ticket not Found", 404));
            }

            TicketComment existingComment = await _context.TicketComments.AsNoTracking().FirstOrDefaultAsync(c => c.Id == updateCommentDto.Id) ?? new TicketComment();
            TicketComment updateComment = updateCommentDto.ToTicketCommentFromUpdateCommentDto(tenantId, existingComment);
            _context.TicketComments.Update(updateComment);
            await _context.SaveChangesAsync();

            List<TicketAttachment> existingAttachments = await _context.TicketAttachments.Where(a => a.CommentId == updateComment.Id).ToListAsync();
            var existingattachmentids = existingAttachments.Select(a => a.Id).ToList();
            List<Guid> attachmentDtoids = new List<Guid>();
            if (updateCommentDto.Attachments != null)
            {
                attachmentDtoids = updateCommentDto.Attachments.Select(a => a.Id).ToList();

                foreach (var attachmentDto in updateCommentDto.Attachments)
                {
                    if (!existingattachmentids.Contains(attachmentDto.Id) && attachmentDto.CommentId != updateComment.Id)
                    {
                        var Image = attachmentDto;
                        if (string.IsNullOrEmpty(Image.Base64Data))
                        {
                            _logger.LogWarning("Base64 data is missing");
                            return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
                        }

                        //If base64 has a data URI prefix, strip it
                        var base64 = Image.Base64Data.Contains(",")
                            ? Image.Base64Data.Substring(Image.Base64Data.IndexOf(",") + 1)
                            : Image.Base64Data;

                        string fileType = _s3Service.GetContentTypeFromBase64(base64);
                        string fileName = _s3Service.GenerateFileName(fileType, tenantId, "forum");

                        string objectKey = $"tenant-{tenantId}/project-{ticket.LinkedProjectId}/froum/{fileName}";
                        await _s3Service.UploadFileAsync(base64, fileType, objectKey);

                        Document document = attachmentDto.ToDocumentFromUpdateAttachmentDto(objectKey, objectKey, existingComment.SentAt, tenantId, batchId, loggedInEmployee.Id);
                        _context.Documents.Add(document);
                        await _context.SaveChangesAsync();

                        var attachment = attachmentDto.ToTicketAttachmentFromUpdateAttachmentDto(existingComment.TicketId, document.Id, updateComment.Id);
                        attachments.Add(attachment);
                    }
                }
            }
            if (attachments.Count != 0)
            {
                _context.TicketAttachments.AddRange(attachments);
            }
            var deletedAttachments = existingAttachments.Where(a => !attachmentDtoids.Contains(a.Id) && a.CommentId != null).ToList();
            var deletedFileIds = deletedAttachments.Select(a => a.FileId).ToList();
            if (deletedFileIds.Count != 0)
            {
                List<Document> existingDocuments = await _context.Documents.Where(d => deletedFileIds.Contains(d.Id)).ToListAsync();
                foreach (var existingDocument in existingDocuments)
                {
                    await _s3Service.DeleteFileAsync(existingDocument.S3Key);
                    //await _s3Service.DeleteFileAsync(existingDocument.ThumbS3Key);
                }
                _context.TicketAttachments.RemoveRange(deletedAttachments);
            }
            await _context.SaveChangesAsync();

            Employee employee = await _context.Employees.FirstOrDefaultAsync(e => e.Id == existingComment.AuthorId) ?? new Employee();
            TicketCommentVM commentVM = updateComment.ToTicketCommentVMFromTicketComment(employee);

            List<TicketAttachmentVM> attachmentVMs = new List<TicketAttachmentVM>();
            attachments = await _context.TicketAttachments.Where(a => a.CommentId == updateCommentDto.Id).ToListAsync();
            var fileIds = attachments.Select(a => a.FileId).ToList();
            List<Document> documents = await _context.Documents.Where(d => fileIds.Contains(d.Id)).ToListAsync();

            if (attachments != null)
            {
                foreach (var attachment in attachments)
                {
                    var document = documents.Find(d => d.Id == attachment.FileId);
                    string preSignedUrl = string.Empty;
                    if (document != null)
                    {
                        preSignedUrl = _s3Service.GeneratePreSignedUrl(document.S3Key);
                    }
                    attachmentVMs.Add(attachment.ToTicketAttachmentVMFromTicketAttachment(preSignedUrl, preSignedUrl));
                }
            }
            commentVM.Attachments = attachmentVMs;

            _logger.LogInfo("comment {CommentId} was Updated", updateComment.Id);
            return Ok(ApiResponse<object>.SuccessResponse(commentVM, "Comment Updated Successfully", 200));
        }

        [HttpPost("ticket/attachment")]
        public async Task<IActionResult> UploadAttachments([FromBody] List<ForumAttachmentDto> forumAttachmentDtos)
        {
            if (!ModelState.IsValid)
            {
                var errors = ModelState.Values
                    .SelectMany(v => v.Errors)
                    .Select(e => e.ErrorMessage)
                    .ToList();
                _logger.LogWarning("{error}", errors);
                return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
            }

            Guid tenantId = _userHelper.GetTenantId();
            var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
            var batchId = Guid.NewGuid();

            List<TicketAttachmentVM> ticketAttachmentVMs = new List<TicketAttachmentVM>();

            List<Guid> ticketIds = forumAttachmentDtos.Select(f => f.TicketId.HasValue ? f.TicketId.Value : Guid.Empty).ToList();

            List<TicketForum> tickets = await _context.Tickets.Where(t => ticketIds.Contains(t.Id)).ToListAsync();

            if (tickets == null || tickets.Count > 0)
            {
                _logger.LogWarning("Tickets not Found in database");
                return NotFound(ApiResponse<object>.ErrorResponse("Ticket not Found", "Ticket not Found", 404));
            }

            TicketAttachment attachment = new TicketAttachment();

            foreach (var forumAttachmentDto in forumAttachmentDtos)
            {
                if (string.IsNullOrEmpty(forumAttachmentDto.Base64Data))
                {
                    _logger.LogWarning("Base64 data is missing");
                    return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Base64 data is missing", 400));
                }
                if (forumAttachmentDto.TicketId == null)
                {
                    _logger.LogWarning("ticket ID is missing");
                    return BadRequest(ApiResponse<object>.ErrorResponse("ticket ID is missing", "ticket ID is missing", 400));
                }
                var ticket = tickets.FirstOrDefault(t => t.Id == forumAttachmentDto.TicketId);
                //If base64 has a data URI prefix, strip it
                var base64 = forumAttachmentDto.Base64Data.Contains(",")
                    ? forumAttachmentDto.Base64Data.Substring(forumAttachmentDto.Base64Data.IndexOf(",") + 1)
                    : forumAttachmentDto.Base64Data;

                string fileType = _s3Service.GetContentTypeFromBase64(base64);
                string fileName = _s3Service.GenerateFileName(fileType, tenantId, "forum");

                string objectKey = $"tenant-{tenantId}/project-{ticket?.LinkedProjectId}/froum/{fileName}";
                await _s3Service.UploadFileAsync(base64, fileType, objectKey);

                Document document = forumAttachmentDto.ToDocumentFromForumAttachmentDto(objectKey, objectKey, forumAttachmentDto.SentAt, tenantId, batchId, loggedInEmployee.Id);
                _context.Documents.Add(document);
                await _context.SaveChangesAsync();

                attachment = forumAttachmentDto.ToTicketAttachmentFromForumAttachmentDto(forumAttachmentDto.TicketId != null ? forumAttachmentDto.TicketId.Value : Guid.Empty, document.Id, forumAttachmentDto.CommentId);
                _context.TicketAttachments.Add(attachment);
                await _context.SaveChangesAsync();

                string preSignedUrl = _s3Service.GeneratePreSignedUrl(document.S3Key);

                TicketAttachmentVM attachmentVM = attachment.ToTicketAttachmentVMFromTicketAttachment(preSignedUrl, preSignedUrl);
                ticketAttachmentVMs.Add(attachmentVM);
            }

            if (attachment.CommentId == null)
            {
                _logger.LogInfo("Attachments were added to ticket {TicketId}", attachment.TicketId);
            }
            else
            {
                _logger.LogInfo("Attachments were added to comment {CommentId}", attachment.CommentId);
            }
            return Ok(ApiResponse<object>.SuccessResponse(ticketAttachmentVMs, "attechments added Successfully", 200));
        }

        [HttpPatch("ticket/status/{id}")]
        public async Task<IActionResult> UpdateTicketStatus(Guid id, [FromQuery] Guid statusId)
        {
            Guid tenantId = _userHelper.GetTenantId();
            TicketForum ticket = await _context.Tickets.FirstOrDefaultAsync(t => t.Id == id && t.TenantId == tenantId) ?? new TicketForum();
            ticket.StatusId = statusId;
            await _context.SaveChangesAsync();

            ticket = await _context.Tickets.Include(t => t.TicketTypeMaster).Include(t => t.TicketStatusMaster).Include(t => t.Priority).FirstOrDefaultAsync(t => t.Id == id && t.TenantId == tenantId) ?? new TicketForum();
            List<TicketComment> comments = await _context.TicketComments.Where(c => c.TicketId == ticket.Id).ToListAsync();
            var authorIds = comments.Select(c => c.AuthorId.ToString()).ToList();

            List<Employee> employees = await _context.Employees.Where(e => e.Id == ticket.CreatedById || authorIds.Contains(e.ApplicationUserId ?? "")).ToListAsync();
            Employee employee = employees.Find(e => e.Id == ticket.CreatedById) ?? new Employee();

            ForumTicketVM ticketVM = ticket.ToForumTicketVMFromTicketForum(employee);

            var ticketTags = await _context.TicketTags.Where(t => t.TicketId == ticket.Id).ToListAsync();
            List<Guid> tagIds = ticketTags.Select(t => t.TagId).ToList();

            List<TicketTagMaster>? tagMasters = await _context.TicketTagMasters.Where(t => tagIds.Contains(t.Id)).ToListAsync();
            List<TicketTagVM> tagVMs = new List<TicketTagVM>();
            foreach (var ticketTag in tagMasters)
            {
                TicketTagVM tagVM = ticketTag.ToTicketTagVMFromTicketTagMaster();
                tagVMs.Add(tagVM);
            }
            ticketVM.Tags = tagVMs;

            List<TicketCommentVM> commentVMs = new List<TicketCommentVM>();
            foreach (var comment in comments)
            {
                employee = employees.Find(e => e.ApplicationUserId == comment.AuthorId.ToString()) ?? new Employee();
                commentVMs.Add(comment.ToTicketCommentVMFromTicketComment(employee));
            }

            List<TicketAttachment> attachments = await _context.TicketAttachments.Where(a => a.TicketId == id).ToListAsync();
            List<Guid> fileIds = attachments.Select(a => a.FileId).ToList();

            List<Document> documents = await _context.Documents.Where(d => fileIds.Contains(d.Id)).ToListAsync();

            List<TicketAttachmentVM> ticketAttachmentVMs = new List<TicketAttachmentVM>();
            foreach (var attachment in attachments)
            {
                var document = documents.Find(d => d.Id == attachment.FileId);
                string preSignedUrl = string.Empty;
                if (document != null)
                {
                    preSignedUrl = _s3Service.GeneratePreSignedUrl(document.S3Key);
                }
                if (attachment.CommentId == null)
                {
                    ticketAttachmentVMs.Add(attachment.ToTicketAttachmentVMFromTicketAttachment(preSignedUrl, preSignedUrl));
                }
                else
                {
                    var commentVM = commentVMs.Find(c => c.Id == attachment.CommentId);
                    if (commentVM != null && commentVM.Attachments != null)
                    {
                        commentVM.Attachments.Add(attachment.ToTicketAttachmentVMFromTicketAttachment(preSignedUrl, preSignedUrl));
                    }
                }
            }
            ticketVM.Comments = commentVMs;
            ticketVM.Attachments = ticketAttachmentVMs;
            Project project = await _context.Projects.FirstOrDefaultAsync(p => p.Id == ticket.LinkedProjectId) ?? new Project();
            ticketVM.ProjectName = project.Name;
            if (ticket.LinkedActivityId != null && ticket.LinkedActivityId != null)
            {
                WorkItem workItem = await _context.WorkItems.Include(w => w.ActivityMaster).FirstOrDefaultAsync(w => w.Id == ticket.LinkedActivityId) ?? new WorkItem();
                if (workItem.ActivityMaster != null)
                {
                    ticketVM.ActivityName = workItem.ActivityMaster.ActivityName;
                }
                else
                {
                    ticketVM.ActivityName = "";
                }
            }
            _logger.LogInfo("Status of Ticket {TicketId} in project {ProjectId} is changes to {status}", id, ticket.LinkedProjectId ?? Guid.Empty, ticket.TicketStatusMaster != null ? ticket.TicketStatusMaster.Name : string.Empty);
            return Ok(ApiResponse<object>.SuccessResponse(ticketVM, "Ticket Fetched Successfully", 200));
        }
        [HttpGet("ticket/{id}")]
        public async Task<IActionResult> GetTicketDetail(Guid id)
        {
            Guid tenantId = _userHelper.GetTenantId();
            TicketForum ticket = await _context.Tickets.Include(t => t.TicketTypeMaster).Include(t => t.TicketStatusMaster).Include(t => t.Priority).FirstOrDefaultAsync(t => t.Id == id && t.TenantId == tenantId) ?? new TicketForum();
            List<TicketComment> comments = await _context.TicketComments.Where(c => c.TicketId == ticket.Id).ToListAsync();
            var authorIds = comments.Select(c => c.AuthorId.ToString()).ToList();

            List<Employee> employees = await _context.Employees.Where(e => e.Id == ticket.CreatedById || authorIds.Contains(e.ApplicationUserId ?? "")).ToListAsync();
            Employee employee = employees.Find(e => e.Id == ticket.CreatedById) ?? new Employee();

            ForumTicketVM ticketVM = ticket.ToForumTicketVMFromTicketForum(employee);

            var ticketTags = await _context.TicketTags.Where(t => t.TicketId == ticket.Id).ToListAsync();
            List<Guid> tagIds = ticketTags.Select(t => t.TagId).ToList();

            List<TicketTagMaster>? tagMasters = await _context.TicketTagMasters.Where(t => tagIds.Contains(t.Id)).ToListAsync();
            List<TicketTagVM> tagVMs = new List<TicketTagVM>();
            foreach (var ticketTag in tagMasters)
            {
                TicketTagVM tagVM = ticketTag.ToTicketTagVMFromTicketTagMaster();
                tagVMs.Add(tagVM);
            }
            ticketVM.Tags = tagVMs;

            List<TicketCommentVM> commentVMs = new List<TicketCommentVM>();
            foreach (var comment in comments)
            {
                employee = employees.Find(e => e.ApplicationUserId == comment.AuthorId.ToString()) ?? new Employee();
                commentVMs.Add(comment.ToTicketCommentVMFromTicketComment(employee));
            }

            List<TicketAttachment> attachments = await _context.TicketAttachments.Where(a => a.TicketId == id).ToListAsync();
            List<Guid> fileIds = attachments.Select(a => a.FileId).ToList();

            List<Document> documents = await _context.Documents.Where(d => fileIds.Contains(d.Id)).ToListAsync();

            List<TicketAttachmentVM> ticketAttachmentVMs = new List<TicketAttachmentVM>();
            foreach (var attachment in attachments)
            {
                var document = documents.Find(d => d.Id == attachment.FileId);
                string preSignedUrl = string.Empty;
                if (document != null)
                {
                    preSignedUrl = _s3Service.GeneratePreSignedUrl(document.S3Key);
                }
                if (attachment.CommentId == null)
                {
                    ticketAttachmentVMs.Add(attachment.ToTicketAttachmentVMFromTicketAttachment(preSignedUrl, preSignedUrl));
                }
                else
                {
                    var commentVM = commentVMs.Find(c => c.Id == attachment.CommentId);
                    if (commentVM != null && commentVM.Attachments != null)
                    {
                        commentVM.Attachments.Add(attachment.ToTicketAttachmentVMFromTicketAttachment(preSignedUrl, preSignedUrl));
                    }
                }
            }
            ticketVM.Comments = commentVMs;
            ticketVM.Attachments = ticketAttachmentVMs;
            Project project = await _context.Projects.FirstOrDefaultAsync(p => p.Id == ticket.LinkedProjectId) ?? new Project();
            ticketVM.ProjectName = project.Name;
            if (ticket.LinkedActivityId != null && ticket.LinkedActivityId != null)
            {
                WorkItem workItem = await _context.WorkItems.Include(w => w.ActivityMaster).FirstOrDefaultAsync(w => w.Id == ticket.LinkedActivityId) ?? new WorkItem();
                if (workItem.ActivityMaster != null)
                {
                    ticketVM.ActivityName = workItem.ActivityMaster.ActivityName;
                }
                else
                {
                    ticketVM.ActivityName = "";
                }
            }
            _logger.LogInfo("Fetched Ticket {TicketId} form project {ProjectId}", id, ticket.LinkedProjectId ?? Guid.Empty);
            return Ok(ApiResponse<object>.SuccessResponse(ticketVM, "Ticket Fetched Successfully", 200));
        }

        [HttpGet("tickets")]
        public async Task<IActionResult> ListTickets()
        {
            Guid tenantId = _userHelper.GetTenantId();
            List<TicketForum> tickets = await _context.Tickets.Include(t => t.TicketTypeMaster).Include(t => t.TicketStatusMaster).Include(t => t.Priority).Where(t => t.TenantId == tenantId).ToListAsync();
            var createdByIds = tickets.Select(t => t.CreatedById).ToList();
            var ticketIds = tickets.Select(t => t.Id).ToList();
            var linkedActivityIds = tickets.Select(t => t.LinkedActivityId).ToList();
            var linkedProjectIds = tickets.Select(t => t.LinkedProjectId).ToList();

            List<TicketComment> ticketComments = await _context.TicketComments.Where(c => ticketIds.Contains(c.TicketId)).ToListAsync();
            var authorIds = ticketComments.Select(c => c.AuthorId.ToString()).ToList();

            List<Employee> employees = await _context.Employees.Where(e => createdByIds.Contains(e.Id) || (e.ApplicationUserId != null && authorIds.Contains(e.ApplicationUserId))).ToListAsync();

            var ticketTags = await _context.TicketTags.Where(t => ticketIds.Contains(t.TicketId)).ToListAsync();
            List<Guid> tagIds = ticketTags.Select(t => t.TagId).ToList();

            List<TicketTagMaster>? tagMasters = await _context.TicketTagMasters.Where(t => tagIds.Contains(t.Id)).ToListAsync();

            List<TicketAttachment> ticketAttachments = await _context.TicketAttachments.Where(a => ticketIds.Contains(a.TicketId)).ToListAsync();
            List<Guid> documentIds = ticketAttachments.Select(a => a.FileId).ToList();

            List<Document> documents = await _context.Documents.Where(d => documentIds.Contains(d.Id)).ToListAsync();

            List<Project> projects = await _context.Projects.Where(p => linkedProjectIds.Contains(p.Id)).ToListAsync() ?? new List<Project>();

            List<WorkItem> workItems = await _context.WorkItems.Include(w => w.ActivityMaster).Where(w => linkedActivityIds.Contains(w.Id)).ToListAsync() ?? new List<WorkItem>();

            List<ForumTicketVM> ticketVMs = new List<ForumTicketVM>();
            foreach (var ticket in tickets)
            {
                Employee employee = employees.Find(e => e.Id == ticket.CreatedById) ?? new Employee();
                ForumTicketVM ticketVM = ticket.ToForumTicketVMFromTicketForum(employee);

                var ticketTagMappers = ticketTags.Where(t => t.TicketId == ticket.Id).ToList();
                List<Guid> ticketTagIds = ticketTagMappers.Select(t => t.TagId).ToList();

                List<TicketTagMaster> ticketTagMasters = tagMasters.Where(t => ticketTagIds.Contains(t.Id)).ToList();

                List<TicketTagVM> tagVMs = new List<TicketTagVM>();
                foreach (var ticketTag in ticketTagMasters)
                {
                    TicketTagVM tagVM = ticketTag.ToTicketTagVMFromTicketTagMaster();
                    tagVMs.Add(tagVM);
                }

                List<TicketComment> comments = ticketComments.Where(c => c.TicketId == ticket.Id).ToList();
                List<TicketCommentVM> commentVMs = new List<TicketCommentVM>();
                foreach (var comment in comments)
                {
                    employee = employees.Find(e => e.ApplicationUserId == comment.AuthorId.ToString()) ?? new Employee();
                    commentVMs.Add(comment.ToTicketCommentVMFromTicketComment(employee));
                }

                List<TicketAttachment> attachments = ticketAttachments.Where(a => a.TicketId == ticket.Id).ToList();
                List<Guid> fileIds = attachments.Select(a => a.FileId).ToList();

                List<Document> files = documents.Where(d => fileIds.Contains(d.Id)).ToList();

                List<TicketAttachmentVM> ticketAttachmentVMs = new List<TicketAttachmentVM>();
                foreach (var attachment in attachments)
                {
                    var document = documents.Find(d => d.Id == attachment.FileId);
                    string preSignedUrl = string.Empty;
                    if (document != null)
                    {
                        preSignedUrl = _s3Service.GeneratePreSignedUrl(document.S3Key);
                    }
                    if (attachment.CommentId == null)
                    {
                        ticketAttachmentVMs.Add(attachment.ToTicketAttachmentVMFromTicketAttachment(preSignedUrl, preSignedUrl));
                    }
                    else
                    {
                        var commentVM = commentVMs.Find(c => c.Id == attachment.CommentId);
                        if (commentVM != null && commentVM.Attachments != null)
                        {
                            commentVM.Attachments.Add(attachment.ToTicketAttachmentVMFromTicketAttachment(preSignedUrl, preSignedUrl));
                        }
                    }
                }
                ticketVM.Comments = commentVMs;
                ticketVM.Attachments = ticketAttachmentVMs;
                ticketVM.Tags = tagVMs;
                Project project = projects.Find(p => p.Id == ticket.LinkedProjectId) ?? new Project();
                ticketVM.ProjectName = project.Name;
                if (ticket.LinkedActivityId != null && ticket.LinkedActivityId != null)
                {
                    WorkItem workItem = workItems.Find(w => w.Id == ticket.LinkedActivityId) ?? new WorkItem();
                    if (workItem.ActivityMaster != null)
                    {
                        ticketVM.ActivityName = workItem.ActivityMaster.ActivityName;
                    }
                    else
                    {
                        ticketVM.ActivityName = "";
                    }
                }
                ticketVMs.Add(ticketVM);
            }
            _logger.LogInfo("{count} tickets records fetched successfully from tenant {tenantId}", ticketVMs.Count, tenantId);
            return Ok(ApiResponse<object>.SuccessResponse(ticketVMs, System.String.Format("{0} tickets records fetched successfully", ticketVMs.Count), 200));
        }
    }
}
