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

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

        public MasterService(
            IDbContextFactory<ApplicationDbContext> dbContextFactory,
            IServiceScopeFactory serviceScopeFactory,
            ApplicationDbContext context,
            ILoggingService logger,
            PermissionServices permission,
            IMapper mapper,
            UtilityMongoDBHelper updateLogHelper)
        {
            _dbContextFactory = dbContextFactory ?? throw new ArgumentNullException(nameof(dbContextFactory));
            _serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory));
            _context = context ?? throw new ArgumentNullException(nameof(context));
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
            _permission = permission ?? throw new ArgumentNullException(nameof(permission));
            _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
            _updateLogHelper = updateLogHelper ?? throw new ArgumentNullException(nameof(updateLogHelper));
        }

        #region =================================================================== Recurring Payment Status APIs ===================================================================

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

            try
            {
                // Step 1: Fetch global recurring payment status
                var status = await _context.RecurringPaymentStatus.OrderBy(rps => rps.Name).ToListAsync();

                _logger.LogInfo("Fetched {Count} recurring payment status records for tenantId: {TenantId}", status.Count, tenantId);

                return ApiResponse<object>.SuccessResponse(status, $"{status.Count} record(s) of recurring payment status fetched successfully", 200);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error fetching recurring payment status");
                return ApiResponse<object>.ErrorResponse("An error occurred while fetching recurring payment status", ex.Message, 500);
            }
        }
        #endregion

        #region =================================================================== Currency APIs ===================================================================

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

            try
            {
                // Step 1: Fetch global currencies
                var currencies = await _context.CurrencyMaster.OrderBy(ot => ot.CurrencyName).ToListAsync();

                _logger.LogInfo("Fetched {Count} currency records for tenantId: {TenantId}", currencies.Count, tenantId);

                return ApiResponse<object>.SuccessResponse(currencies, $"{currencies.Count} record(s) of currency fetched successfully", 200);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error fetching currency");
                return ApiResponse<object>.ErrorResponse("An error occurred while fetching currency", ex.Message, 500);
            }
        }

        #endregion

        #region =================================================================== Organization Type APIs ===================================================================

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

            try
            {
                // Step 1: Fetch global services
                var services = await _context.OrgTypeMasters.OrderBy(ot => ot.Name).ToListAsync();

                _logger.LogInfo("Fetched {Count} organization type records for tenantId: {TenantId}", services.Count, tenantId);

                return ApiResponse<object>.SuccessResponse(services, $"{services.Count} record(s) of organization type fetched successfully", 200);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error fetching organization type");
                return ApiResponse<object>.ErrorResponse("An error occurred while fetching organization type", ex.Message, 500);
            }
        }

        #endregion

        #region =================================================================== Global Services APIs ===================================================================
        public async Task<ApiResponse<object>> GetGlobalServicesAsync(Employee loggedInEmployee, Guid tenantId)
        {
            _logger.LogInfo("GetGlobalServices called");

            try
            {
                // Step 1: Fetch global services
                var services = await _context.GlobalServiceMasters.ToListAsync();

                _logger.LogInfo("Fetched {Count} global service records for tenantId: {TenantId}", services.Count, tenantId);

                return ApiResponse<object>.SuccessResponse(services, $"{services.Count} record(s) of global services fetched successfully", 200);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error fetching global services");
                return ApiResponse<object>.ErrorResponse("An error occurred while fetching global services", ex.Message, 500);
            }
        }
        public async Task<ApiResponse<object>> CreateGlobalServiceAsync(ServiceMasterDto serviceMasterDto, Employee loggedInEmployee, Guid tenantId)
        {
            _logger.LogInfo("CreateGlobalService called with Name: {ServiceName}", serviceMasterDto.Name);

            try
            {
                // Step 1: Permission check
                var hasManagePermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);
                if (!hasManagePermission)
                {
                    _logger.LogWarning("Access denied for employeeId: {EmployeeId}", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("You don't have access", "You don't have permission to take this action", 403);
                }

                // Step 2: Check for duplicate name
                bool isExist = await _context.GlobalServiceMasters
                    .AnyAsync(s => s.Name == serviceMasterDto.Name);

                if (isExist)
                {
                    _logger.LogWarning("Duplicate service name '{ServiceName}' attempted by employeeId: {EmployeeId}", serviceMasterDto.Name, loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse(
                        $"Service with name '{serviceMasterDto.Name}' already exists",
                        $"Service with name '{serviceMasterDto.Name}' already exists", 400);
                }

                // Step 3: Save new service
                GlobalServiceMaster service = _mapper.Map<GlobalServiceMaster>(serviceMasterDto);

                _context.GlobalServiceMasters.Add(service);
                await _context.SaveChangesAsync();

                _logger.LogInfo("New global service '{ServiceName}' created successfully by employeeId: {EmployeeId}", service.Name, loggedInEmployee.Id);

                return ApiResponse<object>.SuccessResponse(service, "New global service created successfully", 201);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error creating service");
                return ApiResponse<object>.ErrorResponse("Failed to create global service", ex.Message, 500);
            }
        }

        //public async Task<ApiResponse<object>> UpdateGlobalServiceAsync(Guid id, ServiceMasterDto serviceMasterDto, Employee loggedInEmployee, Guid tenantId)
        //{
        //    _logger.LogInfo("UpdateService called for Id: {Id}", id);

        //    try
        //    {

        //        // Step 1: Permission check
        //        var hasPermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);
        //        if (!hasPermission)
        //        {
        //            _logger.LogWarning("Access denied. EmployeeId: {EmployeeId} lacks Manage_Master permission.", loggedInEmployee.Id);
        //            return ApiResponse<object>.ErrorResponse("Access denied", "You don't have permission to take this action", 403);
        //        }

        //        // Step 2: Input validation
        //        if (serviceMasterDto.Id != id)
        //        {
        //            _logger.LogWarning("Invalid input data provided for UpdateService. Id: {Id}", id);
        //            return ApiResponse<object>.ErrorResponse("Invalid input", "Please provide valid service data", 400);
        //        }

        //        // Step 3: Retrieve service
        //        var service = await _context.ServiceMasters
        //            .FirstOrDefaultAsync(s => s.Id == id && s.TenantId == tenantId && s.IsActive);

        //        if (service == null)
        //        {
        //            _logger.LogWarning("Service not found for Id: {Id}, Tenant: {TenantId}", id, tenantId);
        //            return ApiResponse<object>.ErrorResponse("Service not found", "The requested service does not exist", 404);
        //        }

        //        // Step 4: Update and save
        //        service.Name = serviceMasterDto.Name.Trim();
        //        service.Description = serviceMasterDto.Description.Trim();

        //        await _context.SaveChangesAsync();

        //        var response = _mapper.Map<ServiceMasterVM>(service);

        //        _logger.LogInfo("Service updated successfully. Id: {Id}, TenantId: {TenantId}", service.Id, tenantId);
        //        return ApiResponse<object>.SuccessResponse(response, "Service updated successfully", 200);
        //    }
        //    catch (Exception ex)
        //    {
        //        _logger.LogError(ex, "Error while updating service Id: {Id}.", id);
        //        return ApiResponse<object>.ErrorResponse("An error occurred while updating the service", ex.Message, 500);
        //    }
        //}

        //public async Task<ApiResponse<object>> DeleteGlobalServiceAsync(Guid id, bool active, Employee loggedInEmployee, Guid tenantId)
        //{
        //    _logger.LogInfo("DeleteService called with ServiceId: {ServiceId}, IsActive: {IsActive}", id, active);

        //    try
        //    {
        //        // Step 1: Get validate permission
        //        var hasPermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);

        //        if (!hasPermission)
        //        {
        //            _logger.LogWarning("Access denied. EmployeeId: {EmployeeId} does not have Manage_Master permission.", loggedInEmployee.Id);
        //            return ApiResponse<object>.ErrorResponse("Access denied", "You don't have permission to delete services", 403);
        //        }

        //        // Step 2: Check if the service exists
        //        var service = await _context.ServiceMasters
        //            .FirstOrDefaultAsync(s => s.Id == id && s.TenantId == tenantId);

        //        if (service == null)
        //        {
        //            _logger.LogWarning("Service not found. ServiceId: {ServiceId}", id);
        //            return ApiResponse<object>.ErrorResponse("Service not found", "Service not found or already deleted", 404);
        //        }

        //        // Protect system-defined service
        //        if (service.IsSystem)
        //        {
        //            _logger.LogWarning("Attempt to delete system-defined service. ServiceId: {ServiceId}", id);
        //            return ApiResponse<object>.ErrorResponse("Cannot delete system-defined service", "This service is system-defined and cannot be deleted", 400);
        //        }

        //        // Step 3: Soft delete or restore
        //        service.IsActive = active;
        //        await _context.SaveChangesAsync();

        //        var status = active ? "restored" : "deactivated";
        //        _logger.LogInfo("Service {ServiceId} has been {Status} successfully by EmployeeId: {EmployeeId}", id, status, loggedInEmployee.Id);

        //        return ApiResponse<object>.SuccessResponse(new { }, $"Service {status} successfully", 200);
        //    }
        //    catch (Exception ex)
        //    {
        //        _logger.LogError(ex, "Unexpected error occurred while deleting service {ServiceId}", id);
        //        return ApiResponse<object>.ErrorResponse("Error deleting service", ex.Message, 500);
        //    }
        //}

        #endregion

        #region =================================================================== Services APIs ===================================================================
        public async Task<ApiResponse<object>> GetServicesAsync(Employee loggedInEmployee, Guid tenantId)
        {
            _logger.LogInfo("GetServices called");

            try
            {
                // Step 1: Fetch services for the tenant
                var services = await _context.ServiceMasters
                    .Where(s => s.TenantId == tenantId && s.IsActive)
                    .Select(s => _mapper.Map<ServiceMasterVM>(s))
                    .ToListAsync();

                _logger.LogInfo("Fetched {Count} service records for tenantId: {TenantId}", services.Count, tenantId);

                return ApiResponse<object>.SuccessResponse(services, $"{services.Count} record(s) of services fetched successfully", 200);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error fetching services");
                return ApiResponse<object>.ErrorResponse("An error occurred while fetching services", ex.Message, 500);
            }
        }

        /// <summary>
        /// Asynchronously retrieves a structured list of services, their activity groups, activities, and associated checklists for a specific tenant.
        /// This method fetches data in multiple steps and processes it in-memory, preserving the original business logic.
        /// </summary>
        /// <param name="loggedInEmployee">The employee currently logged in. This parameter is available for future authorization or logging extensions.</param>
        /// <param name="tenantId">The unique identifier of the tenant for which to retrieve the data.</param>
        /// <returns>An ApiResponse containing a list of ServiceDetailsListVM or an error message in case of failure.</returns>
        public async Task<ApiResponse<object>> GetServiceDetailsListAsync(Guid? serviceId, Employee loggedInEmployee, Guid tenantId)
        {
            // Log the initiation of the request with structured parameters for better traceability.
            _logger.LogInfo("Attempting to fetch service details list for TenantId: {TenantId}", tenantId);

            try
            {
                // --- Step 1: Fetch all relevant activities for the tenant ---
                // This query retrieves all active 'ActivityMaster' records for the given tenant that are properly linked to an ActivityGroup and a Service.
                // Eager loading (.Include/.ThenInclude) is used to bring in related ActivityGroup and Service entities to prevent N+1 query problems later.
                var activityQuery = _context.ActivityMasters
                    .Include(a => a.ActivityGroup)
                        .ThenInclude(ag => ag!.Service)
                    .Where(a => a.TenantId == tenantId && a.IsActive);

                if (serviceId.HasValue)
                {
                    activityQuery = activityQuery.Where(a => a.ActivityGroup != null && a.ActivityGroup.Service != null && a.ActivityGroup.Service.Id == serviceId);
                }

                var activities = await activityQuery
                    .ToListAsync();

                _logger.LogInfo("Step 1 complete: Fetched {ActivityCount} activities for TenantId: {TenantId}", activities.Count, tenantId);

                // --- Step 2: Fetch all checklists related to the retrieved activities ---
                // To avoid fetching all checklists in the database, we first collect the IDs of the activities from the previous step.
                var activityIds = activities.Select(a => a.Id).ToList();

                // This second database query fetches only the 'ActivityCheckList' records associated with the relevant activities.
                var checkLists = await _context.ActivityCheckLists
                    .Where(c => c.TenantId == tenantId && activityIds.Contains(c.ActivityId))
                    .ToListAsync();

                _logger.LogInfo("Step 2 complete: Fetched {ChecklistCount} checklists for TenantId: {TenantId}", checkLists.Count, tenantId);

                // --- Step 3: Group checklists by activity in memory for efficient lookup ---
                // To quickly find all checklists for a given activity, we group the flat list of checklists into a dictionary.
                // The key is the ActivityId, and the value is the list of associated checklists.
                var groupedChecklists = checkLists
                    .GroupBy(c => c.ActivityId)
                    .ToDictionary(g => g.Key, g => g.ToList());

                // --- Step 4: Build the hierarchical ViewModel structure in memory ---
                // First, get distinct lists of services and activity groups from the already fetched 'activities' collection.
                var services = activities.Select(a => a.ActivityGroup!.Service!).Distinct().ToList();
                var activityGroupList = activities.Select(a => a.ActivityGroup!).Distinct().ToList();

                // Now, construct the final nested ViewModel.
                // This part iterates through the distinct services and builds the response object by filtering the in-memory collections.
                List<ServiceDetailsListVM> Vm = services.Select(s =>
                {
                    var response = new ServiceDetailsListVM
                    {
                        Id = s.Id,
                        Name = s.Name,
                        Description = s.Description,
                        IsActive = s.IsActive,
                        IsSystem = s.IsSystem,
                        ActivityGroups = activityGroupList
                            .Where(ag => ag.ServiceId == s.Id) // Find groups for the current service
                            .Select(ag => new ActivityGroupDetailsListVM
                            {
                                Id = ag.Id,
                                Name = ag.Name,
                                Description = ag.Description,
                                IsActive = ag.IsActive,
                                IsSystem = ag.IsSystem,
                                Activities = activities
                                    .Where(a => a.ActivityGroupId == ag.Id) // Find activities for the current group
                                    .Select(a =>
                                    {
                                        // Retrieve the checklists for the current activity from our dictionary lookup.
                                        var checklistForActivity = groupedChecklists.ContainsKey(a.Id)
                                            ? groupedChecklists[a.Id]
                                            : new List<ActivityCheckList>();

                                        return new ActivityVM
                                        {
                                            Id = a.Id,
                                            ActivityName = a.ActivityName,
                                            UnitOfMeasurement = a.UnitOfMeasurement,
                                            // BUG FIX: Correctly mapping properties from the activity ('a') itself, not the parent service ('s').
                                            IsActive = a.IsActive,
                                            IsSystem = a.IsSystem,
                                            CheckLists = _mapper.Map<List<CheckListVM>>(checklistForActivity)
                                        };
                                    }).ToList()
                            }).ToList()
                    };
                    return response;
                }).ToList();

                _logger.LogInfo("Successfully processed and mapped {ServiceCount} services for TenantId: {TenantId}", Vm.Count, tenantId);

                return ApiResponse<object>.SuccessResponse(Vm, "Service details list fetched successfully", 200);
            }
            catch (Exception ex)
            {
                // If any part of the process fails, log the detailed exception and return a standardized error response.
                _logger.LogError(ex, "An error occurred while fetching service details for TenantId: {TenantId}", tenantId);
                return ApiResponse<object>.ErrorResponse("An internal server error occurred while fetching service details.", 500);
            }
        }
        public async Task<ApiResponse<object>> CreateServiceAsync(ServiceMasterDto serviceMasterDto, Employee loggedInEmployee, Guid tenantId)
        {
            _logger.LogInfo("CreateService called with Name: {ServiceName}", serviceMasterDto.Name);

            try
            {
                // Step 1: Permission check
                var hasManagePermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);
                if (!hasManagePermission)
                {
                    _logger.LogWarning("Access denied for employeeId: {EmployeeId}", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("You don't have access", "You don't have permission to take this action", 403);
                }

                // Step 2: Check for duplicate name
                bool isExist = await _context.ServiceMasters
                    .AnyAsync(s => s.TenantId == tenantId && s.Name == serviceMasterDto.Name);

                if (isExist)
                {
                    _logger.LogWarning("Duplicate service name '{ServiceName}' attempted by employeeId: {EmployeeId}", serviceMasterDto.Name, loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse(
                        $"Service with name '{serviceMasterDto.Name}' already exists",
                        $"Service with name '{serviceMasterDto.Name}' already exists", 400);
                }

                // Step 3: Save new service
                ServiceMaster service = _mapper.Map<ServiceMaster>(serviceMasterDto);
                service.TenantId = tenantId;
                service.IsActive = true;
                service.IsSystem = false;
                _context.ServiceMasters.Add(service);
                await _context.SaveChangesAsync();

                var response = _mapper.Map<ServiceMasterVM>(service);

                _logger.LogInfo("New service '{ServiceName}' created successfully by employeeId: {EmployeeId}", service.Name, loggedInEmployee.Id);

                return ApiResponse<object>.SuccessResponse(response, "New service created successfully", 201);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error creating service");
                return ApiResponse<object>.ErrorResponse("Failed to create service", ex.Message, 500);
            }
        }
        public async Task<ApiResponse<object>> UpdateServiceAsync(Guid id, ServiceMasterDto serviceMasterDto, Employee loggedInEmployee, Guid tenantId)
        {
            _logger.LogInfo("UpdateService called for Id: {Id}", id);

            try
            {

                // Step 1: Permission check
                var hasPermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);
                if (!hasPermission)
                {
                    _logger.LogWarning("Access denied. EmployeeId: {EmployeeId} lacks Manage_Master permission.", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access denied", "You don't have permission to take this action", 403);
                }

                // Step 2: Input validation
                if (serviceMasterDto.Id != id)
                {
                    _logger.LogWarning("Invalid input data provided for UpdateService. Id: {Id}", id);
                    return ApiResponse<object>.ErrorResponse("Invalid input", "Please provide valid service data", 400);
                }

                // Step 3: Retrieve service
                var service = await _context.ServiceMasters
                    .FirstOrDefaultAsync(s => s.Id == id && s.TenantId == tenantId && s.IsActive);

                if (service == null)
                {
                    _logger.LogWarning("Service not found for Id: {Id}, Tenant: {TenantId}", id, tenantId);
                    return ApiResponse<object>.ErrorResponse("Service not found", "The requested service does not exist", 404);
                }

                var existingEntityBson = _updateLogHelper.EntityToBsonDocument(service);

                // Step 4: Update and save
                service.Name = serviceMasterDto.Name.Trim();
                service.Description = serviceMasterDto.Description.Trim();

                await _context.SaveChangesAsync();

                var response = _mapper.Map<ServiceMasterVM>(service);

                await _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject
                {
                    EntityId = service.Id.ToString(),
                    UpdatedById = loggedInEmployee.Id.ToString(),
                    OldObject = existingEntityBson,
                    UpdatedAt = DateTime.UtcNow
                }, "ServiceMasterModificationLog");

                _logger.LogInfo("Service updated successfully. Id: {Id}, TenantId: {TenantId}", service.Id, tenantId);
                return ApiResponse<object>.SuccessResponse(response, "Service updated successfully", 200);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error while updating service Id: {Id}.", id);
                return ApiResponse<object>.ErrorResponse("An error occurred while updating the service", ex.Message, 500);
            }
        }
        public async Task<ApiResponse<object>> DeleteServiceAsync(Guid id, bool active, Employee loggedInEmployee, Guid tenantId)
        {
            _logger.LogInfo("DeleteService called with ServiceId: {ServiceId}, IsActive: {IsActive}", id, active);

            try
            {
                // Step 1: Get validate permission
                var hasPermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);

                if (!hasPermission)
                {
                    _logger.LogWarning("Access denied. EmployeeId: {EmployeeId} does not have Manage_Master permission.", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access denied", "You don't have permission to delete services", 403);
                }

                // Step 2: Check if the service exists
                var service = await _context.ServiceMasters
                    .FirstOrDefaultAsync(s => s.Id == id && s.TenantId == tenantId);

                if (service == null)
                {
                    _logger.LogWarning("Service not found. ServiceId: {ServiceId}", id);
                    return ApiResponse<object>.ErrorResponse("Service not found", "Service not found or already deleted", 404);
                }

                // Protect system-defined service
                if (service.IsSystem)
                {
                    _logger.LogWarning("Attempt to delete system-defined service. ServiceId: {ServiceId}", id);
                    return ApiResponse<object>.ErrorResponse("Cannot delete system-defined service", "This service is system-defined and cannot be deleted", 400);
                }

                var activityGroupExists = await _context.ActivityGroupMasters.AnyAsync(ag => ag.ServiceId == service.Id && ag.TenantId == tenantId);
                if (activityGroupExists)
                {
                    _logger.LogWarning("Activity group exists for this cannot be deleted ServiceId: {ServiceId}", id);
                    return ApiResponse<object>.ErrorResponse("Activity group existed for this service cannot delete", "Activity group existed for this service cannot delete", 400);
                }

                var existingEntityBson = _updateLogHelper.EntityToBsonDocument(service);

                // Step 3: Soft delete or restore
                service.IsActive = active;
                await _context.SaveChangesAsync();

                await _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject
                {
                    EntityId = service.Id.ToString(),
                    UpdatedById = loggedInEmployee.Id.ToString(),
                    OldObject = existingEntityBson,
                    UpdatedAt = DateTime.UtcNow
                }, "ServiceMasterModificationLog");

                var status = active ? "restored" : "deactivated";
                _logger.LogInfo("Service {ServiceId} has been {Status} successfully by EmployeeId: {EmployeeId}", id, status, loggedInEmployee.Id);

                return ApiResponse<object>.SuccessResponse(new { }, $"Service {status} successfully", 200);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Unexpected error occurred while deleting service {ServiceId}", id);
                return ApiResponse<object>.ErrorResponse("Error deleting service", ex.Message, 500);
            }
        }
        #endregion

        #region =================================================================== Activity Group APIs ===================================================================

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

            try
            {
                // Step 1: Fetch all activity groups for the tenant
                var activityGroupQuery = _context.ActivityGroupMasters
                    .Include(ag => ag.Service)
                    .Where(ag => ag.TenantId == tenantId && ag.IsActive);

                if (serviceId.HasValue)
                {
                    activityGroupQuery = activityGroupQuery.Where(ag => ag.ServiceId == serviceId);
                }

                var activityGroups = await activityGroupQuery
                    .Select(ag => _mapper.Map<ActivityGroupMasterVM>(ag))
                    .ToListAsync();

                _logger.LogInfo("{Count} activity group(s) fetched for tenantId: {TenantId}", activityGroups.Count, tenantId);

                return ApiResponse<object>.SuccessResponse(activityGroups, $"{activityGroups.Count} record(s) of activity groups fetched successfully", 200);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error fetching activity groups");
                return ApiResponse<object>.ErrorResponse("An error occurred while fetching activity groups", ex.Message, 500);
            }
        }
        public async Task<ApiResponse<object>> CreateActivityGroupAsync(ActivityGroupDto activityGroupDto, Employee loggedInEmployee, Guid tenantId)
        {
            _logger.LogInfo("CreateActivityGroup called with Name: {Name}", activityGroupDto.Name);

            try
            {
                // Step 1: Check Manage Master permission
                var hasPermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);
                if (!hasPermission)
                {
                    _logger.LogWarning("Access denied. EmployeeId: {EmployeeId} lacks Manage_Master permission", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access denied", "You don't have permission to take this action", 403);
                }

                // Step 2: Check for duplicate name within ActivityGroupMasters
                bool isExist = await _context.ActivityGroupMasters
                    .AnyAsync(ag => ag.TenantId == tenantId && ag.Name.ToLower() == activityGroupDto.Name.ToLower());

                if (isExist)
                {
                    _logger.LogWarning("Duplicate activity group name '{Name}' attempted by employeeId: {EmployeeId}", activityGroupDto.Name, loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse(
                        $"Activity group with name '{activityGroupDto.Name}' already exists",
                        $"Activity group with name '{activityGroupDto.Name}' already exists", 400);
                }

                // Step 3: Map and persist
                var activityGroup = _mapper.Map<ActivityGroupMaster>(activityGroupDto);
                activityGroup.TenantId = tenantId;
                activityGroup.IsActive = true;
                activityGroup.IsSystem = false;

                _context.ActivityGroupMasters.Add(activityGroup);
                await _context.SaveChangesAsync();

                var service = await _context.ServiceMasters.FirstOrDefaultAsync(s => s.Id == activityGroup.ServiceId);

                var response = _mapper.Map<ActivityGroupMasterVM>(activityGroup);

                _logger.LogInfo("New activity group '{Name}' created successfully by employeeId: {EmployeeId}", activityGroup.Name, loggedInEmployee.Id);
                return ApiResponse<object>.SuccessResponse(response, "New activity group created successfully", 201);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error creating activity group");
                return ApiResponse<object>.ErrorResponse("Failed to create activity group", ex.Message, 500);
            }
        }
        public async Task<ApiResponse<object>> UpdateActivityGroupAsync(Guid id, ActivityGroupDto activityGroupDto, Employee loggedInEmployee, Guid tenantId)
        {
            _logger.LogInfo("UpdateActivityGroup called for Id: {Id}", id);

            try
            {
                // Step 1: Permission check
                var hasPermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);
                if (!hasPermission)
                {
                    _logger.LogWarning("Access denied. EmployeeId: {EmployeeId} lacks Manage_Master permission.", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access denied", "You don't have permission to take this action", 403);
                }

                // Step 2: Input validation
                if (activityGroupDto.Id != id)
                {
                    _logger.LogWarning("Invalid input for activity group update. Id: {Id}", id);
                    return ApiResponse<object>.ErrorResponse("Invalid input", "Please provide valid data to update activity group", 400);
                }

                var service = await _context.ServiceMasters.FirstOrDefaultAsync(s => s.Id == activityGroupDto.ServiceId && s.IsActive);

                if (service == null)
                {
                    _logger.LogWarning("User tries to update activity group, but service not found");
                    return ApiResponse<object>.ErrorResponse("Invalid service ID", "Please provide valid service ID to update activity group", 400);
                }

                // Step 3: Retrieve existing activity group
                var activityGroup = await _context.ActivityGroupMasters
                    .Include(ag => ag.Service)
                    .FirstOrDefaultAsync(ag => ag.Id == id && ag.TenantId == tenantId && ag.IsActive);

                if (activityGroup == null)
                {
                    _logger.LogWarning("Activity group not found. Id: {Id}, TenantId: {TenantId}", id, tenantId);
                    return ApiResponse<object>.ErrorResponse("Activity group not found", "No such activity group exists", 404);
                }

                var existingEntityBson = _updateLogHelper.EntityToBsonDocument(activityGroup);

                // Step 4: Update and save
                activityGroup.Name = activityGroupDto.Name.Trim();
                activityGroup.Description = activityGroupDto.Description.Trim();
                activityGroup.ServiceId = activityGroupDto.ServiceId;

                await _context.SaveChangesAsync();

                var response = _mapper.Map<ActivityGroupMasterVM>(activityGroup);

                await _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject
                {
                    EntityId = activityGroup.Id.ToString(),
                    UpdatedById = loggedInEmployee.Id.ToString(),
                    OldObject = existingEntityBson,
                    UpdatedAt = DateTime.UtcNow
                }, "ActivityGroupMasterModificationLog");

                _logger.LogInfo("Activity group updated successfully. Id: {Id}, TenantId: {TenantId}", activityGroup.Id, tenantId);
                return ApiResponse<object>.SuccessResponse(response, "Activity group updated successfully", 200);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error while updating activity group Id: {Id}", id);
                return ApiResponse<object>.ErrorResponse("An error occurred while updating the activity group", ex.Message, 500);
            }
        }
        public async Task<ApiResponse<object>> DeleteActivityGroupAsync(Guid id, bool isActive, Employee loggedInEmployee, Guid tenantId)
        {
            _logger.LogInfo("DeleteActivityGroup called with ActivityGroupId: {ActivityGroupId}, IsActive: {IsActive}", id, isActive);

            try
            {
                // Step 1: Permission check
                var hasPermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);

                if (!hasPermission)
                {
                    _logger.LogWarning("Access denied. EmployeeId: {EmployeeId} does not have Manage_Master permission.", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access denied", "You don't have permission to delete activity groups", 403);
                }

                // Step 2: Fetch the activity group
                var activityGroup = await _context.ActivityGroupMasters
                    .FirstOrDefaultAsync(ag => ag.Id == id && ag.TenantId == tenantId);

                if (activityGroup == null)
                {
                    _logger.LogWarning("ActivityGroup not found. Id: {ActivityGroupId}", id);
                    return ApiResponse<object>.ErrorResponse("Activity group not found", "Activity group not found or already deleted", 404);
                }

                //Protect system-defined activity group
                if (activityGroup.IsSystem)
                {
                    _logger.LogWarning("Attempt to delete system-defined activity group. Id: {ActivityGroupId}", id);
                    return ApiResponse<object>.ErrorResponse("Cannot delete system-defined activity group", "This activity group is system-defined and cannot be deleted", 400);
                }

                var activityExists = await _context.ActivityMasters.AnyAsync(ag => ag.ActivityGroupId == activityGroup.Id && ag.TenantId == tenantId);
                if (activityExists)
                {
                    _logger.LogWarning("Activity exists for this cannot be deleted ActivityGroupId: {ActivityGroupId}", id);
                    return ApiResponse<object>.ErrorResponse("Activity existed for this service cannot delete", "Activity existed for this service cannot delete", 400);
                }

                var existingEntityBson = _updateLogHelper.EntityToBsonDocument(activityGroup);

                // Step 3: Perform soft delete or restore
                activityGroup.IsActive = isActive;
                await _context.SaveChangesAsync();

                await _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject
                {
                    EntityId = activityGroup.Id.ToString(),
                    UpdatedById = loggedInEmployee.Id.ToString(),
                    OldObject = existingEntityBson,
                    UpdatedAt = DateTime.UtcNow
                }, "ActivityGroupMasterModificationLog");

                var status = isActive ? "restored" : "deactivated";
                _logger.LogInfo("ActivityGroup {ActivityGroupId} has been {Status} by EmployeeId: {EmployeeId}", id, status, loggedInEmployee.Id);

                return ApiResponse<object>.SuccessResponse(new { }, $"Activity group {status} successfully", 200);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Unexpected error occurred while deleting ActivityGroup {ActivityGroupId}", id);
                return ApiResponse<object>.ErrorResponse("Error deleting activity group", ex.Message, 500);
            }
        }

        #endregion

        #region =================================================================== Activity APIs ===================================================================
        public async Task<ApiResponse<object>> GetActivitiesMasterAsync(Guid? activityGroupId, Employee loggedInEmployee, Guid tenantId)
        {
            _logger.LogInfo("GetActivitiesMaster called");

            try
            {
                // Step 1: Fetch all active activities for the tenant

                var activityQuery = _context.ActivityMasters
                    .Include(c => c.ActivityGroup)
                        .ThenInclude(ag => ag!.Service)
                    .Where(c => c.TenantId == tenantId && c.IsActive);

                if (activityGroupId.HasValue)
                {
                    activityQuery = activityQuery.Where(a => a.ActivityGroupId == activityGroupId);
                }

                var activities = await activityQuery
                    .ToListAsync();

                if (activities.Count == 0)
                {
                    _logger.LogWarning("No active activities found for tenantId: {TenantId}", tenantId);
                    return ApiResponse<object>.SuccessResponse(new List<ActivityVM>(), "No activity records found", 200);
                }

                // Step 2: Fetch all checklists for those activities in a single query to avoid N+1
                var activityIds = activities.Select(a => a.Id).ToList();
                var checkLists = await _context.ActivityCheckLists
                    .Where(c => c.TenantId == tenantId && activityIds.Contains(c.ActivityId))
                    .ToListAsync();

                // Step 3: Group checklists by activity
                var groupedChecklists = checkLists
                    .GroupBy(c => c.ActivityId)
                    .ToDictionary(g => g.Key, g => g.ToList());

                // Step 4: Map to ViewModel
                var activityVMs = activities.Select(activity =>
                {
                    var checklistForActivity = groupedChecklists.ContainsKey(activity.Id)
                        ? groupedChecklists[activity.Id]
                        : new List<ActivityCheckList>();

                    var response = _mapper.Map<ActivityVM>(activity);
                    response.CheckLists = _mapper.Map<List<CheckListVM>>(checklistForActivity);
                    return response;

                }).ToList();

                _logger.LogInfo("{Count} activity records fetched successfully for tenantId: {TenantId}", activityVMs.Count, tenantId);

                return ApiResponse<object>.SuccessResponse(activityVMs, $"{activityVMs.Count} activity records fetched successfully", 200);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error occurred in GetActivitiesMaster");
                return ApiResponse<object>.ErrorResponse("Failed to fetch activity records", ex.Message, 500);
            }
        }
        public async Task<ApiResponse<object>> CreateActivityAsync(CreateActivityMasterDto createActivity, Employee loggedInEmployee, Guid tenantId)
        {
            _logger.LogInfo("CreateActivity called with ActivityName: {Name}", createActivity?.ActivityName ?? "null");

            try
            {
                // Step 1: Validate input
                if (createActivity == null)
                {
                    _logger.LogWarning("Null request body received in CreateActivity");
                    return ApiResponse<object>.ErrorResponse("Invalid input", "Activity data is required", 400);
                }

                var activityGroup = await _context.ActivityGroupMasters
                    .Include(ag => ag.Service)
                    .FirstOrDefaultAsync(ag => ag.Id == createActivity.ActivityGroupId && ag.TenantId == tenantId);
                if (activityGroup == null)
                {
                    _logger.LogWarning("User tried to create new activity, but not found activity group");
                    return ApiResponse<object>.ErrorResponse("Invalid input", "Activity data is required", 400);
                }

                // Step 2: Check permissions
                var hasPermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);
                if (!hasPermission)
                {
                    _logger.LogWarning("Access denied. EmployeeId: {EmployeeId} does not have Manage_Master permission.", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access denied", "You don't have permission to perform this action", 403);
                }

                // Step 3: Convert DTO to entity and add activity
                var activityMaster = _mapper.Map<ActivityMaster>(createActivity);
                activityMaster.TenantId = tenantId;

                _context.ActivityMasters.Add(activityMaster);
                await _context.SaveChangesAsync();

                List<CheckListVM> checkListVMs = new();

                // Step 4: Handle checklist items if present
                if (createActivity.CheckList?.Any() == true)
                {
                    var activityCheckLists = createActivity.CheckList
                        .Select(c =>
                        {
                            var response = _mapper.Map<ActivityCheckList>(c);
                            response.ActivityId = activityMaster.Id;
                            response.IsChecked = false;
                            response.TenantId = tenantId;
                            return response;
                        })
                        .ToList();

                    _context.ActivityCheckLists.AddRange(activityCheckLists);
                    await _context.SaveChangesAsync();

                    checkListVMs = activityCheckLists
                        .Select(c =>
                        {
                            var response = _mapper.Map<CheckListVM>(c);
                            return response;
                        })
                        .ToList();
                }

                // Step 5: Prepare final response
                var activityVM = _mapper.Map<ActivityVM>(activityMaster);
                activityVM.CheckLists = checkListVMs;

                _logger.LogInfo("Activity '{Name}' created successfully for tenant {TenantId}", activityMaster.ActivityName, tenantId);
                return ApiResponse<object>.SuccessResponse(activityVM, "Activity created successfully", 201);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error occurred while creating activity");
                return ApiResponse<object>.ErrorResponse("An error occurred while creating activity", ex.Message, 500);
            }
        }
        public async Task<ApiResponse<object>> UpdateActivityAsync(Guid id, CreateActivityMasterDto createActivity, Employee loggedInEmployee, Guid tenantId)
        {
            _logger.LogInfo("UpdateActivity called for ActivityId: {ActivityId}", id);

            try
            {
                // Step 1: Validate input
                if (createActivity == null || string.IsNullOrWhiteSpace(createActivity.ActivityName) || string.IsNullOrWhiteSpace(createActivity.UnitOfMeasurement))
                {
                    _logger.LogWarning("Invalid activity update input for ActivityId: {ActivityId}", id);
                    return ApiResponse<object>.ErrorResponse("Invalid input", "ActivityName and UnitOfMeasurement are required", 400);
                }

                // Step 2: Check permissions
                var hasPermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);
                if (!hasPermission)
                {
                    _logger.LogWarning("Access denied for employeeId: {EmployeeId}", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access denied", "You don't have permission to update activities", 403);
                }

                // Step 3: Validate service, activity group, and activity existence
                var activityGroup = await _context.ActivityGroupMasters.Include(ag => ag.Service).FirstOrDefaultAsync(ag => ag.Id == createActivity.ActivityGroupId && ag.IsActive);

                if (activityGroup == null)
                {
                    _logger.LogWarning("User tries to update activity, but cannot able found activity group or service");
                    return ApiResponse<object>.ErrorResponse("Invalid activity group ID or service ID", "Please provide valid activity group ID or service ID to update activity group", 400);
                }

                var activity = await _context.ActivityMasters
                    .Include(a => a.ActivityGroup)
                        .ThenInclude(ag => ag!.Service)
                    .FirstOrDefaultAsync(a => a.Id == id && a.IsActive && a.TenantId == tenantId);

                if (activity == null)
                {
                    _logger.LogWarning("Activity not found for ActivityId: {ActivityId}, TenantId: {TenantId}", id, tenantId);
                    return ApiResponse<object>.ErrorResponse("Activity not found", "Activity not found", 404);
                }

                var existingEntityBson = _updateLogHelper.EntityToBsonDocument(activity);

                // Step 4: Update activity core data
                activity.ActivityName = createActivity.ActivityName.Trim();
                activity.UnitOfMeasurement = createActivity.UnitOfMeasurement.Trim();
                activity.ActivityGroupId = createActivity.ActivityGroupId;

                await _context.SaveChangesAsync();

                await _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject
                {
                    EntityId = activity.Id.ToString(),
                    UpdatedById = loggedInEmployee.Id.ToString(),
                    OldObject = existingEntityBson,
                    UpdatedAt = DateTime.UtcNow
                }, "ActivityMasterModificationLog");

                // Step 5: Handle checklist updates
                var existingChecklists = await _context.ActivityCheckLists
                    .AsNoTracking()
                    .Where(c => c.ActivityId == activity.Id)
                    .ToListAsync();

                var updatedChecklistVMs = new List<CheckListVM>();

                if (createActivity.CheckList != null && createActivity.CheckList.Any())
                {
                    var incomingCheckIds = createActivity.CheckList.Where(c => c.Id != null).Select(c => c.Id!.Value).ToHashSet();

                    // Prepare lists
                    var newChecks = createActivity.CheckList.Where(c => c.Id == null);
                    var updates = createActivity.CheckList.Where(c => c.Id != null && existingChecklists.Any(ec => ec.Id == c.Id));
                    var deletes = existingChecklists.Where(ec => !incomingCheckIds.Contains(ec.Id)).ToList();

                    var toAdd = newChecks
                        .Select(c =>
                        {
                            var response = _mapper.Map<ActivityCheckList>(c);
                            response.ActivityId = activity.Id;
                            response.IsChecked = false;
                            response.TenantId = tenantId;
                            return response;
                        })
                        .ToList();

                    var toUpdate = updates
                        .Select(c =>
                        {
                            var response = _mapper.Map<ActivityCheckList>(c);
                            response.ActivityId = activity.Id;
                            response.TenantId = tenantId;
                            return response;
                        })
                        .ToList();

                    _context.ActivityCheckLists.AddRange(toAdd);
                    _context.ActivityCheckLists.UpdateRange(toUpdate);
                    _context.ActivityCheckLists.RemoveRange(deletes);

                    await _context.SaveChangesAsync();

                    // Prepare view model
                    updatedChecklistVMs = toAdd.Concat(toUpdate)
                        .Select(c => _mapper.Map<CheckListVM>(c))
                        .ToList();
                }
                else if (existingChecklists.Any())
                {
                    // If no checklist provided, remove all existing
                    _context.ActivityCheckLists.RemoveRange(existingChecklists);
                    await _context.SaveChangesAsync();
                }

                // Step 6: Prepare response
                var activityVM = _mapper.Map<ActivityVM>(activity);
                activityVM.CheckLists = updatedChecklistVMs;

                _logger.LogInfo("Activity updated successfully. ActivityId: {ActivityId}, TenantId: {TenantId}", activity.Id, tenantId);
                return ApiResponse<object>.SuccessResponse(activityVM, "Activity updated successfully", 200);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Exception in UpdateActivity");
                return ApiResponse<object>.ErrorResponse("Error updating activity", ex.Message, 500);
            }
        }
        public async Task<ApiResponse<object>> DeleteActivityAsync(Guid id, bool isActive, Employee loggedInEmployee, Guid tenantId)
        {
            _logger.LogInfo("DeleteActivity called with ActivityId: {ActivityId}, IsActive: {IsActive}", id, isActive);

            try
            {
                // Step 1: Validate permission
                var hasPermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);

                if (!hasPermission)
                {
                    _logger.LogWarning("Access denied for EmployeeId: {EmployeeId}", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access denied", "You don't have permission to delete activities", 403);
                }

                // Step 2: Fetch the activity
                var activity = await _context.ActivityMasters
                    .FirstOrDefaultAsync(a => a.Id == id && a.TenantId == tenantId);

                if (activity == null)
                {
                    _logger.LogWarning("Activity not found. ActivityId: {ActivityId}", id);
                    return ApiResponse<object>.ErrorResponse("Activity not found", "Activity not found or already deleted", 404);
                }

                var existingEntityBson = _updateLogHelper.EntityToBsonDocument(activity);

                // Step 3: Perform soft delete/restore
                activity.IsActive = isActive;
                await _context.SaveChangesAsync();

                await _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject
                {
                    EntityId = activity.Id.ToString(),
                    UpdatedById = loggedInEmployee.Id.ToString(),
                    OldObject = existingEntityBson,
                    UpdatedAt = DateTime.UtcNow
                }, "ActivityMasterModificationLog");

                string status = isActive ? "restored" : "deactivated";
                _logger.LogInfo("Activity {ActivityId} {Status} successfully by EmployeeId: {EmployeeId}", id, status, loggedInEmployee.Id);

                return ApiResponse<object>.SuccessResponse(new { }, $"Activity {status} successfully", 200);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Unexpected error while deleting activity {ActivityId}", id);
                return ApiResponse<object>.ErrorResponse("Error deleting activity", ex.Message, 500);
            }
        }

        #endregion

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

        /// <summary>
        /// Retrieves the list of contact categories for the specified tenant.
        /// Ensures the tenantId is valid, logs relevant information and handles errors gracefully.
        /// </summary>
        /// <param name="loggedInEmployee">The employee making the request.</param>
        /// <param name="tenantId">The unique identifier for the tenant.</param>
        /// <returns>ApiResponse containing a list of contact categories.</returns>
        public async Task<ApiResponse<object>> GetContactCategoriesList(Employee loggedInEmployee, Guid tenantId)
        {
            // Validate parameters
            if (loggedInEmployee == null)
            {
                _logger.LogWarning("Attempt to fetch contact categories with null employee object");
                return ApiResponse<object>.ErrorResponse("Invalid employee context", "Invalid employee context", 400);
            }

            if (tenantId == Guid.Empty)
            {
                _logger.LogWarning("Attempt to fetch contact categories with empty tenantId by Employee ID {LoggedInEmployeeId}", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Invalid tenant identifier", "Invalid tenant identifier", 400);
            }

            try
            {
                // Fetch categories filtered by tenantId, ensuring no unnecessary tracking
                var categoryList = await _context.ContactCategoryMasters
                    .AsNoTracking()
                    .Where(c => c.TenantId == tenantId)
                    .ToListAsync();

                // Map database entities to view models
                var contactCategories = _mapper.Map<List<ContactCategoryVM>>(categoryList);
                int fetchedCount = contactCategories.Count;

                _logger.LogInfo("{Count} contact categories fetched for TenantId {TenantId} by Employee ID {EmployeeId}", fetchedCount, tenantId, loggedInEmployee.Id);
                return ApiResponse<object>.SuccessResponse(contactCategories, $"{fetchedCount} contact categories fetched successfully", 200);
            }
            catch (Exception ex)
            {
                // Log exception details with context
                _logger.LogError(ex, "Error fetching contact categories for TenantId {TenantId}, Employee ID {EmployeeId}", tenantId, loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("An unexpected error occurred while fetching categories", "An unexpected error occurred while fetching categories", 500);
            }
        }

        /// <summary>
        /// Retrieves a single contact category by its unique ID and associated tenant.
        /// Validates parameters, logs operations, and handles exceptions gracefully.
        /// </summary>
        /// <param name="id">Unique identifier for the contact category.</param>
        /// <param name="loggedInEmployee">Employee requesting the data.</param>
        /// <param name="tenantId">Unique identifier for the tenant.</param>
        /// <returns>ApiResponse with the contact category data, or error details.</returns>
        public async Task<ApiResponse<object>> GetContactCategoryById(Guid id, Employee loggedInEmployee, Guid tenantId)
        {
            // Validate required parameters
            if (loggedInEmployee == null)
            {
                _logger.LogWarning("Null employee object provided when fetching contact category {ContactCategoryID}", id);
                return ApiResponse<object>.ErrorResponse("Invalid employee context", "Invalid employee context", 400);
            }

            if (tenantId == Guid.Empty)
            {
                _logger.LogWarning("Empty tenantId provided by Employee {EmployeeId} when fetching contact category {ContactCategoryID}", loggedInEmployee.Id, id);
                return ApiResponse<object>.ErrorResponse("Invalid tenant identifier", "Invalid tenant identifier", 400);
            }

            if (id == Guid.Empty)
            {
                _logger.LogWarning("Empty contact category ID specified by Employee {EmployeeId}", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Invalid contact category ID", "Invalid contact category ID", 400);
            }

            try
            {
                // Efficient search for category, read-only query
                var category = await _context.ContactCategoryMasters
                    .AsNoTracking()
                    .FirstOrDefaultAsync(c => c.Id == id && c.TenantId == tenantId);

                if (category == null)
                {
                    _logger.LogWarning("Employee {EmployeeId} attempted to fetch contact category {ContactCategoryID} (TenantId {TenantId}), but it was not found", loggedInEmployee.Id, id, tenantId);
                    return ApiResponse<object>.ErrorResponse("Category not found", "Category not found", 404);
                }

                // Map database entity to ViewModel
                var categoryVM = _mapper.Map<ContactCategoryVM>(category);

                _logger.LogInfo("Employee {EmployeeId} fetched contact category {ContactCategoryID} (TenantId {TenantId}) successfully", loggedInEmployee.Id, category.Id, tenantId);
                return ApiResponse<object>.SuccessResponse(categoryVM, "Category fetched successfully", 200);
            }
            catch (Exception ex)
            {
                // Exception logging with relevant context
                _logger.LogError(ex, "Error fetching contact category {ContactCategoryID} for TenantId {TenantId} by Employee {EmployeeId}", id, tenantId, loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("An unexpected error occurred while fetching category", "An unexpected error occurred while fetching category", 500);
            }
        }

        /// <summary>
        /// Creates a new contact category for the specified tenant.
        /// Ensures the category name is unique within the tenant and logs all relevant actions.
        /// </summary>
        /// <param name="model">The DTO containing category creation data.</param>
        /// <param name="loggedInEmployee">The employee initiating the request.</param>
        /// <param name="tenantId">The tenant identifier to scope the operation.</param>
        /// <returns>ApiResponse containing the created category or error details.</returns>
        public async Task<ApiResponse<object>> CreateContactCategory(CreateContactCategoryDto model, Employee loggedInEmployee, Guid tenantId)
        {
            // Validate input parameters
            if (loggedInEmployee == null)
            {
                _logger.LogWarning("CreateContactCategory: Request with null employee context");
                return ApiResponse<object>.ErrorResponse("Invalid employee context", "Invalid employee context", 400);
            }

            if (tenantId == Guid.Empty)
            {
                _logger.LogWarning("CreateContactCategory: Invalid tenant ID {TenantId} provided by Employee {EmployeeId}", tenantId, loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Invalid tenant identifier", "Invalid tenant identifier", 400);
            }

            if (model == null)
            {
                _logger.LogWarning("Employee {EmployeeId} sent empty payload for contact category creation", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Request payload cannot be null", "Request payload cannot be null", 400);
            }

            // Trim and validate name to prevent duplicates due to whitespace
            // Trim and validate name
            string trimmedName = model.Name.Trim();
            if (string.IsNullOrWhiteSpace(trimmedName))
            {
                _logger.LogWarning("Employee {EmployeeId} attempted to create contact category with empty or whitespace-only name", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Category name is required", "Category name is required", 400);
            }

            try
            {
                // Check for existing category with same name in the tenant
                bool categoryExists = await _context.ContactCategoryMasters
                    .AnyAsync(c => c.TenantId == tenantId && c.Name == trimmedName);

                if (categoryExists)
                {
                    _logger.LogWarning("Employee {EmployeeId} attempted to create duplicate contact category with name '{CategoryName}' for Tenant {TenantId}",
                        loggedInEmployee.Id, trimmedName, tenantId);
                    return ApiResponse<object>.ErrorResponse("A category with this name already exists", "A category with this name already exists", 409);
                }

                // Map DTO to entity
                var contactCategory = new ContactCategoryMaster
                {
                    Id = Guid.NewGuid(), // Ensure new ID is generated
                    Name = trimmedName,
                    Description = model.Description.Trim(), // Normalize description
                    TenantId = tenantId
                };


                // Add and save to database
                _context.ContactCategoryMasters.Add(contactCategory);
                await _context.SaveChangesAsync();

                // Map to response model
                var categoryVM = _mapper.Map<ContactCategoryVM>(contactCategory);

                _logger.LogInfo("Contact category created successfully: ID {ContactCategoryId}, Name '{CategoryName}', Tenant {TenantId}, by Employee {EmployeeId}",
                    contactCategory.Id, contactCategory.Name, tenantId, loggedInEmployee.Id);

                return ApiResponse<object>.SuccessResponse(categoryVM, "Category created successfully", 201); // 201 Created
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error creating contact category for Tenant {TenantId} by Employee {EmployeeId}. Payload: {Payload}",
                    tenantId, loggedInEmployee.Id, model.Name);
                return ApiResponse<object>.ErrorResponse("An error occurred while creating the category", "An error occurred while creating the category", 500);
            }
        }

        /// <summary>
        /// Updates an existing contact category within the specified tenant.
        /// Validates ownership, ensures data integrity, logs changes, and supports audit tracking.
        /// </summary>
        /// <param name="id">The unique identifier of the contact category to update.</param>
        /// <param name="contactCategoryDto">The DTO containing updated category data.</param>
        /// <param name="loggedInEmployee">The employee initiating the update.</param>
        /// <param name="tenantId">The tenant identifier to scope the operation.</param>
        /// <returns>ApiResponse containing the updated category or error details.</returns>
        public async Task<ApiResponse<object>> UpdateContactCategory(Guid id, UpdateContactCategoryDto model, Employee loggedInEmployee, Guid tenantId)
        {
            // Validate input parameters
            if (loggedInEmployee == null)
            {
                _logger.LogWarning("UpdateContactCategory: Request with null employee context");
                return ApiResponse<object>.ErrorResponse("Invalid employee context", "Invalid employee context", 400);
            }

            if (tenantId == Guid.Empty)
            {
                _logger.LogWarning("UpdateContactCategory: Invalid tenant ID {TenantId} provided by Employee {EmployeeId}", tenantId, loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Invalid tenant identifier", "Invalid tenant identifier", 400);
            }

            if (id == Guid.Empty)
            {
                _logger.LogWarning("UpdateContactCategory: Invalid category ID {CategoryId} provided by Employee {EmployeeId}", id, loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Invalid category identifier", "Invalid category identifier", 400);
            }

            if (model == null)
            {
                _logger.LogWarning("Employee {EmployeeId} sent null DTO for updating contact category {CategoryId}", loggedInEmployee.Id, id);
                return ApiResponse<object>.ErrorResponse("Request payload cannot be null", "Request payload cannot be null", 400);
            }

            if (id != model.Id)
            {
                _logger.LogWarning("Employee {EmployeeId} attempted to update category {CategoryId} with mismatched DTO ID {DtoId}",
                    loggedInEmployee.Id, id, model.Id);
                return ApiResponse<object>.ErrorResponse("Category ID mismatch between route and payload", "Category ID mismatch between route and payload", 400);
            }

            try
            {
                // Fetch the existing category with tenant scoping
                var contactCategory = await _context.ContactCategoryMasters
                    .FirstOrDefaultAsync(c => c.Id == id && c.TenantId == tenantId);

                if (contactCategory == null)
                {
                    _logger.LogWarning("Employee {EmployeeId} attempted to update non-existent contact category {CategoryId} for Tenant {TenantId}",
                        loggedInEmployee.Id, id, tenantId);
                    return ApiResponse<object>.ErrorResponse("Category not found", "Category not found", 404);
                }

                // Trim and validate name
                string trimmedName = model.Name.Trim();
                if (string.IsNullOrWhiteSpace(trimmedName))
                {
                    _logger.LogWarning("Employee {EmployeeId} attempted to update category {CategoryId} with empty or whitespace-only name",
                        loggedInEmployee.Id, id);
                    return ApiResponse<object>.ErrorResponse("Category name is required", "Category name is required", 400);
                }

                // Check for duplicate name within tenant (excluding current category)
                bool nameExists = await _context.ContactCategoryMasters
                    .AnyAsync(c => c.TenantId == tenantId && c.Name == trimmedName && c.Id != id);

                if (nameExists)
                {
                    _logger.LogWarning("Employee {EmployeeId} attempted to rename category {CategoryId} to '{NewName}', which already exists in Tenant {TenantId}",
                        loggedInEmployee.Id, id, trimmedName, tenantId);
                    return ApiResponse<object>.ErrorResponse("A category with this name already exists", "A category with this name already exists", 409);
                }

                // Capture original state for audit log
                var existingEntityBson = _updateLogHelper.EntityToBsonDocument(contactCategory);

                // Update entity properties
                contactCategory.Name = trimmedName;
                contactCategory.Description = model.Description.Trim(); // Normalize description

                // Log update in directory and audit trail
                _context.DirectoryUpdateLogs.Add(new DirectoryUpdateLog
                {
                    RefereanceId = contactCategory.Id,
                    UpdatedById = loggedInEmployee.Id,
                    UpdateAt = DateTime.UtcNow
                });

                await _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject
                {
                    EntityId = contactCategory.Id.ToString(),
                    UpdatedById = loggedInEmployee.Id.ToString(),
                    OldObject = existingEntityBson,
                    UpdatedAt = DateTime.UtcNow
                }, "ContactCategoryMasterModificationLog");

                // Save changes to database
                await _context.SaveChangesAsync();

                // Map to response model
                var categoryVM = _mapper.Map<ContactCategoryVM>(contactCategory);

                _logger.LogInfo("Contact category updated successfully: ID {ContactCategoryId}, Name '{CategoryName}', Tenant {TenantId}, by Employee {EmployeeId}",
                    contactCategory.Id, contactCategory.Name, tenantId, loggedInEmployee.Id);

                return ApiResponse<object>.SuccessResponse(categoryVM, "Category updated successfully", 200);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error updating contact category {CategoryId} for Tenant {TenantId} by Employee {EmployeeId}",
                    id, tenantId, loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("An error occurred while updating the category", "An error occurred while updating the category", 500);
            }
        }

        /// <summary>
        /// Deletes a contact category by ID after ensuring it's not in use.
        /// Orphaned contacts have their category reference cleared. Full audit trail is maintained.
        /// </summary>
        /// <param name="id">The unique identifier of the contact category to delete.</param>
        /// <param name="loggedInEmployee">The employee initiating the deletion.</param>
        /// <param name="tenantId">The tenant identifier to scope the operation.</param>
        /// <returns>ApiResponse indicating success or failure.</returns>
        public async Task<ApiResponse<object>> DeleteContactCategory(Guid id, Employee loggedInEmployee, Guid tenantId)
        {
            // Validate input parameters
            if (loggedInEmployee == null)
            {
                _logger.LogWarning("DeleteContactCategory: Request with null employee context");
                return ApiResponse<object>.ErrorResponse("Invalid employee context", "Invalid employee context", 400);
            }

            if (tenantId == Guid.Empty)
            {
                _logger.LogWarning("DeleteContactCategory: Invalid tenant ID {TenantId} provided by Employee {EmployeeId}", tenantId, loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Invalid tenant identifier", "Invalid tenant identifier", 400);
            }

            if (id == Guid.Empty)
            {
                _logger.LogWarning("DeleteContactCategory: Invalid category ID {CategoryId} provided by Employee {EmployeeId}", id, loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Invalid category identifier", "Invalid category identifier", 400);
            }

            try
            {
                // Retrieve the category to delete with tenant scoping
                var contactCategory = await _context.ContactCategoryMasters
                    .FirstOrDefaultAsync(c => c.Id == id && c.TenantId == tenantId);

                if (contactCategory == null)
                {
                    _logger.LogWarning("Employee {EmployeeId} attempted to delete non-existent contact category {CategoryId} for Tenant {TenantId}",
                        loggedInEmployee.Id, id, tenantId);
                    return ApiResponse<object>.ErrorResponse("Category not found", "Category not found", 404);
                }

                // Check for associated contacts and update them in bulk
                var hasAssociatedContacts = await _context.Contacts
                    .AnyAsync(c => c.ContactCategoryId == id && c.TenantId == tenantId);

                if (hasAssociatedContacts)
                {
                    // Bulk update: Set ContactCategoryId to null for all related contacts
                    var rowsAffected = await _context.Contacts
                        .Where(c => c.ContactCategoryId == id && c.TenantId == tenantId)
                        .ExecuteUpdateAsync(setters => setters.SetProperty(c => c.ContactCategoryId, (Guid?)null));

                    _logger.LogInfo("Cleared ContactCategoryId for {RowCount} contacts previously linked to category {CategoryId}",
                        rowsAffected, id);
                }

                // Capture original state for audit log before deletion
                var existingEntityBson = _updateLogHelper.EntityToBsonDocument(contactCategory);

                // Remove the category
                _context.ContactCategoryMasters.Remove(contactCategory);

                // Log deletion in directory update log
                _context.DirectoryUpdateLogs.Add(new DirectoryUpdateLog
                {
                    RefereanceId = id,
                    UpdatedById = loggedInEmployee.Id,
                    UpdateAt = DateTime.UtcNow
                });

                // Save all changes to database
                await _context.SaveChangesAsync();

                // Push audit log to external store (e.g., MongoDB)
                await _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject
                {
                    EntityId = id.ToString(),
                    UpdatedById = loggedInEmployee.Id.ToString(),
                    OldObject = existingEntityBson,
                    UpdatedAt = DateTime.UtcNow
                }, "ContactCategoryMasterModificationLog");

                _logger.LogInfo("Contact category deleted successfully: ID {ContactCategoryId}, Tenant {TenantId}, by Employee {EmployeeId}",
                    id, tenantId, loggedInEmployee.Id);

                return ApiResponse<object>.SuccessResponse(new { }, "Category deleted successfully", 200);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error deleting contact category {CategoryId} for Tenant {TenantId} by Employee {EmployeeId}",
                    id, tenantId, loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("An error occurred while deleting the category", "An error occurred while deleting the category", 500);
            }
        }

        #endregion

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

        /// <summary>
        /// Retrieves all contact tags for the specified tenant.
        /// Returns a list of active tags mapped to view models with full audit logging.
        /// </summary>
        /// <param name="loggedInEmployee">The employee making the request.</param>
        /// <param name="tenantId">The unique identifier for the tenant.</param>
        /// <returns>ApiResponse containing the list of contact tags or error details.</returns>
        public async Task<ApiResponse<object>> GetContactTags(Employee loggedInEmployee, Guid tenantId)
        {
            // Validate input parameters
            if (loggedInEmployee == null)
            {
                _logger.LogWarning("GetContactTags: Request with null employee context");
                return ApiResponse<object>.ErrorResponse("Invalid employee context", "Invalid employee context", 400);
            }

            if (tenantId == Guid.Empty)
            {
                _logger.LogWarning("GetContactTags: Invalid tenant ID {TenantId} provided by Employee {EmployeeId}", tenantId, loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Invalid tenant identifier", "Invalid tenant identifier", 400);
            }

            try
            {
                // Fetch tags with tenant filtering and no tracking (read-only operation)
                var tagList = await _context.ContactTagMasters
                    .AsNoTracking()
                    .Where(t => t.TenantId == tenantId)
                    .ToListAsync();

                // Map to view models
                var contactTags = _mapper.Map<List<ContactTagVM>>(tagList);
                int tagCount = contactTags.Count;

                // Log successful retrieval with context
                _logger.LogInfo("{TagCount} contact tags fetched for Tenant {TenantId} by Employee {EmployeeId}", tagCount, tenantId, loggedInEmployee.Id);
                return ApiResponse<object>.SuccessResponse(contactTags, $"{tagCount} contact tags fetched successfully", 200);
            }
            catch (Exception ex)
            {
                // Log any unexpected errors with full context
                _logger.LogError(ex, "Error fetching contact tags for Tenant {TenantId} by Employee {EmployeeId}", tenantId, loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("An error occurred while retrieving contact tags", "An error occurred while retrieving contact tags", 500);
            }
        }

        /// <summary>
        /// Creates a new contact tag for the specified tenant.
        /// Ensures name uniqueness within the tenant, logs all actions, and supports auditability.
        /// </summary>
        /// <param name="model">The DTO containing tag creation data.</param>
        /// <param name="loggedInEmployee">The employee initiating the request.</param>
        /// <param name="tenantId">The tenant identifier to scope the operation.</param>
        /// <returns>ApiResponse containing the created tag or error details.</returns>
        public async Task<ApiResponse<object>> CreateContactTag(CreateContactTagDto model, Employee loggedInEmployee, Guid tenantId)
        {
            // Validate input parameters
            if (loggedInEmployee == null)
            {
                _logger.LogWarning("CreateContactTag: Request with null employee context");
                return ApiResponse<object>.ErrorResponse("Invalid employee context", "Invalid employee context", 400);
            }

            if (tenantId == Guid.Empty)
            {
                _logger.LogWarning("CreateContactTag: Invalid tenant ID {TenantId} provided by Employee {EmployeeId}", tenantId, loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Invalid tenant identifier", "Invalid tenant identifier", 400);
            }

            if (model == null)
            {
                _logger.LogWarning("Employee {EmployeeId} sent empty payload for contact tag creation", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Request payload cannot be null", "Request payload cannot be null", 400);
            }

            // Trim and validate name
            string trimmedName = model.Name.Trim();
            if (string.IsNullOrWhiteSpace(trimmedName))
            {
                _logger.LogWarning("Employee {EmployeeId} attempted to create contact tag with empty or whitespace-only name", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Tag name is required", "Tag name is required", 400);
            }

            try
            {
                // Check for existing tag with same name in the tenant
                bool tagExists = await _context.ContactTagMasters
                    .AnyAsync(t => t.TenantId == tenantId && t.Name == trimmedName);

                if (tagExists)
                {
                    _logger.LogWarning("Employee {EmployeeId} attempted to create duplicate contact tag with name '{TagName}' for Tenant {TenantId}",
                        loggedInEmployee.Id, trimmedName, tenantId);
                    return ApiResponse<object>.ErrorResponse("A tag with this name already exists", "A tag with this name already exists", 409);
                }

                // Create new tag entity
                var contactTag = new ContactTagMaster
                {
                    Id = Guid.NewGuid(),
                    Name = trimmedName,
                    Description = model.Description.Trim(), // Normalize description
                    TenantId = tenantId,
                };

                // Add and save to database
                _context.ContactTagMasters.Add(contactTag);
                await _context.SaveChangesAsync();

                // Map to response model
                var tagVM = _mapper.Map<ContactTagVM>(contactTag);

                // Log successful creation with full context
                _logger.LogInfo("Contact tag created successfully: ID {ContactTagId}, Name '{TagName}', Tenant {TenantId}, by Employee {EmployeeId}",
                    contactTag.Id, contactTag.Name, tenantId, loggedInEmployee.Id);

                return ApiResponse<object>.SuccessResponse(tagVM, "Tag created successfully", 201); // 201 Created
            }
            catch (Exception ex)
            {
                // Log any unexpected errors with full context
                _logger.LogError(ex, "Error creating contact tag for Tenant {TenantId} by Employee {EmployeeId}. Payload: {TagName}",
                    tenantId, loggedInEmployee.Id, model.Name);
                return ApiResponse<object>.ErrorResponse("An error occurred while creating the tag", "An error occurred while creating the tag", 500);
            }
        }

        /// <summary>
        /// Updates an existing contact tag within the specified tenant.
        /// Ensures data integrity, prevents name conflicts, and maintains a full audit trail.
        /// </summary>
        /// <param name="id">The unique identifier of the contact tag to update.</param>
        /// <param name="model">The DTO containing updated tag data.</param>
        /// <param name="loggedInEmployee">The employee initiating the update.</param>
        /// <param name="tenantId">The tenant identifier to scope the operation.</param>
        /// <returns>ApiResponse containing the updated tag or error details.</returns>
        public async Task<ApiResponse<object>> UpdateContactTag(Guid id, UpdateContactTagDto model, Employee loggedInEmployee, Guid tenantId)
        {
            // Validate input parameters
            if (loggedInEmployee == null)
            {
                _logger.LogWarning("UpdateContactTag: Request with null employee context");
                return ApiResponse<object>.ErrorResponse("Invalid employee context", "Invalid employee context", 400);
            }

            if (tenantId == Guid.Empty)
            {
                _logger.LogWarning("UpdateContactTag: Invalid tenant ID {TenantId} provided by Employee {EmployeeId}", tenantId, loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Invalid tenant identifier", "Invalid tenant identifier", 400);
            }

            if (id == Guid.Empty)
            {
                _logger.LogWarning("UpdateContactTag: Invalid tag ID {TagId} provided by Employee {EmployeeId}", id, loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Invalid tag identifier", "Invalid tag identifier", 400);
            }

            if (model == null)
            {
                _logger.LogWarning("Employee {EmployeeId} sent null DTO for updating contact tag {TagId}", loggedInEmployee.Id, id);
                return ApiResponse<object>.ErrorResponse("Request payload cannot be null", "Request payload cannot be null", 400);
            }

            if (model.Id != id)
            {
                _logger.LogWarning("Employee {EmployeeId} attempted to update tag {TagId} with mismatched DTO ID {DtoId}",
                    loggedInEmployee.Id, id, model.Id);
                return ApiResponse<object>.ErrorResponse("Tag ID mismatch between route and payload", "Tag ID mismatch between route and payload", 400);
            }

            try
            {
                // Fetch the existing tag with tenant scoping
                var contactTag = await _context.ContactTagMasters
                    .FirstOrDefaultAsync(t => t.Id == id && t.TenantId == tenantId);

                if (contactTag == null)
                {
                    _logger.LogWarning("Employee {EmployeeId} attempted to update non-existent contact tag {TagId} for Tenant {TenantId}",
                        loggedInEmployee.Id, id, tenantId);
                    return ApiResponse<object>.ErrorResponse("Contact tag not found", "Contact tag not found", 404);
                }

                // Trim and validate name
                string trimmedName = (model.Name ?? string.Empty).Trim();
                if (string.IsNullOrWhiteSpace(trimmedName))
                {
                    _logger.LogWarning("Employee {EmployeeId} attempted to update tag {TagId} with empty or whitespace-only name",
                        loggedInEmployee.Id, id);
                    return ApiResponse<object>.ErrorResponse("Tag name is required", "Tag name is required", 400);
                }

                // Check for duplicate name within tenant (excluding current tag)
                bool nameExists = await _context.ContactTagMasters
                    .AnyAsync(t => t.TenantId == tenantId && t.Name == trimmedName && t.Id != id);

                if (nameExists)
                {
                    _logger.LogWarning("Employee {EmployeeId} attempted to rename tag {TagId} to '{NewName}', which already exists in Tenant {TenantId}",
                        loggedInEmployee.Id, id, trimmedName, tenantId);
                    return ApiResponse<object>.ErrorResponse("A tag with this name already exists", "A tag with this name already exists", 409);
                }

                // Capture original state for audit log
                var existingEntityBson = _updateLogHelper.EntityToBsonDocument(contactTag);

                // Update entity properties
                contactTag.Name = trimmedName;
                contactTag.Description = model.Description.Trim(); // Normalize description

                // Log update in directory and audit trail
                _context.DirectoryUpdateLogs.Add(new DirectoryUpdateLog
                {
                    RefereanceId = contactTag.Id,
                    UpdatedById = loggedInEmployee.Id,
                    UpdateAt = DateTime.UtcNow
                });

                await _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject
                {
                    EntityId = contactTag.Id.ToString(),
                    UpdatedById = loggedInEmployee.Id.ToString(),
                    OldObject = existingEntityBson,
                    UpdatedAt = DateTime.UtcNow
                }, "ContactTagMasterModificationLog");

                // Save changes to database
                await _context.SaveChangesAsync();

                // Map to response model
                var contactTagVm = _mapper.Map<ContactTagVM>(contactTag);

                _logger.LogInfo("Contact tag updated successfully: ID {ContactTagId}, Name '{TagName}', Tenant {TenantId}, by Employee {EmployeeId}",
                    contactTag.Id, contactTag.Name, tenantId, loggedInEmployee.Id);

                return ApiResponse<object>.SuccessResponse(contactTagVm, "Contact tag updated successfully", 200);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error updating contact tag {TagId} for Tenant {TenantId} by Employee {EmployeeId}",
                    id, tenantId, loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("An error occurred while updating the tag", "An error occurred while updating the tag", 500);
            }
        }

        /// <summary>
        /// Deletes a contact tag by ID after removing all associated tag mappings.
        /// Maintains referential integrity and full audit trail for compliance and traceability.
        /// </summary>
        /// <param name="id">The unique identifier of the contact tag to delete.</param>
        /// <param name="loggedInEmployee">The employee initiating the deletion.</param>
        /// <param name="tenantId">The tenant identifier to scope the operation.</param>
        /// <returns>ApiResponse indicating success or failure.</returns>
        public async Task<ApiResponse<object>> DeleteContactTag(Guid id, Employee loggedInEmployee, Guid tenantId)
        {
            // Validate input parameters
            if (loggedInEmployee == null)
            {
                _logger.LogWarning("DeleteContactTag: Request with null employee context");
                return ApiResponse<object>.ErrorResponse("Invalid employee context", "Invalid employee context", 400);
            }

            if (tenantId == Guid.Empty)
            {
                _logger.LogWarning("DeleteContactTag: Invalid tenant ID {TenantId} provided by Employee {EmployeeId}", tenantId, loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Invalid tenant identifier", "Invalid tenant identifier", 400);
            }

            if (id == Guid.Empty)
            {
                _logger.LogWarning("DeleteContactTag: Invalid tag ID {TagId} provided by Employee {EmployeeId}", id, loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Invalid tag identifier", "Invalid tag identifier", 400);
            }

            try
            {
                // Retrieve the tag to delete with tenant scoping
                var contactTag = await _context.ContactTagMasters
                    .FirstOrDefaultAsync(t => t.Id == id && t.TenantId == tenantId);

                if (contactTag == null)
                {
                    _logger.LogWarning("Employee {EmployeeId} attempted to delete non-existent contact tag {TagId} for Tenant {TenantId}",
                        loggedInEmployee.Id, id, tenantId);
                    return ApiResponse<object>.ErrorResponse("Contact tag not found", "Contact tag not found", 404);
                }

                // Capture original state for audit log before deletion
                var existingEntityBson = _updateLogHelper.EntityToBsonDocument(contactTag);

                // Remove all associated tag mappings in bulk
                var mappingsExist = await _context.ContactTagMappings
                    .AnyAsync(m => m.ContactTagId == id);

                if (mappingsExist)
                {
                    var rowsAffected = await _context.ContactTagMappings
                        .Where(m => m.ContactTagId == id)
                        .ExecuteDeleteAsync();

                    _logger.LogInfo("Deleted {RowCount} contact tag mappings associated with tag {TagId}", rowsAffected, id);
                }

                // Log deletion in directory update log
                _context.DirectoryUpdateLogs.Add(new DirectoryUpdateLog
                {
                    RefereanceId = id,
                    UpdatedById = loggedInEmployee.Id,
                    UpdateAt = DateTime.UtcNow
                });

                // Remove the tag
                _context.ContactTagMasters.Remove(contactTag);

                // Save all changes to database
                await _context.SaveChangesAsync();

                // Push audit log to external store (e.g., MongoDB)
                await _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject
                {
                    EntityId = id.ToString(),
                    UpdatedById = loggedInEmployee.Id.ToString(),
                    OldObject = existingEntityBson,
                    UpdatedAt = DateTime.UtcNow
                }, "ContactTagMasterModificationLog");

                _logger.LogInfo("Contact tag deleted successfully: ID {ContactTagId}, Tenant {TenantId}, by Employee {EmployeeId}",
                    id, tenantId, loggedInEmployee.Id);

                return ApiResponse<object>.SuccessResponse(new { }, "Tag deleted successfully", 200);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error deleting contact tag {TagId} for Tenant {TenantId} by Employee {EmployeeId}",
                    id, tenantId, loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("An error occurred while deleting the tag", "An error occurred while deleting the tag", 500);
            }
        }

        #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 model, Employee loggedInEmployee, Guid tenantId)
        {
            _logger.LogInfo("CreateWorkStatus called with Name: {Name}", model.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 == model.Name && ws.TenantId == tenantId);

                if (existingWorkStatus != null)
                {
                    _logger.LogWarning("Work status already exists: {Name}", model.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 = model.Name.Trim(),
                    Description = model.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 model, Employee loggedInEmployee, Guid tenantId)
        {
            _logger.LogInfo("UpdateWorkStatus called for WorkStatus ID: {Id}, New Name: {Name}", id, model.Name);

            try
            {
                // Step 1: Validate input
                if (id == Guid.Empty || id != model.Id)
                {
                    _logger.LogWarning("Invalid ID provided for update. Route ID: {RouteId}, DTO ID: {DtoId}", id, model.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 == model.Name.Trim() && ws.Id != id && ws.TenantId == tenantId);

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

                // Capture original state for audit log
                var existingEntityBson = _updateLogHelper.EntityToBsonDocument(workStatus);

                // Step 5: Update fields
                workStatus.Name = model.Name.Trim();
                workStatus.Description = model.Description.Trim();

                await _context.SaveChangesAsync();

                await _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject
                {
                    EntityId = workStatus.Id.ToString(),
                    UpdatedById = loggedInEmployee.Id.ToString(),
                    OldObject = existingEntityBson,
                    UpdatedAt = DateTime.UtcNow
                }, "WorkStatusMasterModificationLog");

                _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
                    );
                }

                // Capture original state for audit log
                var existingEntityBson = _updateLogHelper.EntityToBsonDocument(workStatus);

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

                await _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject
                {
                    EntityId = workStatus.Id.ToString(),
                    UpdatedById = loggedInEmployee.Id.ToString(),
                    OldObject = existingEntityBson,
                    UpdatedAt = DateTime.UtcNow
                }, "WorkStatusMasterModificationLog");

                _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 Category APIs ===================================================================

        public async Task<ApiResponse<object>> GetExpenseCategoryListAsync(Employee loggedInEmployee, Guid tenantId, bool isActive)
        {
            try
            {
                // Featching the list of Expenses Category.
                var categories = await _context.ExpenseCategoryMasters.Where(et => et.TenantId == tenantId && et.IsActive == isActive).ToListAsync();
                var response = _mapper.Map<List<ExpenseCategoryMasterVM>>(categories);

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

        }
        public async Task<ApiResponse<object>> CreateExpenseCategoryAsync(ExpenseCategoryMasterDto model, Employee loggedInEmployee, Guid tenantId)
        {
            try
            {
                var hasManagePermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);
                if (!hasManagePermission)
                {
                    _logger.LogWarning("Access DENIED for employee {EmployeeId} for managing EXPANSES CATEGORY MASTER.", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to Manage masters", 403);
                }
                var expensesCategory = _mapper.Map<ExpenseCategoryMaster>(model);
                expensesCategory.TenantId = tenantId;

                _context.ExpenseCategoryMasters.Add(expensesCategory);
                await _context.SaveChangesAsync();

                _logger.LogInfo("New Expense Category {ExpensesCategoryId} was added by employee {EmployeeId}", expensesCategory.Id, loggedInEmployee.Id);

                var response = _mapper.Map<ExpenseCategoryMasterVM>(expensesCategory);
                return ApiResponse<object>.SuccessResponse(response, "Expense category craeted Successfully", 201);
            }
            catch (DbUpdateException dbEx)
            {
                _logger.LogError(dbEx, "Database Exception occured while adding new expense 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 expense category by employee {EmployeeId}", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(ex), 500);
            }
        }
        public async Task<ApiResponse<object>> UpdateExpenseCategoryAsync(Guid id, ExpenseCategoryMasterDto model, Employee loggedInEmployee, Guid tenantId)
        {
            try
            {
                // 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 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 expenseCategory = await _context.ExpenseCategoryMasters.AsNoTracking().FirstOrDefaultAsync(et => et.Id == model.Id.Value && et.TenantId == tenantId);

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

                // Mapping ExpenseCategoryMaster to BsonDocument
                var existingEntityBson = _updateLogHelper.EntityToBsonDocument(expenseCategory);

                // Mapping ExpenseCategoryMasterDto to ExpenseCategoryMaster
                _mapper.Map(model, expenseCategory);

                _context.ExpenseCategoryMasters.Update(expenseCategory);
                await _context.SaveChangesAsync();

                _logger.LogInfo("Expense Category {ExpensesCategoryId} was updated by employee {EmployeeId}", expenseCategory.Id, loggedInEmployee.Id);

                // Saving the old entity in mongoDB

                await _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject
                {
                    EntityId = expenseCategory.Id.ToString(),
                    UpdatedById = loggedInEmployee.Id.ToString(),
                    OldObject = existingEntityBson,
                    UpdatedAt = DateTime.UtcNow
                }, "ExpensesCategoryMasterModificationLog");

                // Mapping ExpenseCategoryMaster to ExpenseCategoryMasterVM
                var response = _mapper.Map<ExpenseCategoryMasterVM>(expenseCategory);
                return ApiResponse<object>.SuccessResponse(response, "Expense category updated Successfully", 200);
            }
            catch (DbUpdateException dbEx)
            {
                _logger.LogError(dbEx, "Database Exception occured while updating expense 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 expense category by employee {EmployeeId}", loggedInEmployee.Id);
                return ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(ex), 500);
            }
        }
        public async Task<ApiResponse<object>> DeleteExpenseCategoryAsync(Guid id, bool isActive, Employee loggedInEmployee, Guid tenantId)
        {
            string action = isActive ? "restore" : "delete";
            try
            {
                // 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 CATEGORY MASTER.", loggedInEmployee.Id);
                    return ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to Manage masters", 403);
                }

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

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

                // Mapping ExpenseCategoryMaster to BsonDocument
                var existingEntityBson = _updateLogHelper.EntityToBsonDocument(expenseCategory);

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

                _logger.LogInfo("Expense Category {ExpensesCategoryId} was {Action}d by employee {EmployeeId}", expenseCategory.Id, action, loggedInEmployee.Id);

                // Saving the old entity in mongoDB

                await _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject
                {
                    EntityId = expenseCategory.Id.ToString(),
                    UpdatedById = loggedInEmployee.Id.ToString(),
                    OldObject = existingEntityBson,
                    UpdatedAt = DateTime.UtcNow
                }, "ExpensesCategoryMasterModificationLog");

                // Mapping ExpenseCategoryMaster to ExpenseCategoryMasterVM
                var response = _mapper.Map<ExpenseCategoryMasterVM>(expenseCategory);
                return ApiResponse<object>.SuccessResponse(response, $"Expense category {action}d Successfully", 200);
            }
            catch (DbUpdateException dbEx)
            {
                _logger.LogError(dbEx, "Database Exception occured while {Action}ing expense category 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 category 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
            {
                // 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
            {
                // Featching the list of Payment Modes.
                var paymentModes = await _context.PaymentModeMatser.Where(pm => 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
            {
                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);

                _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
            {
                // 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);

                // 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

                await _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
            {
                // 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);

                // 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

                await _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
            {
                // ✅ 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
            {
                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
            {
                // 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

                await _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
            {
                // 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

                await _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
            {
                // ✅ 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
            {
                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
            {
                // 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

                await _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
            {
                // 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

                await _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 =================================================================== Payment Adjustment Head APIs ===================================================================

        /// <summary>
        /// Retrieves a list of payment adjustment heads for a specific tenant with optional active status filtering.
        /// </summary>
        /// <param name="isActive">Filter for active/inactive payment adjustment heads</param>
        /// <param name="loggedInEmployee">The employee making the request (for auditing/authorization)</param>
        /// <param name="tenantId">The tenant identifier to scope the data</param>
        /// <returns>An API response containing the list of payment adjustment head view models</returns>
        /// <remarks>
        /// This method performs database-level filtering and uses projection to minimize data transfer.
        /// Consider implementing pagination for tenants with large numbers of payment adjustment heads.
        /// </remarks>
        public async Task<ApiResponse<object>> GetPaymentAdjustmentHeadListAsync(bool isActive, Employee loggedInEmployee, Guid tenantId)
        {
            try
            {
                // Log the request details for auditing and troubleshooting
                _logger.LogInfo("Fetching payment adjustment heads for tenant {TenantId} with IsActive={IsActive}", tenantId, isActive);

                var paymentAdjustmentHeads = await _context.PaymentAdjustmentHeads
                    .AsNoTracking() // Improve performance by disabling change tracking for read-only operations
                    .Where(pah => pah.TenantId == tenantId && pah.IsActive == isActive)
                    .Select(pah => _mapper.Map<PaymentAdjustmentHeadVM>(pah))
                    .ToListAsync();

                _logger.LogInfo("Successfully retrieved {Count} payment adjustment heads for tenant {TenantId}", paymentAdjustmentHeads.Count, tenantId);

                return ApiResponse<object>.SuccessResponse(
                    paymentAdjustmentHeads.OrderBy(pah => pah.Name).ToList(),
                    $"Payment Adjustment Heads fetched successfully. Count: {paymentAdjustmentHeads.Count}",
                    200);
            }
            catch (Exception ex)
            {
                // Log the full exception with context for better troubleshooting
                _logger.LogError(ex, "Error occurred while fetching payment adjustment heads for tenant {TenantId}. IsActive: {IsActive}", tenantId, isActive);
                return ApiResponse<object>.ErrorResponse("An error occurred", "Unable to fetch payment adjustment heads", 500);
            }
        }

        /// <summary>
        /// Creates a new payment adjustment head for the specified tenant after permission and uniqueness checks.
        /// </summary>
        /// <param name="model">DTO containing payment adjustment head data</param>
        /// <param name="loggedInEmployee">The employee performing the action</param>
        /// <param name="tenantId">The tenant identifier</param>
        /// <returns>API response with status and context</returns>
        public async Task<ApiResponse<object>> CreatePaymentAdjustmentHeadAsync(PaymentAdjustmentHeadDto model, Employee loggedInEmployee, Guid tenantId)
        {
            try
            {
                // Permission validation
                var hasManagePermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);
                if (!hasManagePermission)
                {
                    _logger.LogWarning("Access denied for employee {EmployeeId} attempting to create payment adjustment head for tenant {TenantId}.", loggedInEmployee.Id, tenantId);
                    return ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to manage masters.", 403);
                }

                // Uniqueness check for payment adjustment head name
                var nameExists = await _context.PaymentAdjustmentHeads
                    .AnyAsync(pah => pah.Name == model.Name && pah.TenantId == tenantId);
                if (nameExists)
                {
                    _logger.LogInfo("Duplicate payment adjustment head name '{Name}' detected for tenant {TenantId}.", model.Name, tenantId);
                    return ApiResponse<object>.ErrorResponse("A payment adjustment head with this name already exists.", "Name of payment adjustment head already exists.", 409);
                }

                // Create and persist new entity
                var paymentAdjustmentHead = _mapper.Map<PaymentAdjustmentHead>(model);
                paymentAdjustmentHead.IsActive = true;
                paymentAdjustmentHead.TenantId = tenantId;

                _context.PaymentAdjustmentHeads.Add(paymentAdjustmentHead);
                await _context.SaveChangesAsync();

                var response = _mapper.Map<PaymentAdjustmentHeadVM>(paymentAdjustmentHead);

                _logger.LogInfo("Payment adjustment head '{Name}' created successfully by employee {EmployeeId} for tenant {TenantId}.", paymentAdjustmentHead.Name, loggedInEmployee.Id, tenantId);

                return ApiResponse<object>.SuccessResponse(response, "Payment adjustment head created successfully.", 201);
            }
            catch (Exception ex)
            {
                // Log full context with exception for easier debugging
                _logger.LogError(ex, "Exception while creating payment adjustment head. Employee: {EmployeeId}, Tenant: {TenantId}, Data: {@Model}", loggedInEmployee.Id, tenantId, model);
                return ApiResponse<object>.ErrorResponse("An error occurred.", "Unable to create payment adjustment head at this moment.", 500);
            }
        }

        /// <summary>
        /// Updates an existing payment adjustment head for a specified tenant after performing
        /// necessary validation, permission, and uniqueness checks.
        /// </summary>
        /// <param name="id">Unique identifier of the payment adjustment head to update</param>
        /// <param name="model">DTO containing updated payment adjustment head data</param>
        /// <param name="loggedInEmployee">The employee performing the action</param>
        /// <param name="tenantId">The tenant identifier</param>
        /// <returns>API response object with update result and status message</returns>
        public async Task<ApiResponse<object>> UpdatePaymentAdjustmentHeadAsync(Guid id, PaymentAdjustmentHeadDto model, Employee loggedInEmployee, Guid tenantId)
        {
            try
            {
                // --- Step 1: Validate request payload correctness ---
                if (!model.Id.HasValue || model.Id != id)
                {
                    _logger.LogWarning("Invalid ID provided in request. Model ID: {ModelId}, Route ID: {RouteId}, TenantId: {TenantId}", model.Id ?? Guid.Empty, id, tenantId);
                    return ApiResponse<object>.ErrorResponse("Invalid request.", "Provided invalid ID.", 400);
                }

                // --- Step 2: Validate permissions ---
                var hasManagePermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);
                if (!hasManagePermission)
                {
                    _logger.LogWarning("Access denied for employee {EmployeeId} attempting to update payment adjustment head for tenant {TenantId}.", loggedInEmployee.Id, tenantId);
                    return ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to manage masters.", 403);
                }

                // --- Step 3: Validate uniqueness constraint for name ---
                var nameExists = await _context.PaymentAdjustmentHeads
                    .AnyAsync(pah => pah.Name == model.Name && pah.Id != id && pah.TenantId == tenantId);

                if (nameExists)
                {
                    _logger.LogInfo("Duplicate payment adjustment head name '{Name}' detected during update for tenant {TenantId}.", model.Name, tenantId);
                    return ApiResponse<object>.ErrorResponse("Conflict detected.", "A payment adjustment head with this name already exists.", 409);
                }

                // --- Step 4: Retrieve and validate existing entity ---
                var paymentAdjustmentHead = await _context.PaymentAdjustmentHeads
                    .FirstOrDefaultAsync(pah => pah.Id == id && pah.TenantId == tenantId);

                if (paymentAdjustmentHead == null)
                {
                    _logger.LogWarning("Payment adjustment head with ID {Id} not found for tenant {TenantId}.", id, tenantId);
                    return ApiResponse<object>.ErrorResponse("Not Found.", "Payment adjustment head not found.", 404);
                }

                // Mapping PaymentAdjustmentHead to BsonDocument
                var existingEntityBson = _updateLogHelper.EntityToBsonDocument(paymentAdjustmentHead);

                // --- Step 5: Map changes and update entity ---
                _mapper.Map(model, paymentAdjustmentHead);

                await _context.SaveChangesAsync();

                await _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject
                {
                    EntityId = paymentAdjustmentHead.Id.ToString(),
                    UpdatedById = loggedInEmployee.Id.ToString(),
                    OldObject = existingEntityBson,
                    UpdatedAt = DateTime.UtcNow
                }, "PaymentAdjustmentHeadModificationLog");

                _logger.LogInfo("Payment adjustment head '{Name}' updated successfully by employee {EmployeeId} for tenant {TenantId}.", paymentAdjustmentHead.Name, loggedInEmployee.Id, tenantId);

                var response = _mapper.Map<PaymentAdjustmentHeadVM>(paymentAdjustmentHead);

                // --- Step 6: Return structured success response ---
                return ApiResponse<object>.SuccessResponse(response, "Payment adjustment head updated successfully.", 200);
            }
            catch (Exception ex)
            {
                // --- Step 7: Handle and log exceptions with full context ---
                _logger.LogError(ex, "Exception while updating payment adjustment head. Employee: {EmployeeId}, Tenant: {TenantId}, Model: {@Model}", loggedInEmployee.Id, tenantId, model);

                return ApiResponse<object>.ErrorResponse("An unexpected error occurred.", "Unable to update payment adjustment head at this moment.", 500);
            }
        }

        /// <summary>
        /// Activates or deactivates a payment adjustment head (soft delete/restore) for a tenant,
        /// including audit logging and permission validation.
        /// </summary>
        /// <param name="id">Unique identifier of the payment adjustment head</param>
        /// <param name="isActive">Flag indicating activation (restore) or deactivation (delete)</param>
        /// <param name="loggedInEmployee">Employee requesting the operation</param>
        /// <param name="tenantId">The tenant identifier</param>
        /// <returns>API response object with operation status</returns>
        public async Task<ApiResponse<object>> DeletePaymentAdjustmentHeadAsync(Guid id, bool isActive, Employee loggedInEmployee, Guid tenantId)
        {
            // Dynamically select operation word for logs and messages
            var operation = isActive ? "restore" : "delete";

            try
            {
                // Step 1: Permission check
                var hasManagePermission = await _permission.HasPermission(PermissionsMaster.ManageMasters, loggedInEmployee.Id);
                if (!hasManagePermission)
                {
                    _logger.LogWarning("Access denied: Employee {EmployeeId} attempted to {Operation} payment adjustment head for tenant {TenantId}.",
                        loggedInEmployee.Id, operation, tenantId);
                    return ApiResponse<object>.ErrorResponse("Access Denied.", "You do not have permission to manage masters.", 403);
                }

                // Step 2: Entity existence check
                var paymentAdjustmentHead = await _context.PaymentAdjustmentHeads
                    .FirstOrDefaultAsync(pah => pah.Id == id && pah.TenantId == tenantId);

                if (paymentAdjustmentHead == null)
                {
                    _logger.LogWarning("Payment adjustment head with ID {Id} not found for tenant {TenantId} (attempted {Operation}).",
                        id, tenantId, operation);
                    return ApiResponse<object>.ErrorResponse("Not Found.", "Payment adjustment head not found.", 404);
                }

                // Step 3: Save pre-update state for audit log
                var existingEntityBson = _updateLogHelper.EntityToBsonDocument(paymentAdjustmentHead);

                // Step 4: Update IsActive status
                paymentAdjustmentHead.IsActive = isActive;

                await _context.SaveChangesAsync();

                // Step 5: Push update action to audit log
                await _updateLogHelper.PushToUpdateLogsAsync(new UpdateLogsObject
                {
                    EntityId = paymentAdjustmentHead.Id.ToString(),
                    UpdatedById = loggedInEmployee.Id.ToString(),
                    OldObject = existingEntityBson,
                    UpdatedAt = DateTime.UtcNow
                }, "PaymentAdjustmentHeadModificationLog");

                _logger.LogInfo(
                    "Payment adjustment head (ID: {Id}, Name: {Name}) successfully {Operation}d by employee {EmployeeId} for tenant {TenantId}.",
                    paymentAdjustmentHead.Id, paymentAdjustmentHead.Name, operation, loggedInEmployee.Id, tenantId);

                return ApiResponse<object>.SuccessResponse(new { }, $"Payment adjustment head {operation}d successfully.", 200);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex,
                    "Exception occurred while performing {Operation} on payment adjustment head (ID: {Id}) for tenant {TenantId}.",
                    operation, id, tenantId);

                return ApiResponse<object>.ErrorResponse("An error occurred.", $"Exception occurred while trying to {operation} payment adjustment head.", 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
    }
}