using AutoMapper;
using Marco.Pms.DataAccess.Data;
using Marco.Pms.Helpers.Utility;
using Marco.Pms.Model.Directory;
using Marco.Pms.Model.DocumentManager;
using Marco.Pms.Model.Dtos.DocumentManager;
using Marco.Pms.Model.Dtos.Master;
using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Entitlements;
using Marco.Pms.Model.Mapper;
using Marco.Pms.Model.Master;
using Marco.Pms.Model.MongoDBModels.Utility;
using Marco.Pms.Model.Utilities;
using Marco.Pms.Model.ViewModels.DocumentManager;
using Marco.Pms.Model.ViewModels.Master;
using Marco.Pms.Services.Helpers;
using Marco.Pms.Services.Service.ServiceInterfaces;
using MarcoBMS.Services.Service;
using Microsoft.CodeAnalysis;
using Microsoft.EntityFrameworkCore;

namespace Marco.Pms.Services.Service
{
    public class MasterService : IMasterService
    {
        private readonly IDbContextFactory<ApplicationDbContext> _dbContextFactory;
        private readonly ApplicationDbContext _context;
        private readonly ILoggingService _logger;
        private readonly PermissionServices _permission;
        private readonly IMapper _mapper;
        private readonly UtilityMongoDBHelper _updateLogHelper;
        private readonly CacheUpdateHelper _cache;

        public MasterService(
            IDbContextFactory<ApplicationDbContext> dbContextFactory,
            ApplicationDbContext context,
            ILoggingService logger,
            PermissionServices permission,
            IMapper mapper,
            UtilityMongoDBHelper updateLogHelper,
            CacheUpdateHelper cache)
        {
            _dbContextFactory = dbContextFactory;
            _context = context;
            _logger = logger;
            _permission = permission;
            _mapper = mapper;
            _updateLogHelper = updateLogHelper;
            _cache = cache;
        }

        #region =================================================================== Contact Category APIs ===================================================================

        public async Task<ApiResponse<object>> CreateContactCategory(CreateContactCategoryDto contactCategoryDto, Employee loggedInEmployee, Guid tenantId)
        {
            if (contactCategoryDto != null)
            {
                ContactCategoryMaster? existingContactCategory = await _context.ContactCategoryMasters.FirstOrDefaultAsync(c => c.TenantId == tenantId && c.Name.ToLower() == (contactCategoryDto.Name != null ? contactCategoryDto.Name.ToLower() : ""));
                if (existingContactCategory == null)
                {
                    ContactCategoryMaster contactCategory = contactCategoryDto.ToContactCategoryMasterFromCreateContactCategoryDto(tenantId);
                    _context.ContactCategoryMasters.Add(contactCategory);
                    await _context.SaveChangesAsync();
                    ContactCategoryVM categoryVM = contactCategory.ToContactCategoryVMFromContactCategoryMaster();

                    _logger.LogInfo("Employee ID {LoggedInEmployeeId} created a contact category {ContactCategoryId}.", loggedInEmployee.Id, contactCategory.Id);
                    return ApiResponse<object>.SuccessResponse(categoryVM, "Category Created Successfully", 200);
                }
                _logger.LogWarning("Employee ID {LoggedInEmployeeId} attempted to create an existing contact category.", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Category already existed", "Category already existed", 409);
            }
            _logger.LogWarning("Employee with ID {LoggedInEmployeeId} sended empty payload", loggedInEmployee.Id);
            return ApiResponse<object>.ErrorResponse("User Send empty Payload", "User Send empty Payload", 400);
        }
        public async Task<ApiResponse<object>> UpdateContactCategory(Guid id, UpdateContactCategoryDto contactCategoryDto, Employee loggedInEmployee, Guid tenantId)
        {
            if (contactCategoryDto != null && id == contactCategoryDto.Id)
            {
                ContactCategoryMaster? contactCategory = await _context.ContactCategoryMasters.FirstOrDefaultAsync(c => c.TenantId == tenantId && c.Id == id);
                if (contactCategory != null)
                {
                    contactCategory.Name = contactCategoryDto.Name ?? "";
                    contactCategory.Description = contactCategoryDto.Description ?? "";

                    _context.DirectoryUpdateLogs.Add(new DirectoryUpdateLog
                    {
                        RefereanceId = contactCategory.Id,
                        UpdatedById = loggedInEmployee.Id,
                        UpdateAt = DateTime.UtcNow
                    });

                    await _context.SaveChangesAsync();
                    ContactCategoryVM categoryVM = contactCategory.ToContactCategoryVMFromContactCategoryMaster();

                    _logger.LogInfo("Employee ID {LoggedInEmployeeId} created a contact category {ContactCategoryId}.", loggedInEmployee.Id, contactCategory.Id);
                    return ApiResponse<object>.SuccessResponse(categoryVM, "Category Created Successfully", 200);
                }
                _logger.LogWarning("Employee ID {LoggedInEmployeeId} attempted to update a contact category but not found in database.", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Category not found", "Category not found", 404);
            }
            _logger.LogWarning("Employee with ID {LoggedInEmployeeId} sended empty payload", loggedInEmployee.Id);
            return ApiResponse<object>.ErrorResponse("User Send empty Payload", "User Send empty Payload", 400);
        }
        public async Task<ApiResponse<object>> GetContactCategoriesList(Employee loggedInEmployee, Guid tenantId)
        {
            var categoryList = await _context.ContactCategoryMasters.Where(c => c.TenantId == tenantId).ToListAsync();
            List<ContactCategoryVM> contactCategories = new List<ContactCategoryVM>();
            foreach (var category in categoryList)
            {
                ContactCategoryVM categoryVM = category.ToContactCategoryVMFromContactCategoryMaster();
                contactCategories.Add(categoryVM);
            }
            _logger.LogInfo("{count} contact categoires are fetched by Employee with ID {LoggedInEmployeeId}", contactCategories.Count, loggedInEmployee.Id);
            return ApiResponse<object>.SuccessResponse(contactCategories, System.String.Format("{0} contact categories fetched successfully", contactCategories.Count), 200);
        }
        public async Task<ApiResponse<object>> GetContactCategoryById(Guid id, Employee loggedInEmployee, Guid tenantId)
        {
            var category = await _context.ContactCategoryMasters.FirstOrDefaultAsync(c => c.Id == id && c.TenantId == tenantId);
            if (category != null)
            {
                ContactCategoryVM categoryVM = category.ToContactCategoryVMFromContactCategoryMaster();
                _logger.LogInfo("Employee {EmployeeId} fetched contact category {ContactCategoryID}", loggedInEmployee.Id, category.Id);
                return ApiResponse<object>.SuccessResponse(categoryVM, "Category fetched successfully", 200);
            }

            _logger.LogWarning("Employee {EmployeeId} attempted to fetch contact category {ContactCategoryID} but not found in database", loggedInEmployee.Id, id);
            return ApiResponse<object>.ErrorResponse("Category not found", "Category not found", 404);
        }
        public async Task<ApiResponse<object>> DeleteContactCategory(Guid id, Employee loggedInEmployee, Guid tenantId)
        {
            ContactCategoryMaster? contactCategory = await _context.ContactCategoryMasters.FirstOrDefaultAsync(c => c.Id == id && c.TenantId == tenantId);
            if (contactCategory != null)
            {
                List<Contact>? existingContacts = await _context.Contacts.AsNoTracking().Where(c => c.ContactCategoryId == contactCategory.Id).ToListAsync();
                if (existingContacts.Count > 0)
                {
                    List<Contact>? contacts = new List<Contact>();
                    foreach (var contact in existingContacts)
                    {
                        contact.ContactCategoryId = null;
                        contacts.Add(contact);
                    }
                    _context.Contacts.UpdateRange(contacts);
                }
                _context.ContactCategoryMasters.Remove(contactCategory);

                _context.DirectoryUpdateLogs.Add(new DirectoryUpdateLog
                {
                    RefereanceId = id,
                    UpdatedById = loggedInEmployee.Id,
                    UpdateAt = DateTime.UtcNow
                });
                await _context.SaveChangesAsync();
                _logger.LogInfo("Employee {EmployeeId} deleted contact category {ContactCategoryId}", loggedInEmployee.Id, id);
            }

            _logger.LogWarning("Employee {EmployeeId} tries to delete Category {CategoryId} but not found in database", loggedInEmployee.Id, id);
            return ApiResponse<object>.SuccessResponse(new { }, "Category deleted successfully", 200);
        }
        #endregion

        #region =================================================================== Contact Tag APIs ===================================================================

        public async Task<ApiResponse<Object>> GetContactTags(Employee loggedInEmployee, Guid tenantId)
        {
            var taglist = await _context.ContactTagMasters.Where(t => t.TenantId == tenantId).ToListAsync();
            List<ContactTagVM> contactTags = new List<ContactTagVM>();
            foreach (var tag in taglist)
            {
                ContactTagVM tagVm = tag.ToContactTagVMFromContactTagMaster();
                contactTags.Add(tagVm);
            }
            _logger.LogInfo("{count} contact Tags are fetched by Employee with ID {LoggedInEmployeeId}", contactTags.Count, loggedInEmployee.Id);
            return ApiResponse<object>.SuccessResponse(contactTags, System.String.Format("{0} contact tags fetched successfully", contactTags.Count), 200);
        }
        public async Task<ApiResponse<object>> CreateContactTag(CreateContactTagDto contactTagDto, Employee loggedInEmployee, Guid tenantId)
        {
            if (contactTagDto != null)
            {
                ContactTagMaster? existingContactTag = await _context.ContactTagMasters.FirstOrDefaultAsync(c => c.TenantId == tenantId && c.Name.ToLower() == (contactTagDto.Name != null ? contactTagDto.Name.ToLower() : ""));
                if (existingContactTag == null)
                {
                    ContactTagMaster contactTag = contactTagDto.ToContactTagMasterFromCreateContactTagDto(tenantId);
                    _context.ContactTagMasters.Add(contactTag);
                    await _context.SaveChangesAsync();
                    ContactTagVM tagVM = contactTag.ToContactTagVMFromContactTagMaster();

                    _logger.LogInfo("Employee ID {LoggedInEmployeeId} created a contact tag {ContactTagId}.", loggedInEmployee.Id, contactTag.Id);
                    return ApiResponse<object>.SuccessResponse(tagVM, "Tag Created Successfully", 200);
                }
                _logger.LogWarning("Employee ID {LoggedInEmployeeId} attempted to create an existing contact tag.", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Tag already existed", "Tag already existed", 409);
            }
            _logger.LogWarning("Employee with ID {LoggedInEmployeeId} sended empty payload", loggedInEmployee.Id);
            return ApiResponse<object>.ErrorResponse("User Send empty Payload", "User Send empty Payload", 400);
        }
        public async Task<ApiResponse<object>> UpdateContactTag(Guid id, UpdateContactTagDto contactTagDto, Employee loggedInEmployee, Guid tenantId)
        {
            if (contactTagDto != null && contactTagDto.Id == id)
            {
                ContactTagMaster? contactTag = await _context.ContactTagMasters.AsNoTracking().FirstOrDefaultAsync(t => t.TenantId == tenantId && t.Id == contactTagDto.Id);
                if (contactTag != null)
                {
                    contactTag = contactTagDto.ToContactTagMasterFromUpdateContactTagDto(tenantId);
                    _context.ContactTagMasters.Update(contactTag);

                    _context.DirectoryUpdateLogs.Add(new DirectoryUpdateLog
                    {
                        RefereanceId = contactTag.Id,
                        UpdatedById = loggedInEmployee.Id,
                        UpdateAt = DateTime.UtcNow
                    });
                    await _context.SaveChangesAsync();
                    await _context.SaveChangesAsync();

                    ContactTagVM contactTagVm = contactTag.ToContactTagVMFromContactTagMaster();



                    _logger.LogInfo("Contact tag master {ConatctTagId} updated successfully by employee {EmployeeId}", contactTagVm.Id, loggedInEmployee.Id);
                    return ApiResponse<object>.SuccessResponse(contactTagVm, "Contact Tag master updated successfully", 200);
                }
                _logger.LogWarning("Contact Tag master {ContactTagId} not found in database", id);
                return ApiResponse<object>.ErrorResponse("Contact Tag master not found", "Contact tag master not found", 404);
            }
            _logger.LogWarning("Employee with ID {LoggedInEmployeeId} sended empty payload", loggedInEmployee.Id);
            return ApiResponse<object>.ErrorResponse("User Send empty Payload", "User Send empty Payload", 400);
        }
        public async Task<ApiResponse<object>> DeleteContactTag(Guid id, Employee loggedInEmployee, Guid tenantId)
        {
            ContactTagMaster? contactTag = await _context.ContactTagMasters.FirstOrDefaultAsync(c => c.Id == id && c.TenantId == tenantId);
            if (contactTag != null)
            {
                List<ContactTagMapping>? tagMappings = await _context.ContactTagMappings.Where(t => t.ContactTagId == contactTag.Id).ToListAsync();

                _context.ContactTagMasters.Remove(contactTag);
                if (tagMappings.Any())
                {
                    _context.ContactTagMappings.RemoveRange(tagMappings);
                }
                _context.DirectoryUpdateLogs.Add(new DirectoryUpdateLog
                {
                    RefereanceId = id,
                    UpdatedById = loggedInEmployee.Id,
                    UpdateAt = DateTime.UtcNow
                });
                await _context.SaveChangesAsync();
                _logger.LogInfo("Employee {EmployeeId} deleted contact tag {ContactTagId}", loggedInEmployee.Id, id);
            }

            _logger.LogWarning("Employee {EmployeeId} tries to delete Tag {ContactTagId} but not found in database", loggedInEmployee.Id, id);
            return ApiResponse<object>.SuccessResponse(new { }, "Tag deleted successfully", 200);
        }

        #endregion

        #region =================================================================== Work Status APIs ===================================================================

        public async Task<ApiResponse<object>> GetWorkStatusList(Employee loggedInEmployee, Guid tenantId)
        {
            _logger.LogInfo("GetWorkStatusList called.");

            try
            {
                // Step 1: Check permission to view master data
                bool hasViewPermission = await _permission.HasPermission(PermissionsMaster.ViewMasters, loggedInEmployee.Id);
                if (!hasViewPermission)
                {
                    _logger.LogWarning("Access denied for employeeId: {EmployeeId}", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("You don't have access", "Don't have access to take action", 403);
                }

                // Step 2: Fetch work statuses for the tenant
                var workStatusList = await _context.WorkStatusMasters
                    .Where(ws => ws.TenantId == tenantId)
                    .Select(ws => new
                    {
                        ws.Id,
                        ws.Name,
                        ws.Description,
                        ws.IsSystem
                    })
                    .ToListAsync();

                _logger.LogInfo("{Count} work statuses fetched for tenantId: {TenantId}", workStatusList.Count, tenantId);

                // Step 3: Return successful response
                return ApiResponse<object>.SuccessResponse(
                    workStatusList,
                    $"{workStatusList.Count} work status records fetched successfully",
                    200
                );
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error occurred while fetching work status list");
                return ApiResponse<object>.ErrorResponse("An error occurred", "Unable to fetch work status list", 500);
            }
        }
        public async Task<ApiResponse<object>> CreateWorkStatus(CreateWorkStatusMasterDto createWorkStatusDto, Employee loggedInEmployee, Guid tenantId)
        {
            _logger.LogInfo("CreateWorkStatus called with Name: {Name}", createWorkStatusDto.Name ?? "");

            try
            {

                // Step 1: Check if user has permission to manage master data
                var hasManageMasterPermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);
                if (!hasManageMasterPermission)
                {
                    _logger.LogWarning("Access denied for employeeId: {EmployeeId}", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("You don't have access", "Don't have access to take action", 403);
                }

                // Step 2: Check if work status with the same name already exists
                var existingWorkStatus = await _context.WorkStatusMasters
                    .FirstOrDefaultAsync(ws => ws.Name == createWorkStatusDto.Name && ws.TenantId == tenantId);

                if (existingWorkStatus != null)
                {
                    _logger.LogWarning("Work status already exists: {Name}", createWorkStatusDto.Name ?? "");
                    return ApiResponse<object>.ErrorResponse("Work status already exists", "Work status already exists", 400);
                }

                // Step 3: Create new WorkStatusMaster entry
                var workStatus = new WorkStatusMaster
                {
                    Name = createWorkStatusDto.Name?.Trim() ?? "",
                    Description = createWorkStatusDto.Description?.Trim() ?? "",
                    IsSystem = false,
                    TenantId = tenantId
                };

                _context.WorkStatusMasters.Add(workStatus);
                await _context.SaveChangesAsync();

                _logger.LogInfo("Work status created successfully: {Id}, Name: {Name}", workStatus.Id, workStatus.Name);
                return ApiResponse<object>.SuccessResponse(workStatus, "Work status created successfully", 200);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error occurred while creating work status");
                return ApiResponse<object>.ErrorResponse("An error occurred", "Unable to create work status", 500);
            }
        }
        public async Task<ApiResponse<object>> UpdateWorkStatus(Guid id, UpdateWorkStatusMasterDto updateWorkStatusDto, Employee loggedInEmployee, Guid tenantId)
        {
            _logger.LogInfo("UpdateWorkStatus called for WorkStatus ID: {Id}, New Name: {Name}", id, updateWorkStatusDto.Name ?? "");

            try
            {
                // Step 1: Validate input
                if (id == Guid.Empty || id != updateWorkStatusDto.Id)
                {
                    _logger.LogWarning("Invalid ID provided for update. Route ID: {RouteId}, DTO ID: {DtoId}", id, updateWorkStatusDto.Id);
                    return ApiResponse<object>.ErrorResponse("Invalid data provided", "The provided work status ID is invalid", 400);
                }

                // Step 2: Check permissions
                var hasManageMasterPermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);
                if (!hasManageMasterPermission)
                {
                    _logger.LogWarning("Access denied. EmployeeId: {EmployeeId} does not have Manage Master permission.", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access denied", "You do not have permission to update this work status", 403);
                }

                // Step 3: Retrieve the work status record
                var workStatus = await _context.WorkStatusMasters
                    .FirstOrDefaultAsync(ws => ws.Id == id && ws.TenantId == tenantId);

                if (workStatus == null)
                {
                    _logger.LogWarning("Work status not found for ID: {Id}", id);
                    return ApiResponse<object>.ErrorResponse("Work status not found", "No work status found with the provided ID", 404);
                }

                // Step 4: Check for duplicate name (optional)
                var isDuplicate = await _context.WorkStatusMasters
                    .AnyAsync(ws => ws.Name == updateWorkStatusDto.Name && ws.Id != id && ws.TenantId == tenantId);

                if (isDuplicate)
                {
                    _logger.LogWarning("Duplicate work status name '{Name}' detected during update. ID: {Id}", updateWorkStatusDto.Name ?? "", id);
                    return ApiResponse<object>.ErrorResponse("Work status with the same name already exists", "Duplicate name", 400);
                }

                // Step 5: Update fields
                workStatus.Name = updateWorkStatusDto.Name?.Trim() ?? "";
                workStatus.Description = updateWorkStatusDto.Description?.Trim() ?? "";

                await _context.SaveChangesAsync();

                _logger.LogInfo("Work status updated successfully. ID: {Id}", id);
                return ApiResponse<object>.SuccessResponse(workStatus, "Work status updated successfully", 200);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error occurred while updating work status ID: {Id}", id);
                return ApiResponse<object>.ErrorResponse("An error occurred", "Unable to update the work status at this time", 500);
            }
        }
        public async Task<ApiResponse<object>> DeleteWorkStatus(Guid id, Employee loggedInEmployee, Guid tenantId)
        {
            _logger.LogInfo("DeleteWorkStatus called for Id: {Id}", id);

            try
            {
                // Step 2: Check permission to manage master data
                var hasManageMasterPermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);
                if (!hasManageMasterPermission)
                {
                    _logger.LogWarning("Delete denied. EmployeeId: {EmployeeId} lacks Manage_Master permission.", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("You don't have access", "Access denied for deleting work status", 403);
                }

                // Step 3: Find the work status
                var workStatus = await _context.WorkStatusMasters
                    .FirstOrDefaultAsync(ws => ws.Id == id && ws.TenantId == tenantId);

                if (workStatus == null)
                {
                    _logger.LogWarning("Work status not found for Id: {Id}", id);
                    return ApiResponse<object>.ErrorResponse("Work status not found", "Work status not found", 404);
                }

                // Step 4: Check for dependencies in TaskAllocations
                bool hasDependency = await _context.TaskAllocations
                    .AnyAsync(ta => ta.TenantId == tenantId && ta.WorkStatusId == id);

                if (hasDependency)
                {
                    _logger.LogWarning("Cannot delete WorkStatus Id: {Id} due to existing task dependency", id);
                    return ApiResponse<object>.ErrorResponse(
                        "Work status has a dependency in assigned tasks and cannot be deleted",
                        "Deletion failed due to associated tasks",
                        400
                    );
                }

                // Step 5: Delete and persist
                _context.WorkStatusMasters.Remove(workStatus);
                await _context.SaveChangesAsync();

                _logger.LogInfo("Work status deleted successfully. Id: {Id}", id);
                return ApiResponse<object>.SuccessResponse(new { }, "Work status deleted successfully", 200);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error occurred while deleting WorkStatus Id: {Id}", id);
                return ApiResponse<object>.ErrorResponse("An error occurred", "Unable to delete work status", 500);
            }
        }

        #endregion

        #region =================================================================== Expenses Type APIs ===================================================================

        public async Task<ApiResponse<object>> GetExpenseTypeListAsync(Employee loggedInEmployee, Guid tenantId, bool isActive)
        {
            try
            {
                // Validation if employee is taking action in same tenant
                if (tenantId != loggedInEmployee.TenantId)
                {
                    _logger.LogWarning("Employee {EmployeeId} attempted to fetch the list of expense type from different tenant", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access Denied", "User do not have access for this information", 403);
                }

                // Featching the list of Expenses Type.
                var typeList = await _context.ExpensesTypeMaster.Where(et => et.TenantId == tenantId && et.IsActive == isActive).ToListAsync();
                var response = _mapper.Map<List<ExpensesTypeMasterVM>>(typeList);

                _logger.LogInfo("{Count} records of expense type have been fetched successfully by employee {EmployeeId}", response.Count, loggedInEmployee.Id);
                return ApiResponse<object>.SuccessResponse(response, $"{response.Count} records of expense type have been fetched successfully.", 200);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error occured while fetching list of expense type list by employee {EmployeeId}", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(ex), 500);
            }

        }
        public async Task<ApiResponse<object>> CreateExpenseTypeAsync(ExpensesTypeMasterDto model, Employee loggedInEmployee, Guid tenantId)
        {
            try
            {
                // Validation if employee is taking action in same tenant
                if (tenantId != loggedInEmployee.TenantId)
                {
                    _logger.LogWarning("Employee {EmployeeId} attempted to add new expense type in different tenant", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access Denied", "User do not have access for this information", 403);
                }
                var hasManagePermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);
                if (!hasManagePermission)
                {
                    _logger.LogWarning("Access DENIED for employee {EmployeeId} for managing EXPANSES TYPE MASTER.", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to Manage masters", 403);
                }
                var expensesType = _mapper.Map<ExpensesTypeMaster>(model);
                expensesType.TenantId = tenantId;

                _context.ExpensesTypeMaster.Add(expensesType);
                await _context.SaveChangesAsync();

                _logger.LogInfo("New Expense Type {ExpensesTypeId} was added by employee {EmployeeId}", expensesType.Id, loggedInEmployee.Id);

                var response = _mapper.Map<ExpensesTypeMasterVM>(expensesType);
                return ApiResponse<object>.SuccessResponse(response, "Expense type craeted Successfully", 201);
            }
            catch (DbUpdateException dbEx)
            {
                _logger.LogError(dbEx, "Database Exception occured while adding new expense type by employee {EmployeeId}", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(dbEx), 500);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Exception occured while adding new expense type by employee {EmployeeId}", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(ex), 500);
            }
        }
        public async Task<ApiResponse<object>> UpdateExpenseTypeAsync(Guid id, ExpensesTypeMasterDto model, Employee loggedInEmployee, Guid tenantId)
        {
            try
            {
                // Validation if employee is taking action in same tenant
                if (tenantId != loggedInEmployee.TenantId)
                {
                    _logger.LogWarning("Employee {EmployeeId} attempted to update expense type in different tenant", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access Denied", "User do not have access for this information", 403);
                }

                // Checking permssion for managing masters
                var hasManagePermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);
                if (!hasManagePermission)
                {
                    _logger.LogWarning("Access DENIED for employee {EmployeeId} for managing EXPANSES TYPE MASTER.", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to Manage masters", 403);
                }

                // Validating the prvided data
                if (model.Id != id)
                {
                    _logger.LogWarning("Employee {EmployeeId} provide different Ids in payload and path variable", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Invalid Data", "User has send invalid payload", 400);
                }

                var expensesType = await _context.ExpensesTypeMaster.AsNoTracking().FirstOrDefaultAsync(et => et.Id == model.Id.Value && et.TenantId == tenantId);

                // Checking if expense type exists
                if (expensesType == null)
                {
                    _logger.LogWarning("Employee {EmployeeId} tries to update expense type, but not found in database", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Expense Type not found", "Expense Type not found", 404);
                }

                // Mapping ExpensesTypeMaster to BsonDocument
                var existingEntityBson = _updateLogHelper.EntityToBsonDocument(expensesType);

                // Mapping ExpensesTypeMasterDto to ExpensesTypeMaster
                _mapper.Map(model, expensesType);

                _context.ExpensesTypeMaster.Update(expensesType);
                await _context.SaveChangesAsync();

                _logger.LogInfo("Expense Type {ExpensesTypeId} was updated by employee {EmployeeId}", expensesType.Id, loggedInEmployee.Id);

                // Saving the old entity in mongoDB

                var mongoDBTask = _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject
                {
                    EntityId = expensesType.Id.ToString(),
                    UpdatedById = loggedInEmployee.Id.ToString(),
                    OldObject = existingEntityBson,
                    UpdatedAt = DateTime.UtcNow
                }, "ExpensesTypeMasterModificationLog");

                // Mapping ExpensesTypeMaster to ExpensesTypeMasterVM
                var response = _mapper.Map<ExpensesTypeMasterVM>(expensesType);
                return ApiResponse<object>.SuccessResponse(response, "Expense type updated Successfully", 200);
            }
            catch (DbUpdateException dbEx)
            {
                _logger.LogError(dbEx, "Database Exception occured while updating expense type by employee {EmployeeId}", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(dbEx), 500);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Exception occured while updating expense type by employee {EmployeeId}", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(ex), 500);
            }
        }
        public async Task<ApiResponse<object>> DeleteExpenseTypeAsync(Guid id, bool isActive, Employee loggedInEmployee, Guid tenantId)
        {
            string action = isActive ? "restore" : "delete";
            try
            {
                // Validation if employee is taking action in same tenant
                if (tenantId != loggedInEmployee.TenantId)
                {
                    _logger.LogWarning("Employee {EmployeeId} attempted to {Action} expense type in different tenant", loggedInEmployee.Id, action);
                    return ApiResponse<object>.ErrorResponse("Access Denied", "User do not have access for this information", 403);
                }

                // Checking permssion for managing masters
                var hasManagePermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);
                if (!hasManagePermission)
                {
                    _logger.LogWarning("Access DENIED for employee {EmployeeId} for managing EXPANSES TYPE MASTER.", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to Manage masters", 403);
                }

                var expensesType = await _context.ExpensesTypeMaster.FirstOrDefaultAsync(et => et.Id == id && et.TenantId == tenantId);

                // Checking if expense type exists
                if (expensesType == null)
                {
                    _logger.LogWarning("Employee {EmployeeId} tries to {Action} expense type, but not found in database", loggedInEmployee.Id, action);
                    return ApiResponse<object>.ErrorResponse("Expense Type not found", "Expense Type not found", 404);
                }

                // Mapping ExpensesTypeMaster to BsonDocument
                var existingEntityBson = _updateLogHelper.EntityToBsonDocument(expensesType);

                expensesType.IsActive = isActive;
                await _context.SaveChangesAsync();

                _logger.LogInfo("Expense Type {ExpensesTypeId} was {Action}d by employee {EmployeeId}", expensesType.Id, action, loggedInEmployee.Id);

                // Saving the old entity in mongoDB

                var mongoDBTask = _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject
                {
                    EntityId = expensesType.Id.ToString(),
                    UpdatedById = loggedInEmployee.Id.ToString(),
                    OldObject = existingEntityBson,
                    UpdatedAt = DateTime.UtcNow
                }, "ExpensesTypeMasterModificationLog");

                // Mapping ExpensesTypeMaster to ExpensesTypeMasterVM
                var response = _mapper.Map<ExpensesTypeMasterVM>(expensesType);
                return ApiResponse<object>.SuccessResponse(response, $"Expense type {action}d Successfully", 200);
            }
            catch (DbUpdateException dbEx)
            {
                _logger.LogError(dbEx, "Database Exception occured while {Action}ing expense type by employee {EmployeeId}", action, loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(dbEx), 500);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Exception occured while {Action}ing expense type by employee {EmployeeId}", action, loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(ex), 500);
            }
        }

        #endregion

        #region =================================================================== Expenses Status APIs ===================================================================
        public async Task<ApiResponse<object>> GetExpensesStatusListAsync(Employee loggedInEmployee, Guid tenantId)
        {
            try
            {
                // Validation if employee is taking action in same tenant
                if (tenantId != loggedInEmployee.TenantId)
                {
                    _logger.LogWarning("Employee {EmployeeId} attempted to fetch the list of expense status from different tenant", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access Denied", "User do not have access for this information", 403);
                }

                // Featching the list of Expenses Status.
                var statusList = await _context.ExpensesStatusMaster.ToListAsync();
                var response = _mapper.Map<List<ExpensesStatusMasterVM>>(statusList);

                var statusIds = statusList.Select(s => s.Id).ToList();
                var permissionStatusMapping = await _context.StatusPermissionMapping
                    .Where(ps => statusIds.Contains(ps.StatusId))
                    .GroupBy(ps => ps.StatusId)
                    .Select(g => new
                    {
                        StatusId = g.Key,
                        PermissionIds = g.Select(ps => ps.PermissionId).ToList()
                    }).ToListAsync();

                foreach (var status in response)
                {
                    status.PermissionIds = permissionStatusMapping.Where(ps => ps.StatusId == status.Id).Select(ps => ps.PermissionIds).FirstOrDefault();
                }

                _logger.LogInfo("{Count} records of expense status have been fetched successfully by employee {EmployeeId}", response.Count, loggedInEmployee.Id);
                return ApiResponse<object>.SuccessResponse(response, $"{response.Count} records of expense status have been fetched successfully.", 200);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error occured while fetching list of expense sattus list by employee {EmployeeId}", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(ex), 500);
            }
        }
        #endregion

        #region =================================================================== Payment mode APIs ===================================================================
        public async Task<ApiResponse<object>> GetPaymentModeListAsync(Employee loggedInEmployee, Guid tenantId, bool isActive)
        {
            try
            {
                // Validation if employee is taking action in same tenant
                if (tenantId != loggedInEmployee.TenantId)
                {
                    _logger.LogWarning("Employee {EmployeeId} attempted to fetch the list of payment modes from different tenant", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access Denied", "User do not have access for this information", 403);
                }

                // Featching the list of Payment Modes.
                var paymentModes = await _context.PaymentModeMatser.Where(pm => pm.TenantId == tenantId && pm.IsActive == isActive).ToListAsync();
                var response = _mapper.Map<List<PaymentModeMatserVM>>(paymentModes);

                _logger.LogInfo("{Count} records of payment modes have been fetched successfully by employee {EmployeeId}", response.Count, loggedInEmployee.Id);
                return ApiResponse<object>.SuccessResponse(response, $"{response.Count} records of payment modes have been fetched successfully.", 200);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error occured while featching list of payment modes list by employee {EmployeeId}", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Internal Error occured while featching list of payment modes list", ExceptionMapper(ex), 500);
            }
        }
        public async Task<ApiResponse<object>> CreatePaymentModeAsync(PaymentModeMatserDto model, Employee loggedInEmployee, Guid tenantId)
        {
            try
            {
                // Validation if employee is taking action in same tenant
                if (tenantId != loggedInEmployee.TenantId)
                {
                    _logger.LogWarning("Employee {EmployeeId} attempted to add new payment mode in different tenant", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access Denied", "User do not have access for this information", 403);
                }
                var hasManagePermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);
                if (!hasManagePermission)
                {
                    _logger.LogWarning("Access DENIED for employee {EmployeeId} for managing PAYMENT MODE MASTER.", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to Manage masters", 403);

                }
                // Mapping the DTO to PaymentModeMatser Model
                var paymentMode = _mapper.Map<PaymentModeMatser>(model);
                paymentMode.TenantId = tenantId;

                _context.PaymentModeMatser.Add(paymentMode);
                await _context.SaveChangesAsync();

                _logger.LogInfo("New Payment Mode {PaymentModeId} was added by employee {EmployeeId}", paymentMode.Id, loggedInEmployee.Id);

                // Mapping the PaymentModeMatser Model to View Model
                var response = _mapper.Map<PaymentModeMatserVM>(paymentMode);
                return ApiResponse<object>.SuccessResponse(response, "Payment Mode craeted Successfully", 201);
            }
            catch (DbUpdateException dbEx)
            {
                _logger.LogError(dbEx, "Database Exception occured while adding new payment mode by employee {EmployeeId}", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(dbEx), 500);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Exception occured while adding new payment mode by employee {EmployeeId}", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(ex), 500);
            }
        }
        public async Task<ApiResponse<object>> UpdatePaymentModeAsync(Guid id, PaymentModeMatserDto model, Employee loggedInEmployee, Guid tenantId)
        {
            try
            {
                // Validation if employee is taking action in same tenant
                if (tenantId != loggedInEmployee.TenantId)
                {
                    _logger.LogWarning("Employee {EmployeeId} attempted to update Payment Mode in different tenant", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access Denied", "User do not have access for this information", 403);
                }

                // Checking permssion for managing masters
                var hasManagePermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);
                if (!hasManagePermission)
                {
                    _logger.LogWarning("Access DENIED for employee {EmployeeId} for managing PAYMENT MODE MASTER.", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to Manage masters", 403);
                }

                // Validating the prvided data
                if (model.Id != id)
                {
                    _logger.LogWarning("Employee {EmployeeId} provide different Ids in payload and path variable", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Invalid Data", "User has send invalid payload", 400);
                }

                var paymentMode = await _context.PaymentModeMatser.AsNoTracking().FirstOrDefaultAsync(et => et.Id == model.Id.Value && et.TenantId == tenantId);

                // Checking if Payment Mode exists
                if (paymentMode == null)
                {
                    _logger.LogWarning("Employee {EmployeeId} tries to update Payment Mode, but not found in database", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Payment Mode not found", "Payment Mode not found", 404);
                }

                // Mapping PaymentModeMatser to BsonDocument
                var existingEntityBson = _updateLogHelper.EntityToBsonDocument(paymentMode);

                // Mapping PaymentModeMatserDto to PaymentModeMatser
                _mapper.Map(model, paymentMode);

                _context.PaymentModeMatser.Update(paymentMode);
                await _context.SaveChangesAsync();

                _logger.LogInfo("Payment Mode {PaymentModeId} was updated by employee {EmployeeId}", paymentMode.Id, loggedInEmployee.Id);

                // Saving the old entity in mongoDB

                var mongoDBTask = _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject
                {
                    EntityId = paymentMode.Id.ToString(),
                    UpdatedById = loggedInEmployee.Id.ToString(),
                    OldObject = existingEntityBson,
                    UpdatedAt = DateTime.UtcNow
                }, "PaymentModeMasterModificationLog");

                // Mapping PaymentModeMatser to PaymentModeMatserVM
                var response = _mapper.Map<PaymentModeMatserVM>(paymentMode);
                return ApiResponse<object>.SuccessResponse(response, "Payment Mode updated Successfully", 200);
            }
            catch (DbUpdateException dbEx)
            {
                _logger.LogError(dbEx, "Database Exception occured while updating Payment Mode by employee {EmployeeId}", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(dbEx), 500);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Exception occured while updating Payment Mode by employee {EmployeeId}", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(ex), 500);
            }
        }
        public async Task<ApiResponse<object>> DeletePaymentModeAsync(Guid id, bool isActive, Employee loggedInEmployee, Guid tenantId)
        {
            string action = isActive ? "restore" : "delete";
            try
            {
                // Validation if employee is taking action in same tenant
                if (tenantId != loggedInEmployee.TenantId)
                {
                    _logger.LogWarning("Employee {EmployeeId} attempted to {Action} Payment Mode in different tenant", loggedInEmployee.Id, action);
                    return ApiResponse<object>.ErrorResponse("Access Denied", "User do not have access for this information", 403);
                }

                // Checking permssion for managing masters
                var hasManagePermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);
                if (!hasManagePermission)
                {
                    _logger.LogWarning("Access DENIED for employee {EmployeeId} for managing PAYMENT MODE MASTER.", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to Manage masters", 403);
                }

                var paymentMode = await _context.PaymentModeMatser.FirstOrDefaultAsync(et => et.Id == id && et.TenantId == tenantId);

                // Checking if Payment Mode exists
                if (paymentMode == null)
                {
                    _logger.LogWarning("Employee {EmployeeId} tries to {Action} Payment Mode, but not found in database", loggedInEmployee.Id, action);
                    return ApiResponse<object>.ErrorResponse("Payment Mode not found", "Payment Mode not found", 404);
                }

                // Mapping PaymentModeMatser to BsonDocument
                var existingEntityBson = _updateLogHelper.EntityToBsonDocument(paymentMode);

                paymentMode.IsActive = isActive;
                await _context.SaveChangesAsync();

                _logger.LogInfo("Payment Mode {PaymentModeId} was {Action}d by employee {EmployeeId}", paymentMode.Id, action, loggedInEmployee.Id);

                // Saving the old entity in mongoDB

                var mongoDBTask = _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject
                {
                    EntityId = paymentMode.Id.ToString(),
                    UpdatedById = loggedInEmployee.Id.ToString(),
                    OldObject = existingEntityBson,
                    UpdatedAt = DateTime.UtcNow
                }, "PaymentModeMatserModificationLog");

                // Mapping PaymentModeMatser to PaymentModeMatserVM
                var response = _mapper.Map<PaymentModeMatserVM>(paymentMode);
                return ApiResponse<object>.SuccessResponse(response, $"Payment Mode {action}d Successfully", 200);
            }
            catch (DbUpdateException dbEx)
            {
                _logger.LogError(dbEx, "Database Exception occured while {Action}ing Payment Mode by employee {EmployeeId}", action, loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(dbEx), 500);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Exception occured while {Action}ing Payment Mode by employee {EmployeeId}", action, loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(ex), 500);
            }
        }

        #endregion

        #region =================================================================== Document Category APIs ===================================================================

        /// <summary>
        /// Fetches the list of Document Categories for a given tenant and optional entity type.
        /// Ensures tenant validation, mapping, and proper logging.
        /// </summary>
        /// <param name="entityTypeId">Optional entity type filter (e.g., EmployeeEntity, ProjectEntity).</param>
        /// <param name="loggedInEmployee">Currently logged-in employee.</param>
        /// <param name="tenantId">Tenant Id context.</param>
        /// <returns>ApiResponse containing the document categories or error details.</returns>
        public async Task<ApiResponse<object>> GetDocumentCategoryMasterListAsync(Guid? entityTypeId, Employee loggedInEmployee, Guid tenantId)
        {
            try
            {
                // ✅ Tenant validation
                if (tenantId != loggedInEmployee.TenantId)
                {
                    _logger.LogWarning("Access denied. Employee {EmployeeId} (TenantId: {EmployeeTenantId}) attempted to fetch document categories for TenantId: {RequestedTenantId}",
                        loggedInEmployee.Id, loggedInEmployee.TenantId, tenantId);

                    return ApiResponse<object>.ErrorResponse("Access Denied", "You do not have access to this information", 403);
                }

                // ✅ Build query
                IQueryable<DocumentCategoryMaster> documentCategoryQuery = _context.DocumentCategoryMasters
                    .AsNoTracking() // optimization: read-only
                    .Where(dc => dc.TenantId == tenantId);

                // ✅ Apply optional filter
                if (entityTypeId.HasValue)
                {
                    documentCategoryQuery = documentCategoryQuery.Where(dc => dc.EntityTypeId == entityTypeId.Value);
                }

                // ✅ Fetch and map
                var documentCategories = await documentCategoryQuery.ToListAsync();
                var response = _mapper.Map<List<DocumentCategoryVM>>(documentCategories);

                _logger.LogInfo("{Count} document categories fetched successfully for TenantId: {TenantId} by Employee {EmployeeId}",
                    response.Count, tenantId, loggedInEmployee.Id);

                return ApiResponse<object>.SuccessResponse(response, $"{response.Count} document categories have been fetched successfully.", 200);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error occurred while fetching document categories for TenantId: {TenantId} by Employee {EmployeeId}",
                    tenantId, loggedInEmployee.Id);

                return ApiResponse<object>.ErrorResponse("Internal Server Error", "Server Error occured", 500);
            }
        }
        public async Task<ApiResponse<object>> CreateDocumentCategoryMasterAsync(CreateDocumentCategoryDto model, Employee loggedInEmployee, Guid tenantId)
        {
            try
            {
                // Validation if employee is taking action in same tenant
                if (tenantId != loggedInEmployee.TenantId)
                {
                    _logger.LogWarning("Employee {EmployeeId} attempted to add new Document Category in different tenant", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access Denied", "User do not have access for this information", 403);
                }
                var hasManagePermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);
                if (!hasManagePermission)
                {
                    _logger.LogWarning("Access DENIED for employee {EmployeeId} for managing Document Category Master.", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to Manage masters", 403);

                }
                var oldExists = await _context.DocumentCategoryMasters
                    .AnyAsync(dc => dc.Name == model.Name && dc.EntityTypeId == model.EntityTypeId && dc.TenantId == tenantId);
                if (oldExists)
                {
                    _logger.LogWarning("Document Category of {Name} is already exists in database for {TenantId}", model.Name, tenantId);
                    return ApiResponse<object>.ErrorResponse("Document Category already exists.", "Document Category already exists in database", 409);
                }
                // Mapping the DTO to Document Category Master Model
                var documentCategory = _mapper.Map<DocumentCategoryMaster>(model);
                documentCategory.CreatedAt = DateTime.UtcNow;
                documentCategory.TenantId = tenantId;

                _context.DocumentCategoryMasters.Add(documentCategory);
                await _context.SaveChangesAsync();

                _logger.LogInfo("New Document Category {DocumentCategoryId} was added by employee {EmployeeId}", documentCategory.Id, loggedInEmployee.Id);

                // Mapping the Document Category Master Model to View Model
                var response = _mapper.Map<DocumentCategoryVM>(documentCategory);
                return ApiResponse<object>.SuccessResponse(response, "Document Category craeted Successfully", 201);
            }
            catch (DbUpdateException dbEx)
            {
                _logger.LogError(dbEx, "Database Exception occured while adding new Document Category by employee {EmployeeId}", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(dbEx), 500);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Exception occured while adding new Document Category by employee {EmployeeId}", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(ex), 500);
            }
        }
        public async Task<ApiResponse<object>> UpdateDocumentCategoryMasterAsync(Guid id, CreateDocumentCategoryDto model, Employee loggedInEmployee, Guid tenantId)
        {
            try
            {
                // Validation if employee is taking action in same tenant
                if (tenantId != loggedInEmployee.TenantId)
                {
                    _logger.LogWarning("Employee {EmployeeId} attempted to update Document Category in different tenant", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access Denied", "User do not have access for this information", 403);
                }

                // Checking permssion for managing masters
                var hasManagePermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);
                if (!hasManagePermission)
                {
                    _logger.LogWarning("Access DENIED for employee {EmployeeId} for managing DOCUMENT CATEGORY MASTER.", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to Manage masters", 403);
                }

                // Validating the prvided data
                if (model.Id != id)
                {
                    _logger.LogWarning("Employee {EmployeeId} provide different Ids in payload and path variable", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Invalid Data", "User has send invalid payload", 400);
                }

                var categoryTask = Task.Run(async () =>
                {
                    await using var context = await _dbContextFactory.CreateDbContextAsync();
                    return await context.DocumentCategoryMasters.AsNoTracking().FirstOrDefaultAsync(et => et.Id == model.Id.Value && et.TenantId == tenantId);
                });
                var oldCategoryExistsTask = Task.Run(async () =>
                {
                    await using var context = await _dbContextFactory.CreateDbContextAsync();
                    return await context.DocumentCategoryMasters.AnyAsync(dc => dc.Name == model.Name && dc.Id != model.Id.Value && dc.EntityTypeId == model.EntityTypeId && dc.TenantId == tenantId);
                });

                await Task.WhenAll(categoryTask, oldCategoryExistsTask);

                var documentCategory = categoryTask.Result;
                var oldCategoryExists = oldCategoryExistsTask.Result;

                // Checking if Document Category exists
                if (documentCategory == null)
                {
                    _logger.LogWarning("Employee {EmployeeId} tries to update Document Category, but not found in database", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Document Category not found", "Document Category not found", 404);
                }
                if (oldCategoryExists)
                {
                    _logger.LogWarning("Document Category of {Name} is already exists in database for {TenantId} while updating document category", model.Name, tenantId);
                    return ApiResponse<object>.ErrorResponse("Document Category already exists.", "Document Category already exists in database", 409);
                }

                // Mapping DocumentCategoryMaster to BsonDocument
                var existingEntityBson = _updateLogHelper.EntityToBsonDocument(documentCategory);

                // Mapping DocumentCategoryDto to DocumentCategoryMaster
                _mapper.Map(model, documentCategory);

                _context.DocumentCategoryMasters.Update(documentCategory);
                await _context.SaveChangesAsync();

                _logger.LogInfo("Document Category {DocumentCategoryId} was updated by employee {EmployeeId}", documentCategory.Id, loggedInEmployee.Id);

                // Saving the old entity in mongoDB

                var mongoDBTask = _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject
                {
                    EntityId = documentCategory.Id.ToString(),
                    UpdatedById = loggedInEmployee.Id.ToString(),
                    OldObject = existingEntityBson,
                    UpdatedAt = DateTime.UtcNow
                }, "DocumentCategoryModificationLog");

                // Mapping DocumentCategoryMaster to DocumentCategoryVM
                var response = _mapper.Map<DocumentCategoryVM>(documentCategory);
                return ApiResponse<object>.SuccessResponse(response, "Document Category updated Successfully", 200);
            }
            catch (DbUpdateException dbEx)
            {
                _logger.LogError(dbEx, "Database Exception occured while updating Document Category by employee {EmployeeId}", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(dbEx), 500);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Exception occured while updating Document Category by employee {EmployeeId}", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(ex), 500);
            }
        }
        public async Task<ApiResponse<object>> DeleteDocumentCategoryMasterAsync(Guid id, Employee loggedInEmployee, Guid tenantId)
        {
            try
            {
                // Validation if employee is taking action in same tenant
                if (tenantId != loggedInEmployee.TenantId)
                {
                    _logger.LogWarning("Employee {EmployeeId} attempted to delete Document Category in different tenant", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access Denied", "User do not have access for this information", 403);
                }

                // Checking permssion for managing masters
                var hasManagePermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);
                if (!hasManagePermission)
                {
                    _logger.LogWarning("Access DENIED for employee {EmployeeId} for managing DOCUMENT CATEGORY MASTER.", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to Manage masters", 403);
                }

                var documentCategory = await _context.DocumentCategoryMasters.FirstOrDefaultAsync(et => et.Id == id && et.TenantId == tenantId);

                // Checking if Document Category exists
                if (documentCategory == null)
                {
                    _logger.LogWarning("Employee {EmployeeId} tries to delete Document Category, but not found in database", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Document Category not found", "Document Category not found", 404);
                }

                // Mapping DocumentCategoryMaster to BsonDocument
                var existingEntityBson = _updateLogHelper.EntityToBsonDocument(documentCategory);

                _context.DocumentCategoryMasters.Remove(documentCategory);
                await _context.SaveChangesAsync();

                _logger.LogInfo("Document Category {DocumentCategoryId} was deleted by employee {EmployeeId}", documentCategory.Id, loggedInEmployee.Id);

                // Saving the old entity in mongoDB

                var mongoDBTask = _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject
                {
                    EntityId = documentCategory.Id.ToString(),
                    UpdatedById = loggedInEmployee.Id.ToString(),
                    OldObject = existingEntityBson,
                    UpdatedAt = DateTime.UtcNow
                }, "DocumentCategoryModificationLog");

                // Mapping DocumentCategoryMatser to DocumentCategoryVM
                var response = _mapper.Map<DocumentCategoryVM>(documentCategory);
                return ApiResponse<object>.SuccessResponse(response, "Document Category deleted Successfully", 200);
            }
            catch (DbUpdateException dbEx)
            {
                _logger.LogError(dbEx, "Database Exception occured while deleteing Document Category by employee {EmployeeId}", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(dbEx), 500);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Exception occured while deleteing Document Category by employee {EmployeeId}", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(ex), 500);
            }
        }


        #endregion

        #region =================================================================== Document Type APIs ===================================================================


        public async Task<ApiResponse<object>> GetDocumentTypeMasterListAsync(Guid? documentCategoryId, Employee loggedInEmployee, Guid tenantId)
        {
            try
            {
                // ✅ Tenant validation
                if (tenantId != loggedInEmployee.TenantId)
                {
                    _logger.LogWarning("Access denied. Employee {EmployeeId} (TenantId: {EmployeeTenantId}) attempted to fetch document types for TenantId: {RequestedTenantId}",
                        loggedInEmployee.Id, loggedInEmployee.TenantId, tenantId);

                    return ApiResponse<object>.ErrorResponse("Access Denied", "You do not have access to this information", 403);
                }

                // ✅ Build query
                IQueryable<DocumentTypeMaster> documentTypeQuery = _context.DocumentTypeMasters
                    .AsNoTracking() // optimization: read-only
                    .Where(dc => dc.TenantId == tenantId);

                // ✅ Apply optional filter
                if (documentCategoryId.HasValue)
                {
                    documentTypeQuery = documentTypeQuery.Where(dc => dc.DocumentCategoryId == documentCategoryId.Value);
                }

                // ✅ Fetch and map
                var documentType = await documentTypeQuery.Include(dt => dt.DocumentCategory).ToListAsync();
                var response = _mapper.Map<List<DocumentTypeVM>>(documentType);

                _logger.LogInfo("{Count} document type fetched successfully for TenantId: {TenantId} by Employee {EmployeeId}",
                    response.Count, tenantId, loggedInEmployee.Id);

                return ApiResponse<object>.SuccessResponse(response, $"{response.Count} document type have been fetched successfully.", 200);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error occurred while fetching document type for TenantId: {TenantId} by Employee {EmployeeId}",
                    tenantId, loggedInEmployee.Id);

                return ApiResponse<object>.ErrorResponse("Internal Server Error", "Server Error occured", 500);
            }
        }
        public async Task<ApiResponse<object>> CreateDocumentTypeMasterAsync(CreateDocumentTypeDto model, Employee loggedInEmployee, Guid tenantId)
        {
            try
            {
                // Validation if employee is taking action in same tenant
                if (tenantId != loggedInEmployee.TenantId)
                {
                    _logger.LogWarning("Employee {EmployeeId} attempted to add new Document Type in different tenant", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access Denied", "User do not have access for this information", 403);
                }
                var hasManagePermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);
                if (!hasManagePermission)
                {
                    _logger.LogWarning("Access DENIED for employee {EmployeeId} for managing DOCUMENT TYPE MASTER.", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to Manage masters", 403);

                }
                var oldExists = await _context.DocumentTypeMasters
                    .AnyAsync(dt => dt.Name == model.Name && dt.DocumentCategoryId == model.DocumentCategoryId && dt.TenantId == tenantId);
                if (oldExists)
                {
                    _logger.LogWarning("Document Type of {Name} is already exists in database for {TenantId} while creating new document type", model.Name, tenantId);
                    return ApiResponse<object>.ErrorResponse("Document Type already exists.", "Document Type already exists in database", 409);
                }
                // Mapping the DTO to Document Type Master Model
                var documentType = _mapper.Map<DocumentTypeMaster>(model);
                if (string.IsNullOrWhiteSpace(model.RegexExpression))
                {
                    documentType.IsValidationRequired = false;
                }
                documentType.IsSystem = false;
                documentType.IsActive = true;
                documentType.CreatedAt = DateTime.UtcNow;
                documentType.TenantId = tenantId;

                _context.DocumentTypeMasters.Add(documentType);
                await _context.SaveChangesAsync();

                _logger.LogInfo("New Document Type {DocumentTypeId} was added by employee {EmployeeId}", documentType.Id, loggedInEmployee.Id);

                // Mapping the Document Type Master Model to View Model
                var response = _mapper.Map<DocumentTypeVM>(documentType);
                return ApiResponse<object>.SuccessResponse(response, "Document Type craeted Successfully", 201);
            }
            catch (DbUpdateException dbEx)
            {
                _logger.LogError(dbEx, "Database Exception occured while adding new Document Type by employee {EmployeeId}", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(dbEx), 500);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Exception occured while adding new Document Type by employee {EmployeeId}", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(ex), 500);
            }
        }
        public async Task<ApiResponse<object>> UpdateDocumentTypeMasterAsync(Guid id, CreateDocumentTypeDto model, Employee loggedInEmployee, Guid tenantId)
        {
            try
            {
                // Validation if employee is taking action in same tenant
                if (tenantId != loggedInEmployee.TenantId)
                {
                    _logger.LogWarning("Employee {EmployeeId} attempted to update Document Type in different tenant", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access Denied", "User do not have access for this information", 403);
                }

                // Checking permssion for managing masters
                var hasManagePermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);
                if (!hasManagePermission)
                {
                    _logger.LogWarning("Access DENIED for employee {EmployeeId} for managing DOCUMENT TYPE MASTER.", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to Manage masters", 403);
                }

                // Validating the prvided data
                if (model.Id != id)
                {
                    _logger.LogWarning("Employee {EmployeeId} provide different Ids in payload and path variable", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Invalid Data", "User has send invalid payload", 400);
                }

                var documentTypeTask = Task.Run(async () =>
                {
                    await using var context = await _dbContextFactory.CreateDbContextAsync();
                    return await context.DocumentTypeMasters.AsNoTracking().FirstOrDefaultAsync(et => et.Id == model.Id.Value && et.TenantId == tenantId);
                });
                var oldTypeExistsTask = Task.Run(async () =>
                {
                    await using var context = await _dbContextFactory.CreateDbContextAsync();
                    return await context.DocumentTypeMasters
                    .AnyAsync(dt => dt.Name == model.Name && dt.Id != model.Id.Value && dt.DocumentCategoryId == model.DocumentCategoryId && dt.TenantId == tenantId);
                });

                await Task.WhenAll(documentTypeTask, oldTypeExistsTask);

                var documentType = documentTypeTask.Result;
                var oldTypeExists = oldTypeExistsTask.Result;

                // Checking if Document Type exists
                if (documentType == null)
                {
                    _logger.LogWarning("Employee {EmployeeId} tries to update Document Type, but not found in database", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Document Type not found", "Document Type not found", 404);
                }
                if (oldTypeExists)
                {
                    _logger.LogWarning("Document Type of {Name} is already exists in database for {TenantId} while updating document Type", model.Name, tenantId);
                    return ApiResponse<object>.ErrorResponse("Document Type already exists.", "Document Type already exists in database", 409);
                }

                // Mapping DocumentTypeMaster to BsonDocument
                var existingEntityBson = _updateLogHelper.EntityToBsonDocument(documentType);

                // Mapping DocumentTypeDto to DocumentTypeMaster
                _mapper.Map(model, documentType);

                _context.DocumentTypeMasters.Update(documentType);
                await _context.SaveChangesAsync();

                _logger.LogInfo("Document Type {DocumentTypeId} was updated by employee {EmployeeId}", documentType.Id, loggedInEmployee.Id);

                // Saving the old entity in mongoDB

                var mongoDBTask = _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject
                {
                    EntityId = documentType.Id.ToString(),
                    UpdatedById = loggedInEmployee.Id.ToString(),
                    OldObject = existingEntityBson,
                    UpdatedAt = DateTime.UtcNow
                }, "DocumentTypeModificationLog");

                // Mapping DocumentTypeMaster to DocumentTypeVM
                var response = _mapper.Map<DocumentTypeVM>(documentType);
                return ApiResponse<object>.SuccessResponse(response, "Document Type updated Successfully", 200);
            }
            catch (DbUpdateException dbEx)
            {
                _logger.LogError(dbEx, "Database Exception occured while updating Document Type by employee {EmployeeId}", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(dbEx), 500);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Exception occured while updating Document Type by employee {EmployeeId}", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(ex), 500);
            }
        }
        public async Task<ApiResponse<object>> DeleteDocumentTypeMasterAsync(Guid id, bool isActive, Employee loggedInEmployee, Guid tenantId)
        {
            string action = isActive ? "restore" : "delete";
            try
            {
                // Validation if employee is taking action in same tenant
                if (tenantId != loggedInEmployee.TenantId)
                {
                    _logger.LogWarning("Employee {EmployeeId} attempted to {Action} Document Type in different tenant", loggedInEmployee.Id, action);
                    return ApiResponse<object>.ErrorResponse("Access Denied", "User do not have access for this information", 403);
                }

                // Checking permssion for managing masters
                var hasManagePermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);
                if (!hasManagePermission)
                {
                    _logger.LogWarning("Access DENIED for employee {EmployeeId} for managing DOCUMENT TYPE MASTER.", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to Manage masters", 403);
                }

                var documentType = await _context.DocumentTypeMasters.FirstOrDefaultAsync(et => et.Id == id && et.TenantId == tenantId);

                // Checking if Document Type exists
                if (documentType == null)
                {
                    _logger.LogWarning("Employee {EmployeeId} tries to {Action} Document Type, but not found in database", loggedInEmployee.Id, action);
                    return ApiResponse<object>.ErrorResponse("Document Type not found", "Document Type not found", 404);
                }
                if (documentType.IsSystem)
                {
                    _logger.LogWarning("Employee {EmployeeId} tries to {Action} Document Type, but could not take action on system defined entity", loggedInEmployee.Id, action);
                    return ApiResponse<object>.ErrorResponse($"Document is system defined cannot be {action}d", $"Document is system defined cannot be {action}d", 400);
                }
                // Mapping DocumentTypeMatser to BsonDocument
                var existingEntityBson = _updateLogHelper.EntityToBsonDocument(documentType);

                documentType.IsActive = isActive;
                await _context.SaveChangesAsync();

                _logger.LogInfo("Document Type {DocumentTypeId} was {Action}d by employee {EmployeeId}", documentType.Id, action, loggedInEmployee.Id);

                // Saving the old entity in mongoDB

                var mongoDBTask = _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject
                {
                    EntityId = documentType.Id.ToString(),
                    UpdatedById = loggedInEmployee.Id.ToString(),
                    OldObject = existingEntityBson,
                    UpdatedAt = DateTime.UtcNow
                }, "DocumentTypeModificationLog");

                // Mapping DocumentTypeMatser to DocumentTypeVM
                var response = _mapper.Map<DocumentTypeVM>(documentType);
                return ApiResponse<object>.SuccessResponse(response, $"Document Type {action}d Successfully", 200);
            }
            catch (DbUpdateException dbEx)
            {
                _logger.LogError(dbEx, "Database Exception occured while {Action}ing Document Type by employee {EmployeeId}", action, loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(dbEx), 500);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Exception occured while {Action}ing Document Type by employee {EmployeeId}", action, loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(ex), 500);
            }
        }

        #endregion

        #region =================================================================== Helper Function ===================================================================
        private static object ExceptionMapper(Exception ex)
        {
            return new
            {
                Message = ex.Message,
                StackTrace = ex.StackTrace,
                Source = ex.Source,
                InnerException = new
                {
                    Message = ex.InnerException?.Message,
                    StackTrace = ex.InnerException?.StackTrace,
                    Source = ex.InnerException?.Source,
                }
            };
        }


        #endregion
    }
}
