import { Injectable, HttpException, HttpStatus, Res, Inject, forwardRef } from "@nestjs/common"
import { InjectRepository } from "@nestjs/typeorm"
import { Repository, Like, Not, ILike, Brackets, IsNull, MoreThan, LessThan, MoreThanOrEqual, In } from "typeorm"
import { classToPlain } from "class-transformer"
import { responseMessages } from "../../messages/response-messages"
// Entities Files../../messages/comet-chat.ts
import { Constants } from "src/common/constants"
import { Campaign, CampaignStatus, ProfilePrivacy } from "../../entities/campaign.entity"
import { Users, UserRoles } from "../../entities/users.entity"
import { CampaignCreateDto } from "./dtos/campaign-create.dto"
import { CampaignRepository } from "./campaign.repository"
import slugify from "slugify"
import { AddInfluencerDto } from "./dtos/add-influencer.dto"
import { CampaignInfluencer, InfluencerStatus, JobStatus, PaymentMode, PRICE_CHANGE_STAGE_LABELS } from "src/entities/campaign_influencer.entity"
import { AddBookmarkDto } from "./dtos/add-bookmark.dto"
import { CampaignInfluencerBookmarks } from "src/entities/campaign_influencer_bookmark.entity"
import { UsersServices } from "src/common/services/users/users.service"
import { Draft, DraftStatus } from "src/entities/draft.entity"
import { DraftVersions, DraftVersionStatus } from "src/entities/draft_versions.entity"
import { DraftComments } from "src/entities/draft_comments.entity"
import { AddPostsDto } from "./dtos/add-posts.dto"
import { RejectCompletionRequest } from "src/entities/reject-completion-request.entity"
import { chatMessages } from "../../messages/comet-chat"
import { SocketGateway } from "src/common/services/socket/socket.gateway"
import { CampaignSaveDto } from "./dtos/save-campaign.dto"
import { InfluenceSaveCampaigns } from "src/entities/influencer-save-campaigns.entity"
import { CommonService } from "../common/common.services"
import { CampaignPayment, PAYMENT_STATUS, PaymentMode as CampaignPaymentMode } from "src/entities/campaign_payment.entity"
import { StripeServices } from "src/common/services/payment-gateway/stripe/stripe.service"
import { Card } from "src/entities/card.entity"
import { ReportDto } from "./dtos/report.dto"
import { Pay } from "twilio/lib/twiml/VoiceResponse"
import { Notifications, RedirectType } from "src/entities/notifications.entity"
import { notificationMessages } from "src/messages/notifications-messages"
import { notificationSettings } from "src/messages/notification-settings"

import { InfluencerCampaignRecommended } from "src/entities/influencer_campaign_recommended.entity"
import { OpenAIService } from "src/common/services/openai/openai.service"
import { SendMailService } from "src/common/config/send-mail.service"
import { emailMessages } from "src/messages/email-messages"
const moment = require('moment');
const { slugify: translitSlug } = require("transliteration");
import * as crypto from 'crypto';
import { TrustProductsEvaluationsContextImpl } from "twilio/lib/rest/trusthub/v1/trustProducts/trustProductsEvaluations"
import { SocialMediaPosts } from "src/entities/social-media-posts.entity"
// DTO Files
import * as ExcelJS from 'exceljs';
import { profile } from "console"
import { EncryptionService } from "src/common/services/encryption/encryption.service"
import axios from "axios"
import { KeyType, Settings } from "src/entities/settings.entity"
import { SocialMedias } from "src/entities/social-medias.entity"
import { CampaignInfluencerIdParamDto } from "./dtos/campaign-influencer-id-param.dto"
import { retry } from "rxjs"
import { Posts } from "src/entities/posts.entity"
import { PreviewCampaignDto } from "./dtos/preview-campaign.dto"
import { CampaignCategoryCountryOrder } from "src/entities/campaign-category-country-order.entity"



@Injectable()
export class CampaignService {
    constructor(
        private campaignRepository: CampaignRepository,
        private SocketGateway: SocketGateway,
        private CommonService: CommonService,
        private StripeService: StripeServices,
        private readonly OpenAIService: OpenAIService,
        private readonly SendMailService: SendMailService,
        private readonly encryptService: EncryptionService,

        @Inject(forwardRef(() => UsersServices))
        private UsersServices: UsersServices,

        @InjectRepository(Campaign)
        private Campaign: Repository<Campaign>,

        @InjectRepository(Users)
        private Users: Repository<Users>,

        @InjectRepository(CampaignInfluencer)
        private CampaignInfluencer: Repository<CampaignInfluencer>,

        @InjectRepository(Draft)
        private Draft: Repository<Draft>,

        @InjectRepository(DraftVersions)
        private DraftVersions: Repository<DraftVersions>,

        @InjectRepository(DraftComments)
        private DraftComments: Repository<DraftComments>,

        @InjectRepository(CampaignInfluencerBookmarks)
        private CampaignInfluencerBookmarks: Repository<CampaignInfluencerBookmarks>,

        @InjectRepository(RejectCompletionRequest)
        private RejectCompletionRequest: Repository<RejectCompletionRequest>,

        @InjectRepository(InfluenceSaveCampaigns)
        private InfluenceSaveCampaigns: Repository<InfluenceSaveCampaigns>,

        @InjectRepository(CampaignPayment)
        private CampaignPayment: Repository<CampaignPayment>,

        @InjectRepository(Card)
        private Card: Repository<Card>,

        @InjectRepository(SocialMediaPosts)
        private SocialMediaPosts: Repository<SocialMediaPosts>,

        @InjectRepository(InfluencerCampaignRecommended)
        private InfluencerCampaignRecommended: Repository<InfluencerCampaignRecommended>,

        @InjectRepository(Notifications)
        private Notifications: Repository<Notifications>,

        @InjectRepository(Settings)
        private Settings: Repository<Settings>,

        @InjectRepository(SocialMedias)
        private SocialMedias: Repository<SocialMedias>,

        @InjectRepository(Posts)
        private Posts: Repository<Posts>,

        @InjectRepository(CampaignCategoryCountryOrder)
        private CampaignCategoryCountryOrder: Repository<CampaignCategoryCountryOrder>,


    ) { }

    async sendCampaignNotification(payload) {
        const settingsMap = notificationSettings as Record<string, any>;
        const settings = settingsMap[payload.type] || {};
        const isInAppEnabled = settings?.in_app !== false;
        const isEmailEnabled = settings?.email === true;
        const isWhatsappEnabled = settings?.whatsapp === true;
        const isCometChatEnabled = settings?.comet_chat === true;

        // Check if the notification limit is reached // TRUE MEANS LIMIT REACHED AND FALSE MEANS LIMIT NOT REACHED
        const isLimitReached = await this.campaignRepository.checkNotificationLimit(payload?.metadata?.campaign_influencer_id ?? null, payload.type, payload.metadata);
        console.log("isLimitReached", isLimitReached);
        if (isInAppEnabled && !isLimitReached) {
            this.campaignRepository.notification(
                payload.receiver,
                payload.description,
                payload.sender ?? null,
                payload.redirectType ?? null,
                payload.redirectId ?? null,
                payload.redirectSlug ?? null,
                payload.metadata ?? {},
                payload.type
            );
        }

        if (isEmailEnabled && !isLimitReached) {
            console.log("payload.type", payload.type);
            this.campaignRepository.sendEmail(
                payload.receiver,
                payload.metadata,
                payload.type,
                payload.redirectSlug ?? null,
                payload.redirectId ?? null,
            );
        }

        if (isWhatsappEnabled && !isLimitReached) {
            this.campaignRepository.sendWhatsappNotification(
                payload.receiver,
                payload.metadata,
                payload.description,
                payload.redirectSlug ?? null,
                payload.redirectId ?? null,
            );
        }

        if (isCometChatEnabled && payload.cometChat) {
            this.campaignRepository.sendCometMessage(
                payload.sender,
                payload.type,
                payload.cometChat.groupId,
                payload.cometChat.userId,
                payload.cometChat.messageType || 'status',
                payload.metadata ?? {}
            );
        }

        if (payload.cometChat?.groupId && payload.metadata?.brand_status) {
            const tags = [payload?.metadata?.brand_status, payload?.metadata?.campaign_id];
            this.campaignRepository.updateCometChatGroupTags(
                payload.cometChat.groupId,
                tags,
                payload?.metadata
            );
        }
    }

    async list(queryParam: any, user: any) {
        const reqPage = queryParam.page ? queryParam.page : 0
        const page = queryParam.page - 1
        const pageSize = queryParam.per_page ? queryParam.per_page : 10
        const offset = page * pageSize
        const nextPage = page + 1
        const nextOffset = nextPage * pageSize
        const userId = queryParam.user_id ? queryParam.user_id : user.id
        let sortByArr = {
            campaign_name: "campaign.campaign_name",
            budget_per_influencer: "campaign.budget_per_influencer",
            number_of_influencers: "campaign.number_of_influencers",
            created_at: "campaign.created_at",
            posting_start_date: "campaign.posting_start_date",
            posting_end_date: "campaign.posting_end_date",
            first_draft_date: "campaign.first_draft_date",
            profile_privacy: "campaign.profile_privacy"
        }

        let sort_by = queryParam.sort_by || "created_at"
        let sort_type = queryParam.sort || "ASC"
        sort_by = sortByArr[sort_by]

        const languageCode = user.language || 'en';
        let queryBuilder = await this.campaignRepository.commonQuery(languageCode); // Resolve the promise

        let query = queryBuilder.where("campaign.status = :status", { status: queryParam.status }).andWhere("campaign.users = :userId", { userId }).orderBy(sort_by, sort_type)

        if (queryParam.search) {
            query = query.andWhere(`(campaign.campaign_name ILIKE :search)`, { search: `%${queryParam.search}%` })
        }

        if (reqPage !== 0) {
            query = query.skip(offset).take(pageSize)
        }

        const [results, total] = await query.getManyAndCount()

        return {
            data: results,
            total: total,
            hasMoreResults: total > nextOffset ? 1 : 0
        }
    }

    async campaignPopup(auth) {
        return await this.Users.update(auth.id, { is_show_campaign_popup: false });
    }

    async getCampaign(queryParam, auth) {
        const languageCode = auth.language || 'en';
        let queryBuilder = await this.campaignRepository.commonQuery(languageCode);
        let runningCampaignsQuery = await queryBuilder
            .where("campaign.status = :statusRunning", { statusRunning: CampaignStatus.RUNNING })
            .andWhere("campaign.users = :userId", { userId: auth.id })
            .orderBy("campaign.created_at", "DESC")

        if (queryParam.search) {
            runningCampaignsQuery = runningCampaignsQuery.andWhere(`(campaign.campaign_name ILIKE :search)`, { search: `%${queryParam.search}%` })
        }
        let runningCampaigns = await runningCampaignsQuery.take(3).getMany()

        let completedCampaignsQuery = await queryBuilder
            .where("campaign.status = :statusCompleted", { statusCompleted: CampaignStatus.COMPLETED })
            .andWhere("campaign.users = :userId", { userId: auth.id })
            .orderBy("campaign.created_at", "DESC")

        if (queryParam.search) {
            completedCampaignsQuery = completedCampaignsQuery.andWhere(`(campaign.campaign_name ILIKE :search)`, { search: `%${queryParam.search}%` })
        }
        let completedCampaigns = await completedCampaignsQuery.take(3).getMany()

        const campaign = {
            running: runningCampaigns,
            completed: completedCampaigns
        }

        return { data: campaign }
    }

    async complete(param, authUser) {
        const campaign = await this.campaignRepository.checkCampaignExists(param.campaign_id, authUser);
        // Check if all influencer are closed
        const completedStatus = await this.campaignRepository.commonCampaignInfluencerCompletedQuery(param.campaign_id);
        if (completedStatus) {
            await this.CampaignInfluencer.update({ campaign: { id: param.campaign_id }, is_archived: true }, { influencer_status: InfluencerStatus.ARCHIVED })
            await this.Campaign.update({ id: param.campaign_id }, { status: CampaignStatus.COMPLETED })
            await this.CampaignInfluencer.update({ campaign: { id: param.campaign_id }, influencer_status: InfluencerStatus.WAITING_FOR_QUOTATIONS }, { influencer_status: InfluencerStatus.ARCHIVED })
            await this.CampaignInfluencer.update({ campaign: { id: param.campaign_id }, influencer_status: InfluencerStatus.QUOTATION_AVAILABLE }, { influencer_status: InfluencerStatus.ARCHIVED })
            await this.CampaignCategoryCountryOrder
                .createQueryBuilder()
                .delete()
                .from(CampaignCategoryCountryOrder)
                .where('campaign_category_id IN (SELECT id FROM campaign_categories WHERE campaign_id = :campaignId)', { campaignId: param.campaign_id })
                .execute();
            const saveCampaigns = await this.InfluenceSaveCampaigns.find({ relations: ["influencer"], where: { campaign: { id: param.campaign_id } } });
            if (saveCampaigns.length > 0) {
                for (const saveCampaign of saveCampaigns) {
                    let campaignInfluencerSave = await this.CampaignInfluencer.findOne({ where: [{ campaign: { id: campaign.id }, influencer: { id: saveCampaign.influencer.id } }] });
                    if (!campaignInfluencerSave) {
                        campaignInfluencerSave = new CampaignInfluencer();
                        campaignInfluencerSave.campaign = campaign;
                        campaignInfluencerSave.influencer = saveCampaign.influencer;
                        campaignInfluencerSave.post_due_date = campaign.posting_end_date;
                        campaignInfluencerSave.draft_due_date = campaign.first_draft_date;
                        campaignInfluencerSave.influencer_budget = 0;
                        campaignInfluencerSave.status = InfluencerStatus.ARCHIVED;
                        campaignInfluencerSave.influencer_status = InfluencerStatus.ARCHIVED;
                        await this.CampaignInfluencer.save(campaignInfluencerSave);
                    }
                    if (campaignInfluencerSave.status != 'closed') {
                        campaignInfluencerSave.influencer_status = InfluencerStatus.ARCHIVED;
                        await this.CampaignInfluencer.save(campaignInfluencerSave);
                    }
                }
            }
            await this.UsersServices.increasePermissionCount(authUser.id, "number_of_campaigns");
            this.completeCometChatCampaign(param.campaign_id);
            // const chatCount = await this.CampaignInfluencer.count({ where: { campaign: { id: param.campaign_id }, is_chat_allowed: true } });
            // await this.UsersServices.increasePermissionCount(authUser.id, "chat_with_influencers", chatCount);
            return;
        }
        throw new HttpException(responseMessages.en.campaign.campaign_influencer_not_closed, HttpStatus.BAD_REQUEST)
    }


    async completeCometChatCampaign(campaignId) {
        // Update CometChat conversation tags for all campaign influencers
        const campaignInfluencers = await this.CampaignInfluencer.find({
            where: { campaign: { id: campaignId } },
            select: ['id'],
        });
        const tags = [campaignId, 'campaign_closed'];
        for (const ci of campaignInfluencers) {
            this.campaignRepository.updateCometChatGroupTags(ci.id, tags);
        }
    }

    async beforeBrandDelete(authUser: any) {
        const campaigns = await this.Campaign.find({ where: { users: { id: authUser.id }, status: CampaignStatus.RUNNING } }); {
            if (campaigns.length == 0) {
                return true;
            }
            for (const campaign of campaigns) {
                const campaignInfluencer = await this.CampaignInfluencer.createQueryBuilder('campaign_influencer')
                    .select(['COUNT(*) as count', "COALESCE(SUM(CASE WHEN campaign_influencer.status = 'closed' THEN 1 ELSE 0 END), 0) AS closedCount"])
                    .where('campaign_influencer.campaign_id = :campaign_id', { campaign_id: campaign.id })
                    .andWhere('campaign_influencer.status NOT IN(:...influencer_status)', { influencer_status: [InfluencerStatus.UNCONNTACTED, InfluencerStatus.WAITING_FOR_QUOTATIONS, InfluencerStatus.QUOTATION_AVAILABLE] })
                    .getRawOne();
                const totalCount = parseInt(campaignInfluencer?.count || '0', 10);
                const closedCount = parseInt(campaignInfluencer?.closedcount || '0', 10);

                if (totalCount > 0 && closedCount !== totalCount) {
                    // Influencers are still registered in some campaigns.
                    return false;
                }
            }
        }
        return true; // No influencer are registered in any campaigns.
    }

    async chaseUp(param: any, request: any, authUser: any) {
        const influencer = await this.campaignRepository.checkUserExists(request.influencer_id);
        const campaign = await this.campaignRepository.checkCampaignExists(param.campaign_id, authUser);
        const notificationMessage = notificationMessages.en.descriptions.chase_up.replace(':userName', `${authUser.company_name}`).replace(":type", request.type).replace(":campaign_name", campaign.campaign_name);;
        const objInfluencerType = {
            'quotation': "waiting_for_quotations",
            'drafts': "drafting_deliverables",
            'posts': "posting_deliverables"
        }
        const metadata = { campaign_id: campaign.id, campaign_influencer_id: influencer.id, campaignName: campaign.campaign_name, status: objInfluencerType[request.type], influencer: { id: influencer.id } };
        this.sendCampaignNotification({
            type: 'chase_up',
            receiver: influencer,
            description: notificationMessage,
            sender: authUser,
            redirectType: RedirectType.JOB_INPROGRESS,
            redirectId: campaign.id,
            redirectSlug: campaign.slug,
            metadata: metadata,
        });
        //  await this.campaignRepository.notification(influencer, notificationMessage, authUser, RedirectType.JOB_INPROGRESS, campaign.id, campaign.slug, metadata, true, 'chase_up');
        return;
    }

    async campaignDetail(param: any, authUser: any) {
        const languageCode = authUser.language || 'en';
        let queryBuilder = await this.campaignRepository.commonQuery(languageCode);
        let campaign = await queryBuilder.where("campaign.slug = :slug", { slug: param.slug }).getOne()
        if (!campaign) {
            throw new HttpException(responseMessages.en.campaign.campaign_not_exists, HttpStatus.NOT_FOUND)
        }
        if (campaign.users.id != authUser?.id && authUser.role == UserRoles.BRAND) {
            throw new HttpException(responseMessages.en.campaign.campaign_not_allowed, HttpStatus.FORBIDDEN);
        }

        return { data: campaign }
    }

    async getCampaignMeta(param: { slug: string }) {
        const campaign = await this.Campaign.createQueryBuilder('campaign')
            .innerJoinAndSelect('campaign.users', 'users')
            .where('campaign.slug = :slug', { slug: param.slug })
            .andWhere('campaign.deleted_at IS NULL')
            // .andWhere('campaign.is_active = :is_active', { is_active: true })
            //.andWhere('campaign.profile_privacy = :profile_privacy', { profile_privacy: ProfilePrivacy.PUBLIC })
            // .andWhere('campaign.status = :status', { status: CampaignStatus.RUNNING })
            .select([
                'campaign.id',
                'campaign.campaign_name',
                'campaign.slug',
                'campaign.attachment',
                'campaign.profile_privacy',
                'users.id',
                'users.slug',
                'users.company_name',
                'users.first_name',
                'users.last_name',
                'users.display_name',
                'users.about_me',
                'users.image',
            ])
            .getOne();

        if (!campaign) {
            throw new HttpException(responseMessages.en.campaign.campaign_not_exists, HttpStatus.NOT_FOUND);
        }
        campaign['campaign_image'] = campaign.attachment ? campaign.attachment[0] : null;
        return { data: campaign, base_url: process.env.AWS_FILE_PATH + 'files/campaign/' }
    }

    async campaignInfluencerDetail(param: any, authUser: any) {
        await this.campaignRepository.checkCampaignExists(param.campaign_id, authUser);
        const languageCode = authUser.language || 'en';
        let queryBuilder = await this.campaignRepository.commonQuery(languageCode);
        queryBuilder = queryBuilder.leftJoinAndSelect("campaign.campaignInfluencers", "campaignInfluencers").withDeleted()
            .where('campaignInfluencers.influencerId = :influencer_id', { influencer_id: param.influencer_id })

        queryBuilder = await this.campaignRepository.draftCommonQuery(queryBuilder);

        let campaign = await queryBuilder.andWhere("campaign.id = :campaign_id", { campaign_id: param.campaign_id }).getOne()
        if (!campaign) {
            throw new HttpException(responseMessages.en.campaign.campaign_not_exists, HttpStatus.NOT_FOUND)
        }
        const influencer = await this.Users.findOne({ where: { id: param.influencer_id }, withDeleted: true });
        let influencerDetails = await this.UsersServices.getUserDetails(influencer, authUser);
        if (param.campaign_id) {
            influencerDetails['campaignBookmarks'] = await this.CampaignInfluencerBookmarks.find({ where: [{ campaign: { id: param.campaign_id }, influencer: { id: influencer.id } }] });
            influencerDetails['isBookmarkAdded'] = influencerDetails.campaignBookmarks.length > 0 ? 1 : 0;
        }

        const campaignInfluencer = await this.CampaignInfluencer.findOne({ where: { campaign: { id: param.campaign_id }, influencer: { id: param.influencer_id } } });
        if (campaignInfluencer?.status == InfluencerStatus.QUOTATION_AVAILABLE) {
            await this.CampaignInfluencer.update({ id: campaignInfluencer.id }, { is_profile_seen: true });
        }

        const failedPayment = await this.CampaignPayment.createQueryBuilder('payment')
            .leftJoin('payment.campaign', 'campaign')
            .where('payment.payment_status = :status', { status: PAYMENT_STATUS.FAILED })
            .andWhere('payment.created_at <= :date', { date: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) })
            .andWhere('payment.campaign_id = :campaign_id', { campaign_id: param.campaign_id })
            .select(['payment.id', 'campaign.id', 'campaign.campaign_name'])
            .getOne();
        let card = await this.Card.exists({ where: { users: { id: authUser.id } } });

        return { data: { campaign, influencerDetails, failedPayment, card } }
    }

    async campaignBrandDetail(param: any, authUser) {
        //  await this.campaignRepository.checkCampaignExists(param.campaign_id);
        if (param.user_id) {
            authUser = await this.Users.findOne({ where: { id: param.user_id } });
        }
        const languageCode = authUser.language || 'en';
        let queryBuilder = await this.campaignRepository.commonQuery(languageCode);
        queryBuilder = queryBuilder.leftJoinAndSelect("campaign.campaignInfluencers", "campaignInfluencers", 'campaignInfluencers.influencerId = :influencer_id', { influencer_id: authUser.id }).withDeleted()
            .loadRelationCountAndMap(
                'campaign.isCampaignSaved',
                'campaign.influencerSaveCampaigns',
                'saveCountByMe',
                (qb) => qb.where('saveCountByMe.influencer = :influencerid', { influencerid: authUser.id })
            )

        queryBuilder = await this.campaignRepository.draftCommonQuery(queryBuilder);

        //  queryBuilder = this.campaignRepository.applyInternationalOnlyVisibilityForInfluencer(queryBuilder, authUser.country);

        let campaign = await queryBuilder.andWhere("campaign.id = :campaign_id", { campaign_id: param.campaign_id }).getOne()
        if (!campaign) {
            throw new HttpException(responseMessages.en.campaign.campaign_not_exists, HttpStatus.NOT_FOUND)
        }

        let brandDetails = await this.campaignRepository.getCampaignBrandDetails(campaign.users.id);
        return { data: { campaign, brandDetails } }
    }


    async getDraftDetails(params, authUser: any) {
        const result = await this.Draft.createQueryBuilder('draft')
            .leftJoinAndSelect('draft.draftComments', 'draftComments')
            .leftJoinAndSelect('draftComments.fromUser', 'fromUser')
            .leftJoinAndSelect('draft.draftVersions', 'draftVersions')
            .where('draft.id = :draft_id', { draft_id: params.draft_id })
            .select([
                'draft.id',
                'draft.is_draft_seen',
                'draftComments.id',
                'draftComments.comment',
                'draftComments.created_at',
                'draftVersions',
                'fromUser.id',
                'fromUser.slug',
                'fromUser.first_name',
                'fromUser.last_name',
                'fromUser.email',
                'fromUser.image',
                'fromUser.role'
            ])
            .orderBy('draftComments.created_at', 'ASC')
            .addOrderBy('draftVersions.status', 'ASC')
            .addOrderBy('draftVersions.created_at', 'DESC')
            .getOne();
        if (authUser.role == UserRoles.BRAND) {
            await this.Draft.update({ id: params.draft_id }, { is_draft_seen: true });
        }
        return { data: result }
    }


    async create(request: CampaignCreateDto, authUser: any) {

        const budget = Number(request.budget_per_influencer);
        if (budget > 0) {
            if (budget < 4 && request.currency == "HKD") {
                throw new HttpException(responseMessages.en.campaign.budget_per_influencer_min.replace(":min", "4 HKD"), HttpStatus.BAD_REQUEST);
            }
        }
        const isAllowed = await this.UsersServices.checkPermission(authUser.id, "number_of_campaigns");
        if (!isAllowed) {
            throw new HttpException(responseMessages.en.permission.campaign_limit_reached, HttpStatus.BAD_REQUEST)
        }
        const total_budget = request.number_of_influencers ? request.budget_per_influencer * request.number_of_influencers : 0;
        request["users"] = authUser
        request['campaign_country'] = authUser.country || "HK";
        request["status"] = CampaignStatus.RUNNING
        request["slug"] = await this.generateSlug(request.campaign_name)
        request["total_budget"] = total_budget;
        request["budget_per_influencer_usd"] = await this.CommonService.convertCurrencyRate(request.currency, request.budget_per_influencer);
        request["total_budget_usd"] = await this.CommonService.convertCurrencyRate(request.currency, total_budget);
        await this.campaignRepository.createCampaign(request)
        return { slug: request["slug"], profile_privacy: request.profile_privacy }
    }

    async openAI(request: any, authUser: any) {
        return await this.OpenAIService.createCampaignPrompt(request);
    }
    async getLogs(query: any) {
        const { log_id } = query;
        return await this.OpenAIService.getLogs(log_id);
    }

    async openAIChat(request: any, authUser: any) {
        return await this.OpenAIService.createChatPrompt(request);
    }

    async addInfluencer(request: AddInfluencerDto, authUser: any) {

        const campaign = await this.campaignRepository.checkCampaignExists(request.campaign_id, authUser);
        if (campaign.status == CampaignStatus.COMPLETED) {
            throw new HttpException(responseMessages.en.campaign.campaign_completed, HttpStatus.BAD_REQUEST);
        }
        if (!campaign.is_active) {
            throw new HttpException(responseMessages.en.campaign.campaign_inactive, HttpStatus.BAD_REQUEST);
        }
        for (const id of request.influencer_id) {
            const influencer = await this.campaignRepository.checkUserExists(id);
            const isExists = await this.CampaignInfluencer.count({ where: { campaign: { id: request.campaign_id }, influencer: { id } } });
            if (!isExists) {
                await this.CampaignInfluencer.save({ campaign, influencer, post_due_date: campaign.posting_end_date, draft_due_date: campaign.first_draft_date });
            }
        }
        return { is_show_campaign_popup: authUser.is_show_campaign_popup }
    }

    async generateSlug(campaign_name) {
        // Generate initial slug from the name
        let slug = campaign_name.trim()
            .toLowerCase()
            .replace(/[\/\\?%*:|"<>&]+/g, '') // remove special characters
            .replace(/\s+/g, '-')             // replace spaces with dashes
            .replace(/-+/g, '-');
        //slugify(translitSlug(campaign_name), { lower: true, strict: true }) || Math.random().toString(36).substring(2, 16);

        // Check if the slug exists
        let existing = await this.Campaign.findOne({ where: { slug } })

        // If slug exists, append a 4-character random string
        if (existing) {
            const randomString = Math.random().toString(36).substring(2, 6) // Generate a 4-character random string
            slug = `${slug}_${randomString}`
        }
        return slug
    }

    async update(request: any, params: any, authUser: any) {
        const campaign = await this.campaignRepository.checkCampaignExists(params.campaign_id, authUser);
        if (campaign.status == CampaignStatus.COMPLETED) {
            throw new HttpException(responseMessages.en.campaign.campaign_completed, HttpStatus.BAD_REQUEST);
        }

        const budget = Number(request.budget_per_influencer);
        if (budget > 0) {
            if (budget < 4 && request.currency == "HKD") {
                throw new HttpException(responseMessages.en.campaign.budget_per_influencer_min.replace(":min", "4 HKD"), HttpStatus.BAD_REQUEST);
            }
        }
        const total_budget = request.number_of_influencers ? request.budget_per_influencer * request.number_of_influencers : 0;
        request["total_budget"] = total_budget
        // request["slug"] = await this.generateSlug(request.campaign_name)
        request["budget_per_influencer_usd"] = await this.CommonService.convertCurrencyRate(request.currency, request.budget_per_influencer);
        request["total_budget_usd"] = await this.CommonService.convertCurrencyRate(request.currency, total_budget);
        if (campaign.is_barter == "false" && request.is_barter == "true") {
            this.sendBarterNotification(campaign, true);
        }
        if (campaign.is_barter == "true" && request.is_barter == "false") {
            this.sendBarterNotification(campaign, false);
        }
        Object.assign(campaign, request)
        await this.Campaign.save(campaign)
        await this.campaignRepository.saveCampaignCategory(request, params.campaign_id);
        this.campaignRepository.saveRecommendInfluencer(params.campaign_id);
        await this.CampaignInfluencer.update({ campaign: { id: params.campaign_id }, status: In([InfluencerStatus.UNCONNTACTED, InfluencerStatus.QUOTATION_AVAILABLE, InfluencerStatus.WAITING_FOR_QUOTATIONS]) }, { post_due_date: campaign.posting_end_date, draft_due_date: campaign.first_draft_date });
        // Invalidate AI scores so next AI Agent run re-scores applicants (workflow §7)
        await this.CampaignInfluencer.update(
            { campaign: { id: params.campaign_id } },
            { ai_score_pending: true },
        );
        return { slug: campaign.slug, profile_privacy: campaign.profile_privacy }
    }

    async sendBarterNotification(campaign, isBarter: boolean) {
        const campaignInfluencer = await this.CampaignInfluencer.find({ relations: ["influencer"], where: { campaign: { id: campaign.id }, influencer_status: InfluencerStatus.QUOTATION_AVAILABLE } });
        for (const influencer of campaignInfluencer) {
            let status = "barter_not_allowed";
            let notificationMessage = notificationMessages.en.descriptions.barter_notification.replace(':userName', `${campaign.users.first_name} ${campaign.users.last_name}`).replace(':campaign_name', campaign.campaign_name);
            if (isBarter) {
                status = "barter_allowed";
                notificationMessage = notificationMessages.en.descriptions.barter_allow_notification.replace(':userName', `${campaign.users.first_name} ${campaign.users.last_name}`).replace(':campaign_name', campaign.campaign_name);
            }
            this.CampaignInfluencer.update(influencer.id, { is_campaign_update: true });
            const metadata = { campaign_id: campaign.id, campaignName: campaign.campaign_name, status: status, brand_status: influencer.status, influencer: { id: influencer.influencer.id } };
            this.sendCampaignNotification({
                type: status,
                receiver: influencer.influencer,
                description: notificationMessage,
                sender: campaign.users,
                redirectType: RedirectType.APPLIED,
                redirectId: campaign.id,
                redirectSlug: campaign.slug,
                metadata: metadata
            })
        }
    }
    async continueWithSamePrice(params, authUser) {
        const campaignInfluencer = await this.CampaignInfluencer.findOne({ relations: ["campaign", "campaign.users"], where: { id: params.campaign_influencer_id } });
        campaignInfluencer.is_campaign_update = false;
        campaignInfluencer.is_continue_with_same_quote = true;
        await this.CampaignInfluencer.save(campaignInfluencer);
        const campaign = campaignInfluencer.campaign;
        let notificationMessage = notificationMessages.en.descriptions.quote_with_same_price.replace(':userName', `${authUser.first_name} ${authUser.last_name}`).replace(':campaign_name', campaign.campaign_name);
        const metadata = { campaign_id: campaign.id, campaignName: campaign.campaign_name, status: campaignInfluencer.status, brand_status: campaignInfluencer.status, influencer: { id: authUser.id } };
        this.sendCampaignNotification({
            type: 'quote_with_same_price',
            receiver: campaign.users,
            description: notificationMessage,
            sender: authUser,
            redirectType: RedirectType.CAMPAIGN_MANAGEMENT,
            redirectId: campaign.id,
            redirectSlug: campaign.slug,
            metadata: metadata
        });
        return
    }

    async delete(params: any) {
        /*  const existsFaq = await this.campaign.findOne({where: [{ id: params.faq_id }] });
 
        if (!existsFaq) {
            throw  new HttpException(responseMessages.en.campaign.campaign_not_exists,HttpStatus.NOT_FOUND);
        }
 
        let id = existsFaq.id;
        await this.campaign.delete(id);
        return; */
    }

    async changeStatus(params: any) {
        let campaign = await this.Campaign.findOne({ where: [{ id: params.campaign_id }] });

        if (!campaign) {
            throw new HttpException(responseMessages.en.campaign.campaign_not_exists, HttpStatus.NOT_FOUND);
        }

        const is_active = (campaign.is_active == true) ? false : true;
        await this.Campaign.update(campaign.id, { is_active });
        if (!is_active) {
            this.InfluencerCampaignRecommended.delete({ campaign: { id: params.campaign_id } });
        }
        return;
    }

    async addBookmark(request: AddBookmarkDto, authUser: any) {

        const campaign = await this.campaignRepository.checkCampaignExists(request.campaign_id, authUser);
        const influencer = await this.campaignRepository.checkUserExists(request.influencer_id);

        const isBookmarkExists = await this.CampaignInfluencerBookmarks.findOne({ where: { campaign: { id: campaign.id }, influencer: { id: influencer.id } } })
        if (isBookmarkExists) {
            this.CampaignInfluencerBookmarks.delete(isBookmarkExists.id)
            return { id: isBookmarkExists.id }
        }
        const bookmark = await this.CampaignInfluencerBookmarks.save({ campaign, influencer })
        return { id: bookmark.id }
    }


    async campaignSave(request: CampaignSaveDto, authUser: any) {
        const campaign = await this.campaignRepository.checkCampaignExists(request.campaign_id, authUser);
        const influenceSaveCampaigns = await this.InfluenceSaveCampaigns.findOne({ where: { campaign: { id: campaign.id }, influencer: { id: authUser.id } } })
        if (influenceSaveCampaigns) {
            this.InfluenceSaveCampaigns.delete(influenceSaveCampaigns.id)
            return
        }
        await this.InfluenceSaveCampaigns.save({ campaign: campaign, influencer: authUser })
        return
    }

    async campaignBookmark(queryParam, auth) {
        const languageCode = auth.language || 'en';
        const query = await this.Campaign.createQueryBuilder("campaign")
            .leftJoinAndSelect("campaign.campaignBookmarks", "campaignBookmark")
            .innerJoinAndSelect("campaignBookmark.influencer", "influencer")
            .leftJoinAndSelect("influencer.influencerCategories", "categories")
            .leftJoin('influencer.social_medias', 'social_medias')
            .leftJoinAndSelect("categories.category", "category", "category.is_active = true")
            .leftJoin("category.translations", "translation_req", "translation_req.language_code = :languageCode AND translation_req.deleted_at IS NULL", { languageCode })
            .leftJoinAndSelect("category.translations", "translation",
                "(translation.language_code = :languageCode OR (translation.language_code = 'en' AND translation_req.id IS NULL)) AND translation.deleted_at IS NULL",
                { languageCode })
            .loadRelationCountAndMap("influencer.isBookmarkAdded", "influencer.campaignBookmarks", "bookmark", (qb) =>
                qb.groupBy("bookmark.influencerId") // Grouping logic
                    .addGroupBy("bookmark.campaign_id") // Grouping by campaign_id as well
                    .where("bookmark.campaign_id = campaign.id")
                    .innerJoin("bookmark.campaign", "campaign")
            )
            .select([
                "campaign.id",
                "campaign.campaign_name",
                "campaign.slug",
                "campaignBookmark",
                "influencer.id",
                "influencer.slug",
                "influencer.first_name",
                "influencer.last_name",
                "influencer.image",
                "influencer.display_name",
                "influencer.country",
                "influencer.platforms",
                "influencer.total_followers",
                "influencer.engagement_ratio",
                "categories.id",
                "category.id",
                "category.slug",
                "category.title",
                "translation.title",
                "translation.description",
                "translation.slug",
                "social_medias.id",
                "social_medias.social_user_name",
                "social_medias.social_type"
            ])
            .where("campaign.users = :userId", { userId: auth.id })
            .andWhere("campaignBookmark.id IS NOT NULL");

        if (queryParam.slug) {
            query.andWhere("campaign.slug = :slug", { slug: queryParam.slug })
        }

        const campaigns = await query.getMany();
        if (queryParam.is_list_view) {
            for (const campaign of campaigns) {
                for (const user of campaign.campaignBookmarks) {
                    user.influencer.socialMediaPosts = await this.SocialMediaPosts.createQueryBuilder("socialMediaPosts").where("socialMediaPosts.users.id = :userId", { userId: user.influencer.id }).andWhere("socialMediaPosts.created_time <> ''").orderBy("DATE(socialMediaPosts.created_time)", "DESC").limit(3).getMany()
                }
            }
        }
        return { data: campaigns }
    }

    async campaignBookmarkExport(queryParam, authUser: any, @Res() response: any) {
        const languageCode = authUser.language || 'en';
        const exportInfluencerTime = await this.UsersServices.checkPermissionWithTimeLimit(authUser.id, 'export_influencer_time_limit_time_period');
        if (!exportInfluencerTime) {
            const message = responseMessages.en.permission.export_limit_reached
            throw new HttpException(message, HttpStatus.BAD_REQUEST);
        }
        const permission = await this.UsersServices.getUserPermission(authUser.id, 'export_influencer_limit');
        if (permission.limit <= 0) {
            throw new HttpException(responseMessages.en.permission.permission_not_exists, HttpStatus.BAD_REQUEST);
        }
        const workbook = new ExcelJS.Workbook();
        const worksheet = workbook.addWorksheet('Campaign Influencer');
        const headers = ["Name", "Profile Engagement", "Category", "Bio", "Profile URL",
            "Instagram URL", "Facebook URL", "Youtube URL", "TikTok URL", "Xiaohongshu URL", "Profile Picture", "Keywords",
            "Instagram User Name", "Instagram Follower Count", "Instagram Engagement Rate", "Instagram Post Count",
            "Facebook User Name", "Facebook Follower Count", "Facebook Engagement Rate", "Facebook Post Count",
            "Youtube User Name", "Youtube Follower Count", "Youtube Engagement Rate", "Youtube Post Count",
            "TikTok User Name", "TikTok Follower Count", "TikTok Engagement Rate", "TikTok Post Count",
            "Xiaohongshu User Name", "Xiaohongshu Follower Count", "Xiaohongshu Following Count", "Xiaohongshu Engagement Rate", "Xiaohongshu Post Count",
        ];

        worksheet.addRow(headers); // Add the headers as the first row
        const query = await this.CampaignInfluencerBookmarks.createQueryBuilder("bookmark")
            .leftJoinAndSelect("bookmark.campaign", "campaign")
            .innerJoinAndSelect("bookmark.influencer", "influencer")
            .leftJoinAndSelect("influencer.influencerCategories", "categories")
            .leftJoinAndSelect('influencer.social_medias', 'social_medias')
            .leftJoinAndSelect("categories.category", "category")
            .leftJoin("category.translations", "translation_req", "translation_req.language_code = :languageCode AND translation_req.deleted_at IS NULL", { languageCode })
            .leftJoinAndSelect("category.translations", "translation",
                "(translation.language_code = :languageCode OR (translation.language_code = 'en' AND translation_req.id IS NULL)) AND translation.deleted_at IS NULL",
                { languageCode })
            .leftJoinAndSelect('influencer.facebookPages', 'facebookPages')
            .leftJoinAndSelect('influencer.instagramProfiles', 'instagramProfiles')
            .leftJoinAndSelect('influencer.youtubeChannels', 'youtubeChannels')
            .leftJoinAndSelect('influencer.tikTokProfiles', 'tikTokProfiles')
            .leftJoinAndSelect('influencer.xiaohongshuProfiles', 'xiaohongshuProfiles')
            .leftJoinAndSelect('influencer.userKeywords', 'userKeywords')
            .leftJoinAndSelect('userKeywords.keywords', 'keywords')
            .leftJoin('keywords.translations', 'keyword_translation_req_cp', 'keyword_translation_req_cp.language_code = :languageCode AND keyword_translation_req_cp.deleted_at IS NULL', { languageCode })
            .leftJoin('keywords.translations', 'keyword_translation_en_cp', 'keyword_translation_en_cp.language_code = \'en\' AND keyword_translation_en_cp.deleted_at IS NULL')
            .leftJoinAndSelect('keywords.translations', 'keyword_translation_cp',
                '(keyword_translation_cp.language_code = :languageCode OR (keyword_translation_cp.language_code = \'en\' AND keyword_translation_req_cp.id IS NULL)) AND keyword_translation_cp.deleted_at IS NULL',
                { languageCode })
            .where("campaign.slug = :slug", { slug: queryParam.slug })
            .orderBy('bookmark.createdAt', 'ASC')

        const influencerArr = await query.take(permission.limit).getMany();
        if (influencerArr.length > 0) {

            for (let data of influencerArr) {
                await this.mergeAllProfile(data.influencer, worksheet);
            }
            worksheet.getRow(1).font = { bold: true }; // Bold headers
            worksheet.columns.forEach((column) => {
                column.width = 20; // Adjust column width
            });
            const fileName = 'campaign-influencer-' + moment().format('YYYY-MM-DD');

            // Set response headers
            response.setHeader(
                'Content-Type',
                'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
            );
            response.setHeader('Content-Disposition', `attachment; filename=${fileName}.xlsx`);

            // Write the file to the response
            await workbook.xlsx.write(response);
            response.end();
            return;
        }
        throw new HttpException(responseMessages.en.file_manager.no_favorite_influencer, HttpStatus.NOT_FOUND);
    }

    async campaignApplicantsExport(queryParam, authUser: any, @Res() response: any) {
        const languageCode = authUser.language || 'en';
        const exportInfluencerTime = await this.UsersServices.checkPermissionWithTimeLimit(authUser.id, 'export_influencer_time_limit_time_period');
        if (!exportInfluencerTime) {
            const message = responseMessages.en.permission.export_limit_reached
            throw new HttpException(message, HttpStatus.BAD_REQUEST);
        }
        const permission = await this.UsersServices.getUserPermission(authUser.id, 'export_influencer_limit');
        if (permission.limit <= 0) {
            throw new HttpException(responseMessages.en.permission.permission_not_exists, HttpStatus.BAD_REQUEST);
        }
        const workbook = new ExcelJS.Workbook();
        const worksheet = workbook.addWorksheet('Campaign Applicants');
        const headers = ["Name", "Quotation Price", "Additional Note", "Profile Engagement", "Category", "Bio", "Profile URL",
            "Instagram URL", "Facebook URL", "Youtube URL", "TikTok URL", "Xiaohongshu URL", "Profile Picture", "Keywords",
            "Instagram User Name", "Instagram Follower Count", "Instagram Engagement Rate", "Instagram Post Count",
            "Facebook User Name", "Facebook Follower Count", "Facebook Engagement Rate", "Facebook Post Count",
            "Youtube User Name", "Youtube Follower Count", "Youtube Engagement Rate", "Youtube Post Count",
            "TikTok User Name", "TikTok Follower Count", "TikTok Engagement Rate", "TikTok Post Count",
            "Xiaohongshu User Name", "Xiaohongshu Follower Count", "Xiaohongshu Following Count", "Xiaohongshu Engagement Rate", "Xiaohongshu Post Count",
        ];

        worksheet.addRow(headers);
        const query = await this.CampaignInfluencer.createQueryBuilder("campaignInfluencer")
            .innerJoinAndSelect("campaignInfluencer.campaign", "campaign")
            .innerJoinAndSelect("campaignInfluencer.influencer", "influencer")
            .leftJoinAndSelect("influencer.influencerCategories", "categories")
            .leftJoinAndSelect('influencer.social_medias', 'social_medias')
            .leftJoinAndSelect("categories.category", "category")
            .leftJoin("category.translations", "translation_req", "translation_req.language_code = :languageCode AND translation_req.deleted_at IS NULL", { languageCode })
            .leftJoinAndSelect("category.translations", "translation",
                "(translation.language_code = :languageCode OR (translation.language_code = 'en' AND translation_req.id IS NULL)) AND translation.deleted_at IS NULL",
                { languageCode })
            .leftJoinAndSelect('influencer.facebookPages', 'facebookPages')
            .leftJoinAndSelect('influencer.instagramProfiles', 'instagramProfiles')
            .leftJoinAndSelect('influencer.youtubeChannels', 'youtubeChannels')
            .leftJoinAndSelect('influencer.tikTokProfiles', 'tikTokProfiles')
            .leftJoinAndSelect('influencer.xiaohongshuProfiles', 'xiaohongshuProfiles')
            .leftJoinAndSelect('influencer.userKeywords', 'userKeywords')
            .leftJoinAndSelect('userKeywords.keywords', 'keywords')
            .leftJoin('keywords.translations', 'keyword_translation_req_ap', 'keyword_translation_req_ap.language_code = :languageCode AND keyword_translation_req_ap.deleted_at IS NULL', { languageCode })
            .leftJoin('keywords.translations', 'keyword_translation_en_ap', 'keyword_translation_en_ap.language_code = \'en\' AND keyword_translation_en_ap.deleted_at IS NULL')
            .leftJoinAndSelect('keywords.translations', 'keyword_translation_ap',
                '(keyword_translation_ap.language_code = :languageCode OR (keyword_translation_ap.language_code = \'en\' AND keyword_translation_req_ap.id IS NULL)) AND keyword_translation_ap.deleted_at IS NULL',
                { languageCode })
            .where("campaign.slug = :slug", { slug: queryParam.slug })
            .andWhere("campaignInfluencer.influencer_status = :status", { status: InfluencerStatus.QUOTATION_AVAILABLE })

        if (queryParam.is_bookmark == 'true') {
            query.innerJoin(
                'influencer.campaignBookmarks',
                'campaignBookmark',
                'campaignBookmark.campaign_id = campaign.id',
            );
        }

        query.orderBy('campaignInfluencer.createdAt', 'ASC');

        const applicantArr = await query.take(permission.limit).getMany();
        if (applicantArr.length > 0) {
            for (const data of applicantArr) {
                const budget = parseFloat(String(data.influencer_budget ?? 0));
                const quotationPrice = data.currency ? `${data.currency} $${budget.toFixed(2)}` : `$${budget.toFixed(2)}`;
                await this.mergeAllProfile(data.influencer, worksheet, {
                    quotationPrice,
                    additionalNote: data.message || '',
                });
            }
            worksheet.getRow(1).font = { bold: true };
            worksheet.columns.forEach((column) => {
                column.width = 20;
            });
            const fileName = 'campaign-applicants-' + moment().format('YYYY-MM-DD');

            response.setHeader(
                'Content-Type',
                'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
            );
            response.setHeader('Content-Disposition', `attachment; filename=${fileName}.xlsx`);

            await workbook.xlsx.write(response);
            response.end();
            return;
        }
        throw new HttpException(responseMessages.en.file_manager.no_applicant_influencer, HttpStatus.NOT_FOUND);
    }

    async getSimpleEngagementMap(social_medias) {
        const engagementMap = {};

        if (social_medias && Array.isArray(social_medias)) {
            social_medias.forEach((media) => {
                const platform = media.social_type;
                const rate = parseFloat(media.engagement_rate);

                if (!isNaN(rate)) {
                    engagementMap[platform] = rate;
                }
            });
        }

        return engagementMap;
    }
    async mergeAllProfile(influencer, worksheet, extras?: {}) {
        const categoryTitles = (influencer.influencerCategories || [])
            .filter(item => item.category && item.category.is_active)
            .map((item) => {
                const translation = item.category?.translations && item.category.translations.length > 0 ? item.category.translations[0] : null;
                return translation ? translation.title : '';
            })
            .filter(title => title !== '')
            .join(", ")

        const keywords = influencer.userKeywords
            .map((item) => {
                const translation = item.keywords?.translations && item.keywords.translations.length > 0
                    ? item.keywords.translations[0]
                    : null;
                return translation ? translation.keyword : (item.keywords?.keyword || '');
            })
            .join(", ")
        const engagementMap = await this.getSimpleEngagementMap(influencer.social_medias);
        const extraFields = extras
            ? Object.fromEntries(
                Object.keys(extras).map(key => [key.toUpperCase(), extras[key] ?? ''])
            )
            : {};
        let row = {
            firstName: influencer.display_name,
            ...extraFields,
            engagementRate: influencer.engagement_ratio,
            category: categoryTitles,
            bio: influencer.about_me,
            profileUrl: process.env.FRONTEND_URL + 'influencers/' + influencer.slug,
            InstagramUrl: influencer?.instagramProfiles?.[0]?.url || '',
            FacebookUrl: influencer?.facebookPages?.[0]?.url || '',
            YoutubeUrl: influencer?.youtubeChannels?.[0]?.url || '',
            TikTokUrl: influencer?.tikTokProfiles?.[0]?.url || '',
            xiaohongshuUrl: influencer?.xiaohongshuProfiles?.[0]?.url || '',
            ProfileImage: influencer.image,
            keywords: keywords || '',
            // INSTAGRAM 

            IG_User_Name: influencer?.instagramProfiles?.[0]?.user_name || '',
            IG_Followers: influencer?.instagramProfiles?.[0]?.followers_count || '',
            IG_Engagement_Rate: engagementMap['instagram'] || '',
            IG_POST_COUNT: influencer?.instagramProfiles?.[0]?.media_count || '',

            // FACEBOOK

            FB_USER_NAME: influencer?.facebookPages?.[0]?.user_name || '',
            FB_FOLLOWER_COUNT: influencer?.facebookPages?.[0]?.followers_count || '',
            FB_ENGAGEMENT_RATE: engagementMap['facebook'] || '',
            FB_POST_COUNT: influencer?.facebookPages?.[0]?.post_count || '',

            // YouTube Data

            YT_USER_NAME: influencer?.youtubeChannels?.[0]?.channel_name || '',
            YT_FOLLOWER_COUNT: influencer?.youtubeChannels?.[0]?.subscriber_count || '',
            YT_ENGAGEMENT_RATE: engagementMap['youtube'] || '',
            YT_POST_COUNT: influencer?.youtubeChannels?.[0]?.video_count || '',

            // TikTok Data
            TK_USER_NAME: influencer?.tikTokProfiles?.[0]?.username || '',
            TK_FOLLOWER_COUNT: influencer?.tikTokProfiles?.[0]?.follower_count || '',
            TK_ENGAGEMENT_RATE: engagementMap['tiktok'] || '',
            TK_POST_COUNT: influencer?.tikTokProfiles?.[0]?.video_count || '',

            // XiaohongshuProfiles Data

            XHS_USER_NAME: influencer?.xiaohongshuProfiles?.[0]?.user_name || '',
            XHS_FOLLOWER_COUNT: influencer?.xiaohongshuProfiles?.[0]?.follower_count || '',
            XHS_ENGAGEMENT_COUNT_RATE: engagementMap['xiaohongshu'] || '',
            XHS_POST_COUNT: influencer?.xiaohongshuProfiles?.[0]?.post_count || '',

        };
        worksheet.addRow(Object.values(row)); // Add the row to the worksheet
    }

    async influencerList(queryParam, authUser: any) {
        const languageCode = authUser.language || 'en';
        const { campaign_id, status } = queryParam
        let queryBuilder = await this.campaignRepository.campaignInfluencerCommonQuery(languageCode); // Resolve the promise
        const result = {
            closed: {},
            completion_request: {},
            draft_available: {},
            drafting_deliverables: {},
            posting_deliverables: {},
            quotation_available: {},
            uncontacted: {},
            waiting_for_quotations: {},
        };

        const counts = await this.campaignCounter(campaign_id);
        await Promise.all(
            Object.keys(result).map(async (status) => {
                if (counts[status] == 0) return
                await this.sleep(100);
                const query = queryBuilder.clone();
                result[status] = await this.getInfluencerByStatus(query, campaign_id, status);
            })
        );

        return {
            data: result,
            counts
        }
    }

    async getInfluencerByStatus(queryBuilder, campaign_id, status) {
        let query = await queryBuilder.where("campaignInfluencer.status = :status", { status: status })
            .andWhere('campaign.id = :id', { id: campaign_id })

        if (status == InfluencerStatus.DRAFT_AVAILABLE) {
            await query.leftJoinAndSelect('campaignInfluencer.drafts', 'drafts', 'drafts.status = :draftStatus AND drafts.deleted_at IS NULL', { draftStatus: DraftStatus.PENDING })
                .leftJoinAndSelect('drafts.draftVersions', 'draftVersions')
                .leftJoinAndSelect('drafts.draftComments', 'draftComments')
                .leftJoinAndSelect('campaignInfluencer.rejectCompletionRequest', 'rejectCompletionRequest')
                .addSelect('drafts', 'draftVersions', 'draftComments', 'rejectCompletionRequest')
                .addOrderBy('draftVersions.created_at', 'DESC')
                .addOrderBy('rejectCompletionRequest.created_at', 'DESC');
        }

        if ([InfluencerStatus.POSTING_DELIVERABLES, InfluencerStatus.COMPLETION_REQUEST].includes(status)) {
            query.leftJoinAndSelect('campaignInfluencer.posts', 'posts')
                .addOrderBy('posts.created_at', 'DESC');
        }
        return await query.addOrderBy("campaignInfluencer.updatedAt", "DESC").getOne() || {};
    }
    /**
     * Returns the count of campaign influencers by status
     * @param campaign_id The id of the campaign
     */
    async campaignCounter(campaign_id: string) {
        const statusCounts = await this.CampaignInfluencer.createQueryBuilder('campInf')
            .select(['COUNT(*) AS count', 'campInf.status as status'])
            .where('campInf.campaign_id = :campaign_id', { campaign_id })
            .groupBy('campInf.status')
            .getRawMany();

        const status = {
            closed: 0,
            completion_request: 0,
            draft_available: 0,
            drafting_deliverables: 0,
            posting_deliverables: 0,
            quotation_available: 0,
            uncontacted: 0,
            waiting_for_quotations: 0,
        };


        statusCounts.map((statusCount) => {
            status[statusCount.status] = parseInt(statusCount.count);
        })

        return status;
    }

    private getInfluencerInvitedCount(influencerId: string, countryId?: string): Promise<number> {
        const query = this.CampaignInfluencer.createQueryBuilder('campaignInfluencer')
            .innerJoin('campaignInfluencer.campaign', 'campaign')
            .where('campaignInfluencer.influencer = :influencerIds', { influencerIds: influencerId })
            .andWhere('campaignInfluencer.influencer_status = :status', { status: InfluencerStatus.WAITING_FOR_QUOTATIONS })
            .andWhere('campaign.deleted_at IS NULL');

        // if (countryId) {
        //     query.andWhere(`EXISTS (
        //         SELECT 1 FROM campaign_categories cc
        //         INNER JOIN campaign_category_country_order cco ON cco.campaign_category_id = cc.id AND cco.deleted_at IS NULL
        //         WHERE cc.campaign_id = campaign.id
        //         AND cco.country_id = :country_id
        //     )`, { country_id: countryId });
        // }

        return query.getCount();
    }

    async contactNow(params: any, request: any, authUser: any) {
        const campaign = await this.campaignRepository.checkCampaignExists(params.campaign_id, authUser);
        if (campaign.status == CampaignStatus.COMPLETED) {
            throw new HttpException(responseMessages.en.campaign.campaign_completed, HttpStatus.BAD_REQUEST);
        }
        if (!campaign.is_active) {
            throw new HttpException(responseMessages.en.campaign.campaign_inactive, HttpStatus.BAD_REQUEST);
        }
        const influencer = await this.campaignRepository.checkUserExists(request.influencer_id);

        const campaignInfluencer = await this.campaignRepository.checkCampaignInfluencerExists(params.campaign_id, request.influencer_id);

        // const isAllowed = await this.UsersServices.checkPermission(campaign.users.id, 'chat_with_influencers');
        // if (isAllowed) {
        //     campaignInfluencer.is_chat_allowed = true;
        // }
        campaignInfluencer.status = InfluencerStatus.WAITING_FOR_QUOTATIONS;
        campaignInfluencer.influencer_status = InfluencerStatus.WAITING_FOR_QUOTATIONS;
        campaignInfluencer.chat_id = request.chat_id;
        await this.CampaignInfluencer.save(campaignInfluencer);
        const metadata = { campaign_id: campaign.id, campaignName: campaign.campaign_name, status: InfluencerStatus.WAITING_FOR_QUOTATIONS, brand_status: campaignInfluencer.status, influencer: { id: influencer.id } };
        const notificationMessage = notificationMessages.en.descriptions.contact_now.replace(':userName', `${authUser.company_name}`).replace(":campaign_name", campaign.campaign_name);;
        await this.sendCampaignNotification({
            type: 'contact_now',
            receiver: influencer,
            description: notificationMessage,
            sender: authUser,
            redirectType: RedirectType.INVITED,
            redirectId: campaign.id,
            redirectSlug: campaign.slug,
            metadata,
            cometChat: {
                groupId: campaignInfluencer.id,
                userId: request.influencer_id
            }
        });

        if (influencer.is_imported_by_admin) {
            const adminNotification = notificationMessages.en.descriptions.contact_now_for_admin.replace(':userName', `${authUser.company_name}`).replace(":campaign_name", campaign.campaign_name).replace(":influencer_name", `${influencer.display_name}`);
            this.campaignRepository.adminNotification(adminNotification, authUser, RedirectType.INFLUENCER_LIST, influencer.id, influencer.slug);
            if (influencer.email) {
                this.sendCampaignNotification({
                    type: 'invitation_request',
                    receiver: influencer,
                    description: adminNotification,
                    sender: null,
                    redirectType: RedirectType.INFLUENCER_LIST,
                    redirectId: influencer.id,
                    redirectSlug: influencer.slug,
                    metadata: { campaign_id: campaign.id, influencer: { id: influencer.id } }
                });
                // const name = `${influencer.first_name} ${influencer.last_name}`;
                // const mailSubject = emailMessages.en.registrationRequest.subject;
                // let info = { first_name: influencer.first_name, last_name: influencer.last_name, email: influencer.email, id: influencer.id };
                // const infoCrypto = Buffer.from(JSON.stringify(info)).toString('base64')
                // const registrationLink = process.env.FRONTEND_URL + 'register/influencer?info=' + infoCrypto;
                // const mailObj = {
                //     hello_user_name: emailMessages.en.hello_user_name.replace(':user_name', name),
                //     main_title: emailMessages.en.registrationRequest.title,
                //     descriptions: emailMessages.en.registrationRequest.description.replace(':INSERT_LINK_HERE', registrationLink),
                //     copyright: emailMessages.en.copyright,
                //     all_rights_reserved: emailMessages.en.all_rights_reserved,
                // }
                // this.SendMailService.sendMailObjectWithTemplates(influencer.email, mailSubject, 'common_mail', mailObj);
            }
            const receivers = await this.Users.find({ where: { role: UserRoles.ADMIN, is_active: true } });
            const influencerSocialMedias = await this.SocialMedias.find({ where: { users: { id: influencer.id } } });
            const metadata = {
                campaign_name: campaign.campaign_name,
                brand_name: `${authUser.first_name} ${authUser.last_name}`,
                influencer_social_platform: influencerSocialMedias.map(item => item.social_type).join(', '),
                influencer_social_handle: influencerSocialMedias.map(item => item.social_user_name).join(', '),
            }

            for (const receiver of receivers) {
                const name1 = `${receiver.first_name} ${receiver.last_name}`;
                let mailSubject1 = emailMessages.en.adminRegistrationRequest.subject;
                if (process.env.NODE_ENV != 'production') {
                    mailSubject1 = `[${process.env.NODE_ENV.toLocaleUpperCase()}] ` + mailSubject1;
                }
                const mailObj1 = {
                    hello_user_name: emailMessages.en.hello_user_name.replace(':user_name', name1),
                    main_title: emailMessages.en.adminRegistrationRequest.title.replace(':influencerName', `${influencer.first_name} ${influencer.last_name}`),
                    descriptions: emailMessages.en.adminRegistrationRequest.description,
                    copyright: emailMessages.en.copyright,
                    all_rights_reserved: emailMessages.en.all_rights_reserved,
                    metadata: metadata
                }
                await this.SendMailService.sendMailObjectWithTemplates(receiver.email, mailSubject1, 'campaign_email_non_register_influencer', mailObj1);
            }
        }
        this.campaignRepository.saveInfluencerRecommendedScore(campaignInfluencer.id, request.influencer_id);
        // WEBSOCKET CALL & SEND CAMPAIGN INFLUENCER DETAILS 
        this.campaignRepository.socketCampaignInfluencerDetails(campaignInfluencer.id);
        return;
    }

    async quotationAproved(params: any, request: any, authUser: any) {
        // CHECK IS PERMITTED OR NOT
        const isAllowed = await this.UsersServices.checkPermission(authUser.id, 'work_order');
        if (!isAllowed) {
            throw new HttpException(responseMessages.en.permission.work_order, HttpStatus.BAD_REQUEST);
        }

        const campaign = await this.campaignRepository.checkCampaignExists(params.campaign_id, authUser);
        if (campaign.status == CampaignStatus.COMPLETED) {
            throw new HttpException(responseMessages.en.campaign.campaign_completed, HttpStatus.BAD_REQUEST);
        }
        if (!campaign.is_active) {
            throw new HttpException(responseMessages.en.campaign.campaign_inactive, HttpStatus.BAD_REQUEST);
        }
        let post_due_date = campaign.posting_end_date;
        let draft_due_date = campaign.first_draft_date;
        const today = moment().format('YYYY-MM-DD');
        if (moment(post_due_date).isBefore(today)) {
            post_due_date = today
        }
        if (moment(draft_due_date).isBefore(today)) {
            draft_due_date = today
        }

        const influencer = await this.campaignRepository.checkUserExists(request.influencer_id);

        const campaignInfluencer = await this.campaignRepository.checkCampaignInfluencerExists(params.campaign_id, request.influencer_id);

        // if (campaignInfluencer.is_chat_allowed == false) {
        //     const isChatAllowed = await this.UsersServices.checkPermission(campaign.users.id, 'chat_with_influencers');
        //     if (isChatAllowed) {
        //         campaignInfluencer.is_chat_allowed = true;
        //     }
        // }
        campaignInfluencer.is_chat_allowed = true;
        campaignInfluencer.post_due_date = post_due_date;
        campaignInfluencer.draft_due_date = draft_due_date;
        campaignInfluencer.status = InfluencerStatus.DRAFTING_DELIVERABLES;
        campaignInfluencer.influencer_status = InfluencerStatus.DRAFTING_DELIVERABLES;
        campaignInfluencer.job_status = JobStatus.INPROGRESS;
        await this.CampaignInfluencer.save(campaignInfluencer);

        // WEBSOCKET CALL & SEND CAMPAIGN INFLUENCER DETAILS 
        this.campaignRepository.socketCampaignInfluencerDetails(campaignInfluencer.id);

        const notificationMessage = notificationMessages.en.descriptions.approved_quotation.replace(':userName', `${authUser.company_name}`).replace(":campaign_name", campaign.campaign_name);;
        await this.sendCampaignNotification({
            type: 'approved_quotation',
            receiver: influencer,
            description: notificationMessage,
            sender: authUser,
            redirectType: RedirectType.JOB_INPROGRESS,
            redirectId: campaign.id,
            redirectSlug: campaign.slug,
            metadata: { campaign_id: campaign.id, campaignName: campaign.campaign_name, status: InfluencerStatus.DRAFTING_DELIVERABLES, brand_status: campaignInfluencer.status, influencer: { id: influencer.id } },
            cometChat: {
                groupId: campaignInfluencer.id,
                userId: request.influencer_id
            }
        });

        //INCREASED HIRE COUNT 
        campaign.hired = campaign.hired + 1;
        await this.Campaign.save(campaign);
        return;
    }

    async bulkQuotationAproved(request: any, authUser: any) {
        const params = { campaign_id: request.campaign_id };
        for (const influencer_id of request.influencer_ids) {
            try {
                await this.quotationAproved(params, { influencer_id }, authUser);
            } catch (error: any) {
                throw new HttpException(error.message, error.status || HttpStatus.BAD_REQUEST);
            }
        }
        return;
    }

    async updateInfluencerDueDates(params: any, request, authUser: any) {
        let campaignInfluencer = await this.CampaignInfluencer.findOne({ relations: ['influencer', 'campaign'], where: [{ id: params.campaign_influencer_id }] })
        if (!campaignInfluencer) {
            throw new HttpException(responseMessages.en.campaign.campaign_influencer_not_exists, HttpStatus.NOT_FOUND)
        }
        if (campaignInfluencer.campaign.status == CampaignStatus.COMPLETED) {
            throw new HttpException(responseMessages.en.campaign.campaign_completed, HttpStatus.BAD_REQUEST);
        }
        if (campaignInfluencer.status == InfluencerStatus.CLOSED) {
            throw new HttpException(responseMessages.en.campaign.campaign_influencer_status_close, HttpStatus.BAD_REQUEST)
        }
        campaignInfluencer.post_due_date = request.post_due_date;
        campaignInfluencer.draft_due_date = request.draft_due_date;
        await this.CampaignInfluencer.save(campaignInfluencer);
        return { campaign_influencer_id: campaignInfluencer.id };
    }
    async checkChatPermission(params: any, authUser) {
        let campaignInfluencer = await this.CampaignInfluencer.findOne({ where: [{ id: params.campaign_influencer_id }] })
        if (!campaignInfluencer) {
            throw new HttpException(responseMessages.en.campaign.campaign_influencer_not_exists, HttpStatus.NOT_FOUND)
        }

        if (campaignInfluencer.is_chat_allowed == false) {
            const isChatAllowed = await this.UsersServices.checkPermission(authUser.id, 'chat_with_influencers');
            if (isChatAllowed) {
                campaignInfluencer.is_chat_allowed = true;
                await this.CampaignInfluencer.save(campaignInfluencer);
                return { is_chat_allowed: true };
            }
        }
        return { is_chat_allowed: false };
    }

    async easyApply(params: any, request: any, authUser: any) {
        const campaign = await this.campaignRepository.checkCampaignExists(params.campaign_id, authUser);
        if (campaign.status == CampaignStatus.COMPLETED) {
            throw new HttpException(responseMessages.en.campaign.campaign_completed, HttpStatus.BAD_REQUEST);
        }
        if (!campaign.is_active) {
            throw new HttpException(responseMessages.en.campaign.campaign_inactive, HttpStatus.BAD_REQUEST);
        }
        const budget = Number(request.influencer_budget);
        if (budget > 0) {
            if (budget < 4 && campaign.currency == "HKD") {
                throw new HttpException(responseMessages.en.campaign.influencer_budget_min.replace(":min", "4 HKD"), HttpStatus.BAD_REQUEST);
            }
            if (budget < 0.5 && campaign.currency == "USD") {
                throw new HttpException(responseMessages.en.campaign.influencer_budget_min.replace(":min", "0.5 USD"), HttpStatus.BAD_REQUEST);
            }
            if (budget < 0.7 && campaign.currency == "SGD") {
                throw new HttpException(responseMessages.en.campaign.influencer_budget_min.replace(":min", "0.7 SGD"), HttpStatus.BAD_REQUEST);
            }
        }

        const influencer = await this.campaignRepository.checkUserExists(authUser.id);

        let campaignInfluencer = await this.CampaignInfluencer.findOne({ where: [{ campaign: { id: campaign.id }, influencer: { id: influencer.id } }] })
        if (!campaignInfluencer) {
            if (campaign.profile_privacy == ProfilePrivacy.PRIVATE) {
                throw new HttpException(responseMessages.en.campaign.campaign_influencer_not_exists, HttpStatus.NOT_FOUND)
            }
            campaignInfluencer = new CampaignInfluencer();
            campaignInfluencer.campaign = campaign;
            campaignInfluencer.influencer = influencer;
            campaignInfluencer.chat_id = request.chat_id;
            campaignInfluencer.post_due_date = campaign.posting_end_date;
            campaignInfluencer.draft_due_date = campaign.first_draft_date;

            // CHECK IS CHAT ALLOWED OR NOT 
            // const isAllowed = await this.UsersServices.checkPermission(campaign.users.id, 'chat_with_influencers');
            // if (isAllowed) {
            //     campaignInfluencer.is_chat_allowed = true;
            // }
        }


        campaignInfluencer.status = InfluencerStatus.QUOTATION_AVAILABLE;
        campaignInfluencer.influencer_status = InfluencerStatus.QUOTATION_AVAILABLE;
        campaignInfluencer.name = request.name;
        campaignInfluencer.is_applied = true;
        campaignInfluencer.attachment = request.attachment;
        campaignInfluencer.influencer_budget = request.influencer_budget;
        campaignInfluencer.currency = campaign.currency;
        campaignInfluencer.influencer_budget_usd = await this.CommonService.convertCurrencyRate(campaign.currency, request.influencer_budget);
        campaignInfluencer.message = request.message;
        campaignInfluencer = await this.CampaignInfluencer.save(campaignInfluencer);

        const notificationMessage = notificationMessages.en.descriptions.easy_apply.replace(':userName', `${authUser.display_name}`).replace(":campaign_name", campaign.campaign_name);
        const metadata = { campaign_id: campaign.id, status: InfluencerStatus.QUOTATION_AVAILABLE, campaignName: campaign.campaign_name, brand_status: campaignInfluencer.status, influencer: { id: influencer.id } }
        await this.sendCampaignNotification({
            type: 'easy_apply',
            receiver: campaign.users,
            description: notificationMessage,
            sender: authUser,
            redirectType: RedirectType.CAMPAIGN_MANAGEMENT,
            redirectId: campaign.id,
            redirectSlug: campaign.slug,
            metadata,
            cometChat: {
                groupId: campaignInfluencer.id,
                userId: authUser.id
            }
        });

        // WEBSOCKET CALL & SEND CAMPAIGN INFLUENCER DETAILS 
        this.campaignRepository.socketCampaignInfluencerDetails(campaignInfluencer.id);
        const invitedCount = await this.CampaignInfluencer.createQueryBuilder('campaignInfluencer')
            .innerJoin('campaignInfluencer.campaign', 'campaign')
            .where('campaignInfluencer.influencer = :influencerIds', { influencerIds: authUser.id })
            .andWhere('campaignInfluencer.influencer_status = :status', { status: InfluencerStatus.WAITING_FOR_QUOTATIONS })
            .andWhere('campaign.deleted_at IS NULL')
            .getCount();

        campaign.applied = campaign.applied + 1;
        this.Campaign.save(campaign);

        this.campaignRepository.addHotCategory(campaign);

        this.campaignRepository.saveInfluencerRecommendedScore(campaignInfluencer.id, authUser.id);
        return { campaign_influencer_id: campaignInfluencer.id, invited_count: invitedCount };
    }



    async otherAttachment(params: any, request: any, authUser: any) {
        let campaignInfluencer = await this.CampaignInfluencer.findOne({ relations: ['influencer', 'campaign'], where: [{ id: params.campaign_influencer_id }] })
        if (!campaignInfluencer) {
            throw new HttpException(responseMessages.en.campaign.campaign_influencer_not_exists, HttpStatus.NOT_FOUND)
        }
        if (campaignInfluencer.campaign.status == CampaignStatus.COMPLETED) {
            throw new HttpException(responseMessages.en.campaign.campaign_completed, HttpStatus.BAD_REQUEST);
        }
        if (campaignInfluencer.status == InfluencerStatus.CLOSED) {
            throw new HttpException(responseMessages.en.campaign.campaign_influencer_status_close, HttpStatus.BAD_REQUEST)
        }
        if (campaignInfluencer.chat_attachment == null) {
            campaignInfluencer.chat_attachment = [request.attachment];
        } else {
            campaignInfluencer.chat_attachment.push(request.attachment);
        }

        await this.CampaignInfluencer.save(campaignInfluencer);
        return { attachments: campaignInfluencer.attachment, chat_attachment: campaignInfluencer.chat_attachment };
    }

    async editPrice(params: any, request: any, authUser: any) {
        let campaignInfluencer = await this.CampaignInfluencer.findOne({ relations: ['influencer', 'campaign'], where: [{ id: params.campaign_influencer_id }] })
        if (!campaignInfluencer) {
            throw new HttpException(responseMessages.en.campaign.campaign_influencer_not_exists, HttpStatus.NOT_FOUND)
        }
        if (campaignInfluencer.campaign.status == CampaignStatus.COMPLETED) {
            throw new HttpException(responseMessages.en.campaign.campaign_completed, HttpStatus.BAD_REQUEST);
        }
        if (campaignInfluencer.status == InfluencerStatus.CLOSED) {
            throw new HttpException(responseMessages.en.campaign.campaign_influencer_status_close, HttpStatus.BAD_REQUEST)
        }
        if (!campaignInfluencer.campaign.is_active) {
            throw new HttpException(responseMessages.en.campaign.campaign_inactive, HttpStatus.BAD_REQUEST);
        }
        const budget = Number(request.influencer_budget);
        if (budget > 0) {
            if (budget < 4 && campaignInfluencer.currency == "HKD") {
                throw new HttpException(responseMessages.en.campaign.influencer_budget_min.replace(":min", "4 HKD"), HttpStatus.BAD_REQUEST);
            }
            if (budget < 0.5 && campaignInfluencer.currency == "USD") {
                throw new HttpException(responseMessages.en.campaign.influencer_budget_min.replace(":min", "0.5 USD"), HttpStatus.BAD_REQUEST);
            }
            if (budget < 0.7 && campaignInfluencer.currency == "SGD") {
                throw new HttpException(responseMessages.en.campaign.influencer_budget_min.replace(":min", "0.7 SGD"), HttpStatus.BAD_REQUEST);
            }
        }
        const stageName = PRICE_CHANGE_STAGE_LABELS[campaignInfluencer.status];
        const isPriceChangeStage = !!stageName;
        const currency = campaignInfluencer.currency || campaignInfluencer.campaign.currency;
        const convertedBudget = await this.CommonService.convertCurrencyRate(campaignInfluencer.campaign.currency, request.influencer_budget);
        const oldPrice = this.formatCampaignPrice(currency, campaignInfluencer.influencer_budget);
        const newPrice = this.formatCampaignPrice(currency, request.influencer_budget);

        if (isPriceChangeStage) {
            campaignInfluencer.influencer_budget_new = request.influencer_budget;
            campaignInfluencer.influencer_budget_usd_new = convertedBudget;
            campaignInfluencer.is_budget_changed = true;
        } else {
            campaignInfluencer.influencer_budget = request.influencer_budget;
            campaignInfluencer.influencer_budget_usd = convertedBudget;
        }
        campaignInfluencer.message = request.message;
        campaignInfluencer.name = request.name;
        campaignInfluencer.is_campaign_update = false;
        campaignInfluencer.attachment = request.attachment;
        campaignInfluencer.currency = campaignInfluencer.campaign.currency;
        await this.CampaignInfluencer.save(campaignInfluencer);
        const campaign = await this.campaignRepository.checkCampaignExists(campaignInfluencer.campaign.id, authUser);

        if (isPriceChangeStage) {
            await this.sendPriceChangeRequestNotification(campaign, campaignInfluencer, authUser, {
                stageName,
                oldPrice,
                newPrice,
                message: request.message,
            });
        } else {
            const notificationMessage = notificationMessages.en.descriptions.edit_price.replace(':userName', `${authUser.display_name}`).replace(":campaign_name", campaign.campaign_name);
            const metadata = { campaign_id: campaign.id, status: campaignInfluencer.status, brand_status: campaignInfluencer.status, influencer: { id: authUser.id }, campaignName: campaign.campaign_name }
            await this.sendCampaignNotification({
                type: 'edit_price',
                receiver: campaign.users,
                description: notificationMessage,
                sender: authUser,
                redirectType: RedirectType.CAMPAIGN_MANAGEMENT,
                redirectId: campaign.id,
                redirectSlug: campaign.slug,
                metadata,
                cometChat: {
                    groupId: campaignInfluencer.id,
                    userId: authUser.id
                }
            });
        }

        // WEBSOCKET CALL & SEND CAMPAIGN INFLUENCER DETAILS 
        this.campaignRepository.socketCampaignInfluencerDetails(campaignInfluencer.id);

        const invitedCount = await this.CampaignInfluencer.createQueryBuilder('campaignInfluencer')
            .innerJoin('campaignInfluencer.campaign', 'campaign')
            .where('campaignInfluencer.influencer = :influencerIds', { influencerIds: authUser.id })
            .andWhere('campaignInfluencer.influencer_status = :status', { status: InfluencerStatus.WAITING_FOR_QUOTATIONS })
            .andWhere('campaign.deleted_at IS NULL')
            .getCount();
        return { campaign_influencer_id: campaignInfluencer.id, invited_count: invitedCount };
    }

    private formatCampaignPrice(currency: string, amount: any): string {
        const value = Number(amount);
        const formatted = Number.isFinite(value)
            ? (Number.isInteger(value) ? String(value) : value.toFixed(2).replace(/\.00$/, ''))
            : String(amount ?? '').replace(/\.00$/, '');
        return currency ? `${currency} ${formatted}` : formatted;
    }

    private async sendPriceChangeRequestNotification(campaign, campaignInfluencer, authUser, extras: { stageName: string, oldPrice: string, newPrice: string, message?: string }) {
        const notificationMessage = notificationMessages.en.descriptions.price_change_request
            .replace(':userName', authUser.display_name)
            .replace(':campaign_name', campaign.campaign_name)
            .replace(':stage', extras.stageName);
        const metadata = {
            campaign_id: campaign.id,
            campaignName: campaign.campaign_name,
            influencerName: authUser.display_name,
            status: campaignInfluencer.status,
            brand_status: campaignInfluencer.status,
            stage: extras.stageName,
            oldPrice: extras.oldPrice,
            newPrice: extras.newPrice,
            message: extras.message || 'No additional note provided.',
            campaign_influencer_id: campaignInfluencer.id,
            influencer: { id: authUser.id },
        };
        await this.sendCampaignNotification({
            type: 'price_change_request',
            receiver: campaign.users,
            sender: authUser,
            description: notificationMessage,
            redirectType: RedirectType.CAMPAIGN_MANAGEMENT,
            redirectId: campaign.id,
            redirectSlug: campaign.slug,
            metadata,
            cometChat: {
                groupId: campaignInfluencer.id,
                userId: authUser.id,
                messageType: 'status'
            }
        });
    }

    async approvePriceChange(params: any, authUser: any) {
        const campaignInfluencer = await this.CampaignInfluencer.findOne({
            relations: ['influencer', 'campaign', 'campaign.users'],
            where: [{ id: params.campaign_influencer_id }]
        });
        if (!campaignInfluencer) {
            throw new HttpException(responseMessages.en.campaign.campaign_influencer_not_exists, HttpStatus.NOT_FOUND);
        }
        if (campaignInfluencer.campaign.users.id !== authUser.id) {
            throw new HttpException(responseMessages.en.campaign.campaign_not_allowed, HttpStatus.FORBIDDEN);
        }
        if (campaignInfluencer.campaign.status == CampaignStatus.COMPLETED) {
            throw new HttpException(responseMessages.en.campaign.campaign_completed, HttpStatus.BAD_REQUEST);
        }
        if (campaignInfluencer.status == InfluencerStatus.CLOSED) {
            throw new HttpException(responseMessages.en.campaign.campaign_influencer_status_close, HttpStatus.BAD_REQUEST);
        }
        if (!campaignInfluencer.campaign.is_active) {
            throw new HttpException(responseMessages.en.campaign.campaign_inactive, HttpStatus.BAD_REQUEST);
        }
        if (!campaignInfluencer.is_budget_changed) {
            throw new HttpException(responseMessages.en.campaign.price_change_not_pending, HttpStatus.BAD_REQUEST);
        }

        const stageName = PRICE_CHANGE_STAGE_LABELS[campaignInfluencer.status] || 'Active';
        const currency = campaignInfluencer.currency || campaignInfluencer.campaign.currency;
        const newPrice = this.formatCampaignPrice(currency, campaignInfluencer.influencer_budget_new);
        const brandName = authUser.company_name || authUser.display_name;

        campaignInfluencer.influencer_budget = campaignInfluencer.influencer_budget_new;
        campaignInfluencer.influencer_budget_usd = campaignInfluencer.influencer_budget_usd_new;
        campaignInfluencer.influencer_budget_new = 0;
        campaignInfluencer.influencer_budget_usd_new = 0;
        campaignInfluencer.is_budget_changed = false;
        await this.CampaignInfluencer.save(campaignInfluencer);

        const campaign = campaignInfluencer.campaign;
        const notificationMessage = notificationMessages.en.descriptions.price_change_approved
            .replace(':userName', brandName)
            .replace(':campaign_name', campaign.campaign_name);
        const metadata = {
            campaign_id: campaign.id,
            campaignName: campaign.campaign_name,
            brandName,
            influencerName: campaignInfluencer.influencer.display_name,
            status: campaignInfluencer.influencer_status,
            brand_status: campaignInfluencer.status,
            stage: stageName,
            newPrice,
            campaign_influencer_id: campaignInfluencer.id,
            influencer: { id: campaignInfluencer.influencer.id },
        };
        await this.sendCampaignNotification({
            type: 'price_change_approved',
            receiver: campaignInfluencer.influencer,
            sender: authUser,
            description: notificationMessage,
            redirectType: RedirectType.JOB_INPROGRESS,
            redirectId: campaign.id,
            redirectSlug: campaign.slug,
            metadata,
            cometChat: {
                groupId: campaignInfluencer.id,
                userId: authUser.id,
                messageType: 'status'
            }
        });

        this.campaignRepository.socketCampaignInfluencerDetails(campaignInfluencer.id);
        return;
    }

    async deleteDraft(params: any, authUser: any) {
        try {
            const draft = await this.Draft.findOne({ relations: ['campaign_influencer'], where: [{ id: params.draft_id }] })
            console.log(draft)
            if (!draft) {
                throw new HttpException(responseMessages.en.campaign.draft_not_exists, HttpStatus.NOT_FOUND)
            }
            if (draft.status == DraftStatus.APPROVED) {
                throw new HttpException(responseMessages.en.campaign.not_delete_approved_draft, HttpStatus.BAD_REQUEST);
            }
            console.log(draft.campaign_influencer)
            const campaignInfluencer = await this.CampaignInfluencer.findOne({ relations: ['campaign'], where: [{ id: draft.campaign_influencer.id }] })
            if (!campaignInfluencer) {
                throw new HttpException(responseMessages.en.campaign.campaign_influencer_not_exists, HttpStatus.NOT_FOUND)
            }

            // CREATE NOTIFICATION
            const campaign = await this.campaignRepository.checkCampaignExists(campaignInfluencer.campaign.id, authUser);
            const notificationMessage = notificationMessages.en.descriptions.delete_draft.replace(':userName', `${authUser.display_name}`).replace(":campaign_name", campaign.campaign_name);
            const metadata = { campaign_id: campaign.id, status: InfluencerStatus.DRAFT_AVAILABLE, brand_status: campaignInfluencer.status, influencer: { id: authUser.id }, campaignName: campaign.campaign_name }
            //   this.campaignRepository.notification(campaign.users, notificationMessage, authUser, RedirectType.CAMPAIGN_MANAGEMENT, campaign.id, campaign.slug, metadata, true, 'draft_deleted');
            this.sendCampaignNotification({
                type: 'delete_draft',
                receiver: campaign.users,
                description: notificationMessage,
                sender: authUser,
                redirectType: RedirectType.CAMPAIGN_MANAGEMENT,
                redirectId: campaign.id,
                redirectSlug: campaign.slug,
                metadata: metadata,
                cometChat: {
                    groupId: campaignInfluencer.id,
                    userId: authUser.id
                }
            });
            await this.Draft.softDelete({ id: draft.id });

            const influencerDraftCount = await this.Draft.count({ where: { campaign_influencer: { id: campaignInfluencer.id } } });
            // IF INFLUENCER HAS NO DRAFTS, SET THE INFLUENCER STATUS TO DRAFTING_DELIVERABLES
            if (influencerDraftCount <= 0) {
                campaignInfluencer.status = InfluencerStatus.DRAFTING_DELIVERABLES;
                campaignInfluencer.influencer_status = InfluencerStatus.DRAFTING_DELIVERABLES;
                await this.CampaignInfluencer.save(campaignInfluencer);
            } else {
                const approvedDraftCount = await this.Draft.count({ where: { campaign_influencer: { id: campaignInfluencer.id }, status: DraftStatus.APPROVED } });
                if (approvedDraftCount == influencerDraftCount) {
                    campaignInfluencer.status = InfluencerStatus.POSTING_DELIVERABLES;
                    campaignInfluencer.influencer_status = InfluencerStatus.POSTING_DELIVERABLES;
                    await this.CampaignInfluencer.save(campaignInfluencer);
                }
            }
            // WEBSOCKET CALL & SEND CAMPAIGN INFLUENCER DETAILS 
            this.campaignRepository.socketCampaignInfluencerDetails(campaignInfluencer.id);
            return;
        } catch (error: any) {
            console.log(error)
            throw new HttpException(error.message, HttpStatus.BAD_REQUEST)
        }
    }


    async addDraft(params: any, request: any, authUser: any) {
        let campaignInfluencer = await this.CampaignInfluencer.findOne({ relations: ['influencer', 'campaign'], where: [{ id: params.campaign_influencer_id }] })
        if (!campaignInfluencer) {
            throw new HttpException(responseMessages.en.campaign.campaign_influencer_not_exists, HttpStatus.NOT_FOUND)
        }
        if (campaignInfluencer.campaign.status == CampaignStatus.COMPLETED) {
            throw new HttpException(responseMessages.en.campaign.campaign_completed, HttpStatus.BAD_REQUEST);
        }
        if (campaignInfluencer.status == InfluencerStatus.CLOSED) {
            throw new HttpException(responseMessages.en.campaign.campaign_influencer_status_close, HttpStatus.BAD_REQUEST)
        }
        await this.campaignRepository.addDraft(request, campaignInfluencer);
        campaignInfluencer.status = InfluencerStatus.DRAFT_AVAILABLE;
        campaignInfluencer.influencer_status = InfluencerStatus.DRAFT_AVAILABLE;
        campaignInfluencer.notification_reminder_at = moment.utc().add(Constants.CAMPAIGN_BRAND_STATUS_NOTIFICATION_REMINDER, 'hours').format('YYYY-MM-DD HH:mm:ss');
        campaignInfluencer.draft_notification_sent = 0;
        let arrpoveCount = await this.Draft.count({ where: { campaign_influencer: { id: params.campaign_influencer_id }, status: DraftStatus.APPROVED } });
        if (arrpoveCount) {
            campaignInfluencer.influencer_status = InfluencerStatus.POSTING_DELIVERABLES;
        }

        await this.CampaignInfluencer.save(campaignInfluencer);
        const campaign = await this.campaignRepository.checkCampaignExists(campaignInfluencer.campaign.id, authUser);
        const notificationMessage = notificationMessages.en.descriptions.add_draft.replace(':userName', `${authUser.display_name}`).replace(":campaign_name", campaign.campaign_name);
        const metadata = { campaign_id: campaign.id, status: InfluencerStatus.DRAFT_AVAILABLE, brand_status: campaignInfluencer.status, "campaign_influencer_id": campaignInfluencer.id, influencer: { id: authUser.id }, campaignName: campaign.campaign_name }
        await this.sendCampaignNotification({
            type: 'add_draft',
            receiver: campaign.users,
            description: notificationMessage,
            sender: authUser,
            redirectType: RedirectType.CAMPAIGN_MANAGEMENT,
            redirectId: campaign.id,
            redirectSlug: campaign.slug,
            metadata,
            cometChat: {
                groupId: campaignInfluencer.id,
                userId: authUser.id
            }
        });

        // WEBSOCKET CALL & SEND CAMPAIGN INFLUENCER DETAILS 
        this.campaignRepository.socketCampaignInfluencerDetails(campaignInfluencer.id);
        return;
    }

    async approveDraft(params: any, request, authUser) {

        const campaignInfluencer = await this.CampaignInfluencer.findOne({ relations: ['influencer', 'campaign'], where: [{ id: params.campaign_influencer_id }] })
        if (campaignInfluencer.campaign.status == CampaignStatus.COMPLETED) {
            throw new HttpException(responseMessages.en.campaign.campaign_completed, HttpStatus.BAD_REQUEST);
        }
        if (campaignInfluencer.status == InfluencerStatus.CLOSED) {
            throw new HttpException(responseMessages.en.campaign.campaign_influencer_status_close, HttpStatus.BAD_REQUEST)
        }

        const draft = await this.campaignRepository.checkDraftExists(request.draft_id);
        if (draft.status == DraftStatus.APPROVED) {
            throw new HttpException(responseMessages.en.campaign.draft_already_approved, HttpStatus.BAD_REQUEST);
        }
        draft.status = DraftStatus.APPROVED;
        await this.Draft.save(draft);

        await this.DraftVersions.update({ id: request.draft_version_id }, { status: DraftVersionStatus.APPROVED });
        campaignInfluencer.influencer_status = InfluencerStatus.POSTING_DELIVERABLES;
        let pendingCount = await this.Draft.count({ where: { campaign_influencer: { id: params.campaign_influencer_id }, status: DraftStatus.PENDING } });
        if (!pendingCount) {
            campaignInfluencer.status = InfluencerStatus.POSTING_DELIVERABLES;
        }
        campaignInfluencer.notification_reminder_at = moment.utc().add(Constants.CAMPAIGN_INFLUENCER_STATUS_NOTIFICATION_REMINDER, 'hours').format('YYYY-MM-DD HH:mm:ss');

        await this.CampaignInfluencer.save(campaignInfluencer);
        // WEBSOCKET CALL & SEND CAMPAIGN INFLUENCER DETAILS 
        this.campaignRepository.socketCampaignInfluencerDetails(campaignInfluencer.id);
        const metadata = { campaign_id: campaignInfluencer.campaign.id, campaignName: campaignInfluencer.campaign.campaign_name, status: campaignInfluencer.influencer_status, brand_status: campaignInfluencer.status, influencer: { id: campaignInfluencer.influencer.id } };
        const notificationMessage = notificationMessages.en.descriptions.draft_approved.replace(':userName', `${authUser.company_name}`).replace(":campaign_name", campaignInfluencer.campaign.campaign_name);
        await this.sendCampaignNotification({
            type: 'draft_approved',
            receiver: campaignInfluencer.influencer,
            description: notificationMessage,
            sender: authUser,
            redirectType: RedirectType.JOB_INPROGRESS,
            redirectId: campaignInfluencer.campaign.id,
            redirectSlug: campaignInfluencer.campaign.slug,
            metadata,
            cometChat: {
                groupId: campaignInfluencer.id,
                userId: authUser.id
            }
        });
        return;
    }

    async isArchived(params: any, authUser) {
        const campaignInfluencer = await this.CampaignInfluencer.findOne({ relations: ['influencer', 'campaign'], where: [{ id: params.campaign_influencer_id }] })
        if (!campaignInfluencer) {
            throw new HttpException(responseMessages.en.campaign.campaign_influencer_not_exists, HttpStatus.NOT_FOUND)
        }
        if (campaignInfluencer.is_archived) {
            campaignInfluencer.status = campaignInfluencer.influencer_status;
            campaignInfluencer.is_archived = false;
            campaignInfluencer.is_chat_allowed = false;
            if (campaignInfluencer.influencer_status == InfluencerStatus.DRAFTING_DELIVERABLES) {
                const isAllowed = await this.UsersServices.checkPermission(authUser.id, 'work_order');
                if (!isAllowed) {
                    throw new HttpException(responseMessages.en.permission.work_order, HttpStatus.BAD_REQUEST);
                }
            }

        } else {
            if (campaignInfluencer.status == InfluencerStatus.DRAFTING_DELIVERABLES) {
                const chatCount = 1;
                await this.UsersServices.increasePermissionCount(authUser.id, "work_order", chatCount);
            }
            campaignInfluencer.status = InfluencerStatus.CLOSED;
            campaignInfluencer.is_archived = true;
        }

        await this.CampaignInfluencer.save(campaignInfluencer);
        // WEBSOCKET CALL & SEND CAMPAIGN INFLUENCER DETAILS 
        this.campaignRepository.socketCampaignInfluencerDetails(campaignInfluencer.id);
        // SLEEP FOR 1 SECOND
        await new Promise(resolve => setTimeout(resolve, 100));
        const tags = [campaignInfluencer.status, campaignInfluencer.campaign.id];
        const metadata = { campaign_id: campaignInfluencer.campaign.id, campaignName: campaignInfluencer.campaign.campaign_name, brand_status: campaignInfluencer.status, influencer: { id: campaignInfluencer.influencer.id }, status: campaignInfluencer.status }
        this.campaignRepository.updateCometChatGroupTags(
            campaignInfluencer.id,
            tags,
            metadata
        );

        return;
    }

    async addDraftComment(params: any, request: any, authUser: any) {
        await this.campaignRepository.addDraftComment(params.draft_id, request.comment, authUser);
        // SEND COMET MESSAGE
        const darft = await this.Draft.findOne({ relations: ['campaign', 'campaign_influencer'], where: [{ id: params.draft_id }] })
        if (darft.campaign.status == CampaignStatus.COMPLETED) {
            throw new HttpException(responseMessages.en.campaign.campaign_completed, HttpStatus.BAD_REQUEST);
        }
        let metadata = { campaign_id: null, campaignName: "", status: "", brand_status: "", influencer: { id: authUser.id }, campaign_influencer_id: null };
        if (darft) {
            let campaign = darft.campaign;
            let sender = authUser
            let receiver = {};
            metadata = { campaign_id: campaign.id, status: darft.campaign_influencer.status, brand_status: darft.campaign_influencer.status, influencer: { id: authUser.id }, campaignName: campaign.campaign_name, campaign_influencer_id: authUser.id }

            let redirect_type = RedirectType.CAMPAIGN_MANAGEMENT;
            let userName = authUser.display_name;
            if (authUser.role == 'influencer') {
                const camp = await this.campaignRepository.checkCampaignExists(campaign.id, authUser);
                receiver = camp.users;
            } else {
                const campInfluencer = await this.CampaignInfluencer.findOne({ relations: ['influencer'], where: { id: darft.campaign_influencer.id } });
                receiver = campInfluencer.influencer;
                metadata.influencer = { id: campInfluencer.influencer.id };
                redirect_type = RedirectType.JOB_INPROGRESS;
                userName = authUser.company_name;
            }

            const notificationMessage = notificationMessages.en.descriptions.add_draft_comment.replace(':userName', userName).replace(":campaign_name", campaign.campaign_name);;
            await this.sendCampaignNotification({
                type: 'add_draft_comment',
                receiver: receiver,
                description: notificationMessage,
                sender: sender,
                redirectType: redirect_type,
                redirectId: campaign.id,
                redirectSlug: campaign.slug,
                metadata,
                cometChat: {
                    groupId: darft.campaign_influencer.id,
                    userId: authUser.id
                }
            });
        }
        return;
    }

    async getDraftComment(params: any, authUser: any) {
        const data = await this.DraftComments.createQueryBuilder('draftComments')
            .leftJoin('draftComments.fromUser', 'fromUser')
            .where('draftComments.draft_id = :draft_id', { draft_id: params.draft_id })
            .select([
                'draftComments.id',
                'draftComments.comment',
                'draftComments.created_at',
                'fromUser.id',
                'fromUser.slug',
                'fromUser.first_name',
                'fromUser.last_name',
                "fromUser.display_name",
                'fromUser.email',
                'fromUser.image',
                'fromUser.role'
            ])
            .getMany();
        return { data: data }
    }


    async influencerCampaignList(queryParam: any, authUser: any) {

        const reqPage = queryParam.page ? queryParam.page : 0
        const page = queryParam.page - 1
        const pageSize = queryParam.per_page ? queryParam.per_page : 10
        const offset = page * pageSize
        const nextPage = page + 1
        const nextOffset = nextPage * pageSize
        const user = queryParam.user_id ? await this.Users.findOne({ where: { id: queryParam.user_id } }) : authUser;
        if (!user) {
            throw new HttpException(responseMessages.en.user.user_not_exists, HttpStatus.NOT_FOUND);
        }
        let sortByArr = {
            campaign_name: "campaign.campaign_name",
            budget_per_influencer: "campaign.budget_per_influencer",
            number_of_influencers: "campaign.number_of_influencers",
            created_at: "campaign.created_at",
            updated_at: "campaignInfluencers.updatedAt",
            latest_saved: "influencerSaveCampaigns.createdAt",
            posting_start_date: "campaign.posting_start_date",
            posting_end_date: "campaign.posting_end_date",
            first_draft_date: "campaign.first_draft_date",
            profile_privacy: "campaign.profile_privacy"
        }

        let sort_by = queryParam.sort_by || "created_at"
        let sort_type = queryParam.sort || "DESC"
        sort_by = sortByArr[sort_by]

        const languageCode = user.language || 'en';
        let queryBuilder = await this.campaignRepository.commonQuery(languageCode, queryParam.country_id);

        let query = queryBuilder.leftJoinAndSelect('campaign.campaignInfluencers', 'campaignInfluencers', 'campaignInfluencers.influencerId = :influencerId', { influencerId: user.id }).withDeleted()
            .leftJoin('campaignInfluencers.influencer', 'influencer')
            .addSelect(['influencer.id', 'influencer.slug', 'influencer.first_name', 'influencer.last_name',
                'influencer.email', 'influencer.role', 'influencer.country_code', 'influencer.country',
                'influencer.phone_number', 'influencer.image', 'influencer.gender', 'influencer.total_followers',
                'influencer.total_following', 'influencer.engagement_ratio', 'influencer.platforms'])
            .leftJoinAndSelect('campaign.influencerSaveCampaigns', 'influencerSaveCampaigns')
            .leftJoin('influencerSaveCampaigns.influencer', 'campaignSaveInfluencer')
            .loadRelationCountAndMap(
                'campaign.isCampaignSaved',
                'campaign.influencerSaveCampaigns',
                'saveCountByMe',
                (qb) => qb.where('saveCountByMe.influencer = :influencerid', { influencerid: user.id })
            )
            .andWhere('(campaignInfluencers.id IS NULL OR campaignInfluencers.job_status IS NULL OR campaignInfluencers.job_status != :closed)', { closed: 'closed' })
            .andWhere('campaign.deleted_at IS NULL')

        //  query = this.campaignRepository.applyInternationalOnlyVisibilityForInfluencer(query, user.country);

        if (queryParam.category_id) {
            if (queryParam.category_id == 'other') {
                const categoryIds = await this.campaignRepository.getNonRecommendedCategoryIdsByCountry(queryParam.country_id);
                if (categoryIds.length > 0) {
                    query = query.andWhere('campaignCategory.category_id IN (:...categoryIds)', { categoryIds })
                        .addOrderBy('campaign.created_at', 'DESC');
                } else {
                    query = query.andWhere('1 = 0');
                }
            } else {
                query = query.andWhere('campaignCategory.category_id = :category_id', { category_id: queryParam.category_id }).orderBy('countryOrder.order_by', 'DESC').addOrderBy('campaign.created_at', 'DESC');
                query = await this.campaignRepository.applyInternationalTabHomeCountryExclusion(query, queryParam.category_id, user.country);
            }
        } else {
            query = query.orderBy(sort_by, sort_type);
        }
        if (queryParam.country_id && queryParam.status != 'applied' && queryParam.status != 'invited') {
            query = query.andWhere('countryOrder.country_id = :country_id', { country_id: queryParam.country_id })
                .andWhere('countryOrder.is_hidden = :isHidden', { isHidden: false });
        }
        if (queryParam.status == 'all') {
            query = query.andWhere('campaign.platforms ?| :platforms::text[]', { platforms: user.platforms ?? [] })
                .andWhere('campaign.status = :campaignStatus', { campaignStatus: CampaignStatus.RUNNING })
                .andWhere('campaign.is_active = :is_active', { is_active: true })
                .andWhere('campaign.profile_privacy = :profile_privacy', { profile_privacy: ProfilePrivacy.PUBLIC });
        }
        if (queryParam.status == 'invited') {
            query = query.andWhere('campaignInfluencers.influencer_status = :influencer_status', { influencer_status: InfluencerStatus.WAITING_FOR_QUOTATIONS })
            // .andWhere('campaignInfluencers.influencerId = :influencerId', { influencerId: user.id });   
        }

        if (queryParam.status == 'applied') {
            query = query.andWhere('campaignInfluencers.influencer_status = :influencer_status', { influencer_status: InfluencerStatus.QUOTATION_AVAILABLE })
            //.andWhere('campaignInfluencers.influencerId = :influencerId', { influencerId: user.id })
        }
        if (queryParam.status == 'archived') {
            query = query.andWhere('campaignInfluencers.influencer_status = :influencer_status', { influencer_status: InfluencerStatus.ARCHIVED })
            //.andWhere('campaignInfluencers.influencerId = :influencerId', { influencerId: user.id })
        }

        if (queryParam.status == 'saved') {
            query = query.andWhere(' influencerSaveCampaigns.influencer_id = :influencerid', { influencerid: user.id })
                .andWhere('(campaignInfluencers.id IS NULL OR campaignInfluencers.influencer_status != :influencer_status)', { influencer_status: 'archived' })
        }

        if (queryParam.search) {
            query = query.andWhere(`(campaign.campaign_name ILIKE :search 
                OR CONCAT(users.first_name, ' ', users.last_name) ILIKE :search 
                OR users.company_name ILIKE :search )`,
                { search: `%${queryParam.search}%` })
        }

        if (reqPage !== 0) {
            query = query.skip(offset).take(pageSize)
        }

        const [results, total] = await query.getManyAndCount()
        const invitedCount = await this.getInfluencerInvitedCount(user.id, queryParam.country_id);
        return {
            data: results,
            total: total,
            invited_count: invitedCount,
            hasMoreResults: total > nextOffset ? 1 : 0
        }
    }

    async influencerRecommendedCampaign(queryParam: any, user: any) {
        const reqPage = queryParam.page ? queryParam.page : 0
        const page = queryParam.page - 1
        const pageSize = queryParam.per_page ? queryParam.per_page : 10
        const offset = page * pageSize
        const nextPage = page + 1
        const nextOffset = nextPage * pageSize
        // CREATE NEW RECOMMENDED CAMPAIGN EVERY 2 HOURS
        if (user.recommended_date_time == null || moment(user.recommended_date_time).isBefore(moment())) {
            await this.campaignRepository.saveRecommendedCampaign(user.id);
        }

        let query = await this.InfluencerCampaignRecommended.createQueryBuilder('recommended')
            .leftJoinAndSelect("recommended.campaign", "campaign")
            .leftJoinAndSelect("campaign.users", "users")
            .leftJoinAndSelect("campaign.campaignCategory", "campaignCategory")
            .leftJoinAndSelect("campaignCategory.category", "category", 'category.is_active = true')
            .leftJoinAndSelect('campaign.campaignInfluencers', 'campaignInfluencers', 'campaignInfluencers.influencerId = :influencerId', { influencerId: user.id }).withDeleted()
            .leftJoin('campaign.influencerSaveCampaigns', 'influencerSaveCampaigns')
            .leftJoin('influencerSaveCampaigns.influencer', 'campaignSaveInfluencer')
            .loadRelationCountAndMap(
                'campaign.isCampaignSaved',
                'campaign.influencerSaveCampaigns',
                'saveCountByMe',
                (qb) => qb.where('saveCountByMe.influencer = :influencerid', { influencerid: user.id })
            )
            .select(["recommended.id", "recommended.createdAt", "campaignInfluencers",
                "campaign.id", "campaign.campaign_name", "campaign.platforms", "campaign.slug", "campaign.applied",
                "campaign.currency", "campaign.budget_per_influencer", "campaign.number_of_influencers", "campaign.product_value",
                "campaign.total_budget", "campaign.content_type", "campaign.influencer_type", "campaign.job_requirement",
                "campaign.posting_start_date", "campaign.posting_end_date", "campaign.first_draft_date",
                "campaign.campaign_image", "campaign.profile_privacy", "campaign.status",
                "campaign.campaign_requirements", "campaign.attachment",
                "campaign.messages", "campaign.invited", "campaign.hired", "campaign.campaign_country",
                "campaign.created_at", "users.id", "users.slug", "users.first_name",
                "users.last_name", "users.email", "users.image", "users.country", "users.about_me",
                "users.is_offline_payment_allowed", "users.offline_payment_allowed", "campaignCategory.id",
                "category.id", "category.slug", "category.title"])

            .where('recommended.influencer_id = :influencerId', { influencerId: user.id })
            .orderBy('recommended.createdAt', 'DESC');

        if (queryParam.search) {
            query = query.andWhere(`(campaign.campaign_name ILIKE :search OR CONCAT(users.first_name, ' ', users.last_name) ILIKE :search)`, { search: `%${queryParam.search}%` })
        }

        if (reqPage !== 0) {
            query = query.skip(offset).take(pageSize)
        }

        const [results, total] = await query.getManyAndCount()

        return {
            data: results,
            total: total,
            hasMoreResults: total > nextOffset ? 1 : 0
        }
    }

    async influencerMyJob(queryParam: any, authUser: any) {
        const reqPage = queryParam.page ? queryParam.page : 0
        const page = queryParam.page - 1
        const pageSize = queryParam.per_page ? queryParam.per_page : 10
        const offset = page * pageSize
        const nextPage = page + 1
        const nextOffset = nextPage * pageSize
        const user = queryParam.user_id ? await this.Users.findOne({ where: { id: queryParam.user_id } }) : authUser;
        if (!user) {
            throw new HttpException(responseMessages.en.user.user_not_exists, HttpStatus.NOT_FOUND);
        }

        let sortByArr = {
            campaign_name: "campaign.campaign_name",
            budget_per_influencer: "campaign.budget_per_influencer",
            number_of_influencers: "campaign.number_of_influencers",
            created_at: "campaignInfluencers.createdAt",
            updated_at: "campaignInfluencers.updatedAt",
            draft_due_date: "campaignInfluencers.draft_due_date",
            posting_start_date: "campaign.posting_start_date",
            posting_end_date: "campaign.posting_end_date",
            first_draft_date: "campaign.first_draft_date",
            profile_privacy: "campaign.profile_privacy"
        }

        let sort_by = queryParam.sort_by || "updated_at"
        let sort_type = queryParam.sort || "DESC"
        sort_by = sortByArr[sort_by] || sortByArr["updated_at"]

        const languageCode = user.language || 'en';
        let queryBuilder = await this.campaignRepository.commonQuery(languageCode); // Resolve the promise

        let query = queryBuilder.leftJoinAndSelect('campaign.campaignInfluencers', 'campaignInfluencers').withDeleted()
            .where('campaignInfluencers.influencerId = :influencerId', { influencerId: user.id })
            .orderBy(sort_by, sort_type);
        if (queryParam.sort_by == 'draft_due_date') {
            query = query.addOrderBy('campaignInfluencers.post_due_date', sort_type);
        }
        if (queryParam.job_status === 'inprogress') {
            query.andWhere('campaignInfluencers.job_status = :job_status', { job_status: 'inprogress' });
        } else if (queryParam.job_status === 'closed') {
            query.andWhere('campaignInfluencers.job_status IN (:...statuses)', { statuses: ['closed', 'cancelled'] });
        } else {
            query.andWhere('campaignInfluencers.job_status IN (:...statuses)', { statuses: ['closed', 'cancelled', 'inprogress'] });
        }

        if (queryParam.search) {
            query = query.andWhere(`(campaign.campaign_name ILIKE :search OR CONCAT(users.first_name, ' ', users.last_name) ILIKE :search)`, { search: `%${queryParam.search}%` })
        }


        if (reqPage !== 0) {
            query = query.skip(offset).take(pageSize)
        }

        const [results, total] = await query.getManyAndCount()

        return {
            data: results,
            total: total,
            hasMoreResults: total > nextOffset ? 1 : 0
        }
    }


    async addPosts(request: AddPostsDto, authUser: any) {
        let campaignInfluencer = await this.CampaignInfluencer.findOne({ relations: ['influencer', 'campaign'], where: [{ id: request.campaign_influencer_id }] })
        if (!campaignInfluencer) {
            throw new HttpException(responseMessages.en.campaign.campaign_influencer_not_exists, HttpStatus.NOT_FOUND)
        }
        if (campaignInfluencer.campaign.status == CampaignStatus.COMPLETED) {
            throw new HttpException(responseMessages.en.campaign.campaign_completed, HttpStatus.BAD_REQUEST);
        }
        if (campaignInfluencer.status == InfluencerStatus.CLOSED) {
            throw new HttpException(responseMessages.en.campaign.campaign_influencer_status_close, HttpStatus.BAD_REQUEST)
        }
        // CHECK IF THE POST IS ALREADY ADDED TODAY
        const todaysPostsCount = await this.Posts.createQueryBuilder('post')
            .where('post.campaign_influencer_id = :campaignInfluencerId', { campaignInfluencerId: campaignInfluencer.id })
            .andWhere('DATE(post.created_at) = DATE(NOW())')
            .getCount();
        if (todaysPostsCount > 0) {
            campaignInfluencer.today_post_count = campaignInfluencer.today_post_count + 1;
        } else {
            campaignInfluencer.today_post_count = 1;
        }

        //campaignInfluencer.influencer_status = InfluencerStatus.CLOSED;
        // let pendingCount = await this.Draft.count({where: {campaign_influencer: {id: request.campaign_influencer_id},status:DraftStatus.PENDING}});    
        // if(!pendingCount){
        //     campaignInfluencer.status = InfluencerStatus.COMPLETION_REQUEST;
        //     // WEBSOCKET CALL & SEND CAMPAIGN INFLUENCER DETAILS 
        //      this.campaignRepository.socketCampaignInfluencerDetails(campaignInfluencer.id);
        // }

        // IF THE INFLUENCER STATUS IS NOT COMPLETION REQUEST, THEN SET THE INFLUENCER STATUS TO POSTING DELIVERABLES
        if (campaignInfluencer.influencer_status != InfluencerStatus.COMPLETION_REQUEST) {
            campaignInfluencer.influencer_status = InfluencerStatus.POSTING_DELIVERABLES;
        }

        let isSentTrigger = false;

        if ([InfluencerStatus.DRAFTING_DELIVERABLES].includes(campaignInfluencer.status)) {
            isSentTrigger = true;
            campaignInfluencer.status = InfluencerStatus.POSTING_DELIVERABLES;
        }
        await this.CampaignInfluencer.save(campaignInfluencer);
        const storedPosts = await this.campaignRepository.addPosts(request, campaignInfluencer);
        if (storedPosts) {
            const campaign = await this.campaignRepository.checkCampaignExists(campaignInfluencer.campaign.id, authUser);
            const notificationMessage = notificationMessages.en.descriptions.add_posts.replace(':userName', `${authUser.first_name} ${authUser.last_name}`).replace(":campaign_name", campaign.campaign_name);
            const metadata = { campaign_id: campaign.id, today_post_count: campaignInfluencer.today_post_count, storedPosts: storedPosts, status: campaignInfluencer.status, brand_status: campaignInfluencer.status, influencer: { id: authUser.id }, campaignName: campaign.campaign_name, campaign_influencer_id: campaignInfluencer.id }
            await this.sendCampaignNotification({
                type: 'add_posts',
                receiver: campaign.users,
                description: notificationMessage,
                sender: authUser,
                redirectType: RedirectType.CAMPAIGN_MANAGEMENT,
                redirectId: campaign.id,
                redirectSlug: campaign.slug,
                metadata,
                cometChat: {
                    groupId: campaignInfluencer.id,
                    userId: campaignInfluencer.influencer.id
                }
            });

            // SEND NOTIFICATION TO INFLUENCER
            const influencerNotificationMessage = notificationMessages.en.descriptions.add_posts_influencer;
            await this.sendCampaignNotification({
                type: 'add_posts',
                receiver: campaignInfluencer.influencer,
                description: influencerNotificationMessage,
                sender: null,
                redirectType: RedirectType.JOB_INPROGRESS,
                redirectId: campaign.id,
                redirectSlug: campaign.slug,
                metadata
            });

            if (isSentTrigger) {
                this.campaignRepository.socketCampaignInfluencerDetails(campaignInfluencer.id);
            }

            const postNotStoredCount = request.posts.length - storedPosts;
            if (postNotStoredCount) {
                return { message: responseMessages.en.campaign.campaign_post.replace(':totalPost', `${request.posts.length}`).replace(':postNotStoredCount', `${postNotStoredCount}`) }
            }
            return { message: responseMessages.en.campaign.campaign_post_no_duplicate.replace(':totalPost', `${request.posts.length}`) }
        }
        throw new HttpException(responseMessages.en.campaign.duplicate_campaign_post, HttpStatus.BAD_REQUEST)
    }
    async withdrawJob(params: any, authUser: any) {
        let campaignInfluencer = await this.CampaignInfluencer.findOne({ 'relations': ['influencer', 'campaign'], where: [{ id: params.campaign_influencer_id }] })
        if (!campaignInfluencer) {
            throw new HttpException(responseMessages.en.campaign.campaign_influencer_not_exists, HttpStatus.NOT_FOUND)
        }
        if (campaignInfluencer.campaign.status == CampaignStatus.COMPLETED) {
            throw new HttpException(responseMessages.en.campaign.campaign_completed, HttpStatus.BAD_REQUEST);
        }
        if (campaignInfluencer.status == InfluencerStatus.CLOSED) {
            throw new HttpException(responseMessages.en.campaign.campaign_influencer_status_close, HttpStatus.BAD_REQUEST)
        }

        campaignInfluencer.status = InfluencerStatus.CLOSED;
        campaignInfluencer.influencer_status = InfluencerStatus.CLOSED;
        campaignInfluencer.job_status = 'closed';
        campaignInfluencer.is_withdraw_job = true;
        campaignInfluencer.completed_date = moment.utc().format("YYYY-MM-DD");
        await this.CampaignInfluencer.save(campaignInfluencer);

        const campaign = await this.campaignRepository.checkCampaignExists(campaignInfluencer.campaign.id, authUser);
        const notificationMessage = notificationMessages.en.descriptions.withdraw_job.replace(':userName', `${authUser.display_name}`).replace(":campaign_name", campaign.campaign_name);
        const metadata = { campaign_id: campaign.id, status: campaignInfluencer.status, brand_status: campaignInfluencer.status, influencer: { id: authUser.id }, campaignName: campaign.campaign_name }
        // this.campaignRepository.notification(campaign.users, notificationMessage, authUser, RedirectType.CAMPAIGN_MANAGEMENT, campaign.id, campaign.slug, metadata, false);
        this.sendCampaignNotification({
            type: 'withdraw_job',
            receiver: campaign.users,
            description: notificationMessage,
            sender: authUser,
            redirectType: RedirectType.CAMPAIGN_MANAGEMENT,
            redirectId: campaign.id,
            redirectSlug: campaign.slug,
            metadata: metadata,
            cometChat: {
                groupId: campaignInfluencer.id,
                userId: authUser.id
            }
        });
        this.UsersServices.increasePermissionCount(campaign.users.id, "work_order", 1);
        //this.campaignRepository.sendCometMessage(brandMessage, influencerMessage, adminMessage, campaignInfluencer.id, authUser.id)
        this.campaignRepository.socketCampaignInfluencerDetails(campaignInfluencer.id);
        return;

    }

    async terminateJobRequest(params: any, authUser: any) {
        let campaignInfluencer = await this.CampaignInfluencer.findOne({ 'relations': ['influencer', 'campaign'], where: [{ id: params.campaign_influencer_id }] })
        if (!campaignInfluencer) {
            throw new HttpException(responseMessages.en.campaign.campaign_influencer_not_exists, HttpStatus.NOT_FOUND)
        }
        if (campaignInfluencer.campaign.status == CampaignStatus.COMPLETED) {
            throw new HttpException(responseMessages.en.campaign.campaign_completed, HttpStatus.BAD_REQUEST);
        }
        if (campaignInfluencer.status == InfluencerStatus.CLOSED) {
            throw new HttpException(responseMessages.en.campaign.campaign_influencer_status_close, HttpStatus.BAD_REQUEST)
        }

        campaignInfluencer.status = InfluencerStatus.CLOSED;
        campaignInfluencer.influencer_status = InfluencerStatus.CLOSED;
        campaignInfluencer.job_status = 'closed';
        campaignInfluencer.is_terminate_job_request = true;
        campaignInfluencer.completed_date = moment.utc().format("YYYY-MM-DD");
        await this.CampaignInfluencer.save(campaignInfluencer);

        const campaign = await this.campaignRepository.checkCampaignExists(campaignInfluencer.campaign.id, authUser);
        const notificationMessage = notificationMessages.en.descriptions.terminate_job_request.replace(':userName', `${authUser.display_name}`).replace(":campaign_name", campaign.campaign_name);
        const metadata = { campaign_id: campaign.id, status: campaignInfluencer.status, brand_status: campaignInfluencer.status, influencer: { id: campaignInfluencer.influencer.id }, campaignName: campaign.campaign_name }

        await this.sendCampaignNotification({
            type: 'terminate_job_request',
            receiver: campaignInfluencer.influencer,
            description: notificationMessage,
            sender: authUser,
            redirectType: RedirectType.JOB_INPROGRESS,
            redirectId: campaign.id,
            redirectSlug: campaign.slug,
            metadata: metadata,
            cometChat: {
                groupId: campaignInfluencer.id,
                userId: authUser.id
            }
        });
        this.UsersServices.increasePermissionCount(campaign.users.id, "work_order", 1);
        return;
    }



    async changeInfluencerStatus(params: any, request: any, authUser: any) {
        let campaignInfluencer = await this.CampaignInfluencer.findOne({ 'relations': ['influencer', 'campaign'], where: [{ id: params.campaign_influencer_id }] })
        if (!campaignInfluencer) {
            throw new HttpException(responseMessages.en.campaign.campaign_influencer_not_exists, HttpStatus.NOT_FOUND)
        }
        if (campaignInfluencer.campaign.status == CampaignStatus.COMPLETED) {
            throw new HttpException(responseMessages.en.campaign.campaign_completed, HttpStatus.BAD_REQUEST);
        }
        if (campaignInfluencer.status == InfluencerStatus.CLOSED) {
            throw new HttpException(responseMessages.en.campaign.campaign_influencer_status_close, HttpStatus.BAD_REQUEST)
        }
        let sender = authUser;
        let receiver = {}
        campaignInfluencer.status = request.status;
        campaignInfluencer.influencer_status = request.status;
        campaignInfluencer.job_status = request.status == 'closed' ? 'closed' : 'inprogress';
        campaignInfluencer.completed_date = request.status == 'closed' ? moment.utc().format("YYYY-MM-DD") : null;
        if (request.status == 'rejected') {
            campaignInfluencer.status = InfluencerStatus.DRAFTING_DELIVERABLES;
            campaignInfluencer.influencer_status = InfluencerStatus.DRAFTING_DELIVERABLES;
            await this.RejectCompletionRequest.save({ reason: request.reason, campaign_influencer: campaignInfluencer });
        }

        let notificationMessage = ""
        let metadata = { campaign_id: null, campaignName: "", status: "", brand_status: "", influencer: { id: authUser.id } };
        let redirect_type = RedirectType.JOB_INPROGRESS;
        let cometMessageType = '';
        switch (request.status) {
            case 'completion_request':
                const camp = await this.campaignRepository.checkCampaignExists(campaignInfluencer.campaign.id, authUser);
                receiver = camp.users;
                notificationMessage = notificationMessages.en.descriptions.completion_request.replace(':userName', `${authUser.display_name}`).replace(":campaign_name", campaignInfluencer.campaign.campaign_name);;
                if (request.payment_mode) {
                    // campaignInfluencer.status = InfluencerStatus.CLOSED;
                    // campaignInfluencer.influencer_status = InfluencerStatus.CLOSED;
                    // campaignInfluencer.job_status = InfluencerStatus.CLOSED;
                    //campaignInfluencer.completed_date = moment.utc().format("YYYY-MM-DD");
                    // await this.CampaignInfluencer.update({ id: params.campaign_influencer_id }, { payment_mode: PaymentMode.OFFLINE });
                    //  this.campaignRepository.influencerPayment(params.campaign_influencer_id, camp.users);
                }
                campaignInfluencer.payment_mode = request.payment_mode ? PaymentMode.OFFLINE : PaymentMode.ONLINE;
                campaignInfluencer.notification_reminder_at = moment.utc().add(Constants.CAMPAIGN_INFLUENCER_STATUS_NOTIFICATION_REMINDER, 'hours').format('YYYY-MM-DD HH:mm:ss');
                campaignInfluencer.completion_request_at = moment.utc().format('YYYY-MM-DD');
                redirect_type = RedirectType.CAMPAIGN_MANAGEMENT;
                metadata = { campaign_id: campaignInfluencer.campaign.id, status: campaignInfluencer.status, brand_status: campaignInfluencer.status, influencer: { id: authUser.id }, campaignName: campaignInfluencer.campaign.campaign_name }
                break;
            case 'closed':
                receiver = campaignInfluencer.influencer;
                cometMessageType = 'closed';
                if (campaignInfluencer.payment_mode == PaymentMode.OFFLINE) {
                    this.campaignRepository.influencerPayment(params.campaign_influencer_id, authUser);
                }
                //  if (campaignInfluencer.is_chat_allowed) {
                const chatCount = 1;
                await this.UsersServices.increasePermissionCount(authUser.id, "work_order", chatCount);
                // }
                notificationMessage = notificationMessages.en.descriptions.closed.replace(':userName', `${authUser.company_name}`).replace(":campaign_name", campaignInfluencer.campaign.campaign_name);;
                metadata = { campaign_id: campaignInfluencer.campaign.id, status: campaignInfluencer.status, brand_status: campaignInfluencer.status, influencer: { id: campaignInfluencer.influencer.id }, campaignName: campaignInfluencer.campaign.campaign_name }
                break;
            case 'rejected':
                receiver = campaignInfluencer.influencer;
                notificationMessage = notificationMessages.en.descriptions.reject_declaration.replace(':userName', `${authUser.company_name}`).replace(":campaign_name", campaignInfluencer.campaign.campaign_name);;
                cometMessageType = 'reject_declaration';
                metadata = { campaign_id: campaignInfluencer.campaign.id, status: campaignInfluencer.status, brand_status: campaignInfluencer.status, influencer: { id: campaignInfluencer.influencer.id }, campaignName: campaignInfluencer.campaign.campaign_name }
                break;
        }

        await this.CampaignInfluencer.save(campaignInfluencer);

        if (request.status == 'closed') {
            const campaignCompleteStatus = await this.campaignRepository.commonCampaignInfluencerCompletedQuery(campaignInfluencer.campaign.id);
            if (campaignCompleteStatus) {
                await this.Campaign.update({ id: campaignInfluencer.campaign.id }, { notification_reminder_at: moment.utc().add(Constants.CAMPAIGN_INFLUENCER_STATUS_NOTIFICATION_REMINDER, 'hours').format('YYYY-MM-DD HH:mm:ss') });
            }
        }

        await this.sendCampaignNotification({
            type: request.status == 'rejected' ? 'reject_declaration' : request.status,
            receiver: receiver,
            description: notificationMessage,
            sender: sender,
            redirectType: redirect_type,
            redirectId: campaignInfluencer.campaign.id,
            redirectSlug: campaignInfluencer.campaign.slug,
            metadata,
            cometChat: {
                groupId: campaignInfluencer.id,
                userId: authUser.id
            }
        });
        // WEBSOCKET CALL & SEND CAMPAIGN INFLUENCER DETAILS 
        this.campaignRepository.socketCampaignInfluencerDetails(campaignInfluencer.id);

        return;
    }


    async campaignPayment(request, auth) {
        return await this.campaignRepository.influencerPayment(request.campaign_influencer_id, auth, request.card_id);
    }

    async deleteCampaignPayment(param, auth) {
        return await this.CampaignPayment.delete({ id: param.payment_id });
    }
    async influencerCalendar(param, auth) {
        const languageCode = auth.language || 'en';
        let queryBuilder = await this.campaignRepository.commonQuery(languageCode);
        let results = await queryBuilder.leftJoinAndSelect('campaign.campaignInfluencers', 'camapignInfluencer', 'camapignInfluencer.influencerId = :influencer_id', { influencer_id: auth.id }).withDeleted()
            .where(new Brackets(qb => {
                qb.where('camapignInfluencer.influencer_status = :postStatus', { postStatus: InfluencerStatus.POSTING_DELIVERABLES })
                qb.orWhere('camapignInfluencer.influencer_status = :draftStatus', { draftStatus: InfluencerStatus.DRAFTING_DELIVERABLES })
            }))
            .andWhere(new Brackets(qb => {
                qb.where('camapignInfluencer.draft_due_date BETWEEN :startDate AND :endDate', { startDate: param.start_date + " 00:00:00", endDate: param.end_date + " 23:59:59" })
                qb.orWhere('camapignInfluencer.post_due_date BETWEEN :startDate AND :endDate', { startDate: param.start_date + " 00:00:00", endDate: param.end_date + " 23:59:59" })
            }))
            .getMany();
        return { data: results }
    }

    async brandCalender(param, auth) {
        const languageCode = auth.language || 'en';
        let queryBuilder = await this.campaignRepository.commonQueryAllCampaignInfluencer(languageCode);

        let results = await queryBuilder.where('campaignInfluencer.status IN (:...statuses)', {
            statuses: [
                InfluencerStatus.COMPLETION_REQUEST,
                InfluencerStatus.QUOTATION_AVAILABLE,
                InfluencerStatus.DRAFT_AVAILABLE
            ]
        })
            .andWhere(new Brackets(qb => {
                qb.where('campaignInfluencer.draft_due_date BETWEEN :startDate AND :endDate', { startDate: param.start_date + " 00:00:00", endDate: param.end_date + " 23:59:59" })
                qb.orWhere('campaignInfluencer.post_due_date BETWEEN :startDate AND :endDate', { startDate: param.start_date + " 00:00:00", endDate: param.end_date + " 23:59:59" })
            }))
            .andWhere('campaign.users = :brandId', { brandId: auth.id })
            .getMany();
        return { data: results }
    }

    async influencerDashboardList(param: any, auth) {
        const languageCode = auth.language || 'en';
        let queryBuilder = await this.campaignRepository.commonQueryAllCampaignInfluencer(languageCode);
        let results = await queryBuilder.where('campaignInfluencer.status = :status', { status: param.status })
            .andWhere('campaign.users = :user_id', { user_id: auth.id })
            .orderBy('campaignInfluencer.createdAt', 'DESC')
            .getMany();

        return { data: results }
    }

    async viewAsCreator(queryParam: any, auth: any) {
        const reqPage = queryParam.page ? queryParam.page : 0;
        const page = queryParam.page - 1;


        const languageCode = auth.language || 'en';
        let myRecentCampaigns = [];
        const brand = await this.Users.createQueryBuilder('user')
            .leftJoinAndSelect('user.brandIndustries', 'brandIndustries')
            .leftJoinAndSelect('brandIndustries.category', 'brandCategory', 'brandCategory.is_active = true')
            .where('user.id = :id', { id: auth.id })
            .getOne();

        const categoryIds = (brand?.brandIndustries || [])
            .map((item) => item.category?.id)
            .filter(Boolean);
        if (page === 0) {
            const recentQueryBuilder = await this.campaignRepository.commonQuery(languageCode);
            let recentQuery = recentQueryBuilder
                .where('campaign.users = :userId', { userId: auth.id })
                .andWhere('campaign.deleted_at IS NULL')
                .andWhere('campaign.status = :status', { status: CampaignStatus.RUNNING })
                .andWhere('campaign.is_active = :is_active', { is_active: true })
                .andWhere('campaign.profile_privacy = :profile_privacy', { profile_privacy: ProfilePrivacy.PUBLIC })
                .orderBy('campaign.created_at', 'DESC')
                .take(3);

            myRecentCampaigns = await recentQuery.getMany();
        }
        const pageSize = 9;
        const offset = page * pageSize;
        const nextPage = page + 1;
        const nextOffset = nextPage * pageSize;

        let relatedCampaigns: Campaign[] = [];
        let relatedTotal = 0;

        if (auth.country || categoryIds.length > 0) {
            const relatedQueryBuilder = await this.campaignRepository.commonQuery(languageCode);
            let relatedQuery = relatedQueryBuilder
                .andWhere('campaign.users != :userId', { userId: auth.id })
                .andWhere('campaign.profile_privacy = :profile_privacy', { profile_privacy: ProfilePrivacy.PUBLIC })
                .andWhere('campaign.status = :status', { status: CampaignStatus.RUNNING })
                .andWhere('campaign.is_active = :is_active', { is_active: true })
                .andWhere('campaign.deleted_at IS NULL')
                .andWhere(new Brackets(qb => {
                    if (auth.country) {
                        qb.where('campaign.campaign_country = :country', { country: auth.country });
                    }
                    if (categoryIds.length > 0) {
                        const categoryCondition = `campaign.id IN (
                            SELECT cc.campaign_id FROM campaign_categories cc
                            WHERE cc.category_id IN (:...categoryIds) AND cc.deleted_at IS NULL
                        )`;
                        if (auth.country) {
                            qb.orWhere(categoryCondition, { categoryIds });
                        } else {
                            qb.where(categoryCondition, { categoryIds });
                        }
                    }
                }))
                .orderBy('campaign.created_at', 'DESC');

            if (queryParam.search) {
                relatedQuery = relatedQuery.andWhere(
                    `(campaign.campaign_name ILIKE :search OR CONCAT(users.first_name, ' ', users.last_name) ILIKE :search)`,
                    { search: `%${queryParam.search}%` }
                );
            }

            if (reqPage !== 0) {
                relatedQuery = relatedQuery.skip(offset).take(pageSize);
            }

            [relatedCampaigns, relatedTotal] = await relatedQuery.getManyAndCount();
        }
        relatedTotal = relatedTotal + myRecentCampaigns.length;
        return {
            data: [...myRecentCampaigns, ...relatedCampaigns],
            total: relatedTotal,
            hasMoreResults: relatedTotal > nextOffset ? 1 : 0,
        };
    }

    async previewAsCreator(request: PreviewCampaignDto, queryParam: any, auth: any) {
        const languageCode = auth.language || 'en';
        const categories = request.categories?.length ? request.categories : [];
        const platforms = request.platforms?.length ? request.platforms : [];
        const isBarter = request.is_barter ?? 'false';
        const jobPrivacy = request.job_privacy;
        const currency = request.currency;

        const reqPage = queryParam.page ? Number(queryParam.page) : 1;
        const page = reqPage > 0 ? reqPage - 1 : 0;
        const pageSize = page == 0 ? 8 : 9;
        const offset = page * pageSize;
        const nextOffset = offset + pageSize;

        const { items: scoredCampaigns, total } = await this.campaignRepository.getRelatedPublicCampaignsForPreview(
            auth.id,
            { currency, categories, platforms, isBarter, jobPrivacy },
            offset,
            pageSize,
        );

        if (!scoredCampaigns.length) {
            return { data: [], total, hasMoreResults: 0 };
        }

        const campaignIds = scoredCampaigns.map((item) => item.id);
        const orderMap = new Map(scoredCampaigns.map((item, index) => [item.id, index]));

        const relatedQueryBuilder = await this.campaignRepository.commonQuery(languageCode);
        const relatedCampaigns = await relatedQueryBuilder
            .andWhere('campaign.id IN (:...campaignIds)', { campaignIds })
            .getMany();

        relatedCampaigns.sort((a, b) => (orderMap.get(a.id) ?? 0) - (orderMap.get(b.id) ?? 0));

        return {
            data: relatedCampaigns,
            total,
            hasMoreResults: total > nextOffset ? 1 : 0,
        };
    }

    async brandDashboard(auth) {

        const paymentFailedList = await this.CampaignPayment.createQueryBuilder('payment')
            .leftJoin('payment.campaign', 'campaign')
            .where('payment.payment_status = :status', { status: PAYMENT_STATUS.FAILED })
            .andWhere('payment.created_at <= :date', { date: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) })
            .andWhere('payment.brand_id = :brandId', { brandId: auth.id })
            .select(['payment.id', 'campaign.id', 'campaign.campaign_name', 'payment.comment'])
            .getMany();

        return { paymentFailedList }
    }

    async brandPayNow(body: any, auth) {
        let payment = await this.CampaignPayment.findOne({ where: { id: body.payment_id } });
        if (!payment) {
            throw new HttpException(responseMessages.en.campaign.payment_not_exists, HttpStatus.NOT_FOUND)
        }
        if (payment.payment_mode == PaymentMode.OFFLINE) {
            payment.payment_status = PAYMENT_STATUS.COMPLETED;
            await this.CampaignPayment.save(payment);
            return { response: { paymentIntent: null, status: true } }
        }
        let card = await this.Card.findOne({ where: { card_id: body.card_id, users: { id: auth.id } } });
        if (!card) {
            throw new HttpException(responseMessages.en.campaign.card_not_exists, HttpStatus.NOT_FOUND)
        }

        const { amount, currency } = payment;

        const response = await this.StripeService.paymentIntents(amount, currency, body.card_id, auth.stripe_customer_id);
        if (response.response.status) {
            payment.transaction_id = response.response.paymentIntent.id;
            payment.payment_response = response.response;
            await this.CampaignPayment.save(payment);
            let count = 0;
            while (count < 3) {
                await this.sleep(1000);
                const isCompleted = await this.CampaignPayment.count({
                    where: { id: payment.id, payment_status: PAYMENT_STATUS.COMPLETED }
                });
                if (isCompleted) break; // Exit if subscription is active
                count++;
            }
            return;
        }
        if (response.response?.error) {
            throw new HttpException(response.response.error, HttpStatus.BAD_REQUEST)
        }
        payment.transaction_id = response.response.paymentIntent.id;
        await this.CampaignPayment.save(payment);
        return response;

    }

    async sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    async paymentListForBrand(queryParam: any, auth: any) {

        const reqPage = queryParam.page ? queryParam.page : 0
        const page = queryParam.page - 1
        const pageSize = queryParam.per_page ? queryParam.per_page : 10
        const offset = page * pageSize
        const nextPage = page + 1
        const nextOffset = nextPage * pageSize

        const languageCode = auth.language || 'en';
        let query = this.Campaign.createQueryBuilder('campaign')
            .leftJoin('campaign.campaignInfluencers', 'campaignInfluencer').withDeleted()
            .leftJoin("campaign.users", "users")
            .leftJoin('campaignInfluencer.influencer', 'influencer')
            .innerJoin('campaignInfluencer.campaignPayment', 'campaignPayment')
            .leftJoin('influencer.influencerCategories', 'categories')
            .leftJoin('categories.category', 'category')
            .leftJoin("category.translations", "translation_req", "translation_req.language_code = :languageCode AND translation_req.deleted_at IS NULL", { languageCode })
            .leftJoinAndSelect("category.translations", "translation",
                "(translation.language_code = :languageCode OR (translation.language_code = 'en' AND translation_req.id IS NULL)) AND translation.deleted_at IS NULL",
                { languageCode })
            .where('campaignInfluencer.status = :status', { status: InfluencerStatus.CLOSED })
            .andWhere('campaignInfluencer.is_archived = false')
            .andWhere('campaign.users = :userId', { userId: auth.id })
            .select(['campaign.id', 'campaign.slug', 'campaign.campaign_name', 'campaign.posting_start_date', 'campaign.posting_end_date',
                'campaign.influencer_type', 'campaign.job_requirement', 'campaign.content_type',
                'campaignInfluencer', "users.id",
                "users.slug", "users.first_name", "users.last_name", "users.email", "users.image", "users.country", "users.display_name",
                "users.about_me", "users.is_offline_payment_allowed", "users.offline_payment_allowed",
                'campaignPayment.id', 'campaignPayment.amount', 'campaignPayment.currency', 'campaignPayment.payment_mode', 'campaignPayment.amount_usd',
                'campaignPayment.influencer_payable_amount', 'campaignPayment.influencer_payable_amount_usd', 'campaignPayment.payment_status',
                'campaignPayment.transaction_id', 'campaignPayment.invoice_url', 'campaignPayment.created_at', 'campaignPayment.is_admin_paid',
                'influencer.id', 'influencer.first_name', 'influencer.last_name', 'influencer.image', 'influencer.email', 'influencer.country', "influencer.display_name",
                'categories.id', 'category.id', 'category.slug', 'category.title', 'translation.title', 'translation.description', 'translation.slug'])
            .orderBy('campaignPayment.created_at', 'DESC')
            .addOrderBy('campaignInfluencer.id', 'DESC');

        if (queryParam.campaign_id) {
            query.andWhere('campaign.id = :campaign_id', { campaign_id: queryParam.campaign_id });
        }

        if (queryParam.payment_status != 'all' && queryParam.payment_status != 'offline' && queryParam.payment_status != 'force_offline') {
            query.andWhere('campaignPayment.payment_status = :payment_status', { payment_status: queryParam.payment_status });
            query.andWhere('campaignPayment.payment_mode NOT IN (:...excludedPaymentModes)', {
                excludedPaymentModes: [CampaignPaymentMode.OFFLINE, CampaignPaymentMode.FORCE_OFFLINE]
            });
        }
        if (queryParam.payment_status == 'offline') {
            query.andWhere('campaignPayment.payment_mode = :payment_mode', { payment_mode: CampaignPaymentMode.OFFLINE });
        }
        if (queryParam.payment_status == 'force_offline') {
            query.andWhere('campaignPayment.payment_mode = :payment_mode', { payment_mode: CampaignPaymentMode.FORCE_OFFLINE });
        }


        if (queryParam.search) {
            query = query.andWhere(`(campaign.campaign_name ILIKE :search OR CONCAT(influencer.first_name, ' ', influencer.last_name) ILIKE :search)`, { search: `%${queryParam.search}%` })
        }

        const totalRow = await query.clone()
            .select('COUNT(DISTINCT campaign.id)', 'cnt')
            .orderBy()
            .getRawOne();
        const total = Number(totalRow?.cnt || 0);

        const pagedCampaigns = await query.clone()
            .select('campaign.id', 'id')
            .addSelect('MAX(campaignPayment.created_at)', 'latestPayment')
            .orderBy()
            .groupBy('campaign.id')
            .orderBy('MAX(campaignPayment.created_at)', 'DESC')
            .addOrderBy('campaign.id', 'DESC')
            .offset(Number(offset) || 0)
            .limit(Number(pageSize) || 10)
            .getRawMany();

        const campaignIds = pagedCampaigns.map((row: any) => row.id);
        if (!campaignIds.length) {
            return { data: [], total, hasMoreResults: 0 };
        }

        const results = await query
            .andWhere('campaign.id IN (:...campaignIds)', { campaignIds })
            .getMany();

        const orderMap = new Map(campaignIds.map((id: string, index: number) => [id, index]));
        results.sort((a, b) => (orderMap.get(a.id) ?? 0) - (orderMap.get(b.id) ?? 0));

        return {
            data: results,
            total: total,
            hasMoreResults: total > nextOffset ? 1 : 0
        }
    }

    async paymentStatisticForBrand(queryParam: any, auth: any) {
        const statisticsQuery = this.CampaignPayment.createQueryBuilder('campaignPayment')
            .leftJoin('campaignPayment.brand', 'brand')
            .leftJoin('campaignPayment.campaign', 'campaign')
            .where('brand.id = :brandId', { brandId: auth.id })
            .select([
                'COUNT(campaignPayment.id) AS totalInfluencer',
                'COUNT(DISTINCT campaign.id) AS totalCampaigns',
                'SUM(campaignPayment.amount_usd) AS totalAmount',
                'SUM(CASE WHEN campaignPayment.payment_status = \'completed\' THEN campaignPayment.amount_usd ELSE 0 END) AS paidAmount',
                'SUM(CASE WHEN campaignPayment.payment_status = \'failed\' THEN campaignPayment.amount_usd ELSE 0 END) AS remainingAmount'
            ]);

        const paymentStatistics = await statisticsQuery.getRawOne();
        return { paymentStatistics }
    }


    async paymentListForInfluencer(queryParam: any, auth: any) {
        const reqPage = queryParam.page ? queryParam.page : 0
        const page = queryParam.page - 1
        const pageSize = queryParam.per_page ? queryParam.per_page : 10
        const offset = page * pageSize
        const nextPage = page + 1
        const nextOffset = nextPage * pageSize
        let query = this.CampaignInfluencer.createQueryBuilder('campaignInfluencer').withDeleted()
            .leftJoin('campaignInfluencer.campaign', 'campaign')
            .leftJoin("campaign.users", "users")
            .innerJoin('campaignInfluencer.campaignPayment', 'campaignPayment')
            .where('campaignInfluencer.status = :status', { status: InfluencerStatus.CLOSED })
            .andWhere('campaignInfluencer.is_archived = false')
            .andWhere('campaignInfluencer.influencerId = :userId', { userId: auth.id })
            .select(['campaignInfluencer',
                'campaign.id', 'campaign.slug', 'campaign.campaign_name', 'campaign.posting_start_date', 'campaign.posting_end_date',
                'campaign.influencer_type', 'campaign.job_requirement', 'campaign.content_type',
                "users.id", "users.slug", "users.first_name", "users.last_name", "users.email", "users.image", "users.country", "users.about_me", "users.is_offline_payment_allowed", "users.offline_payment_allowed",
                'campaignPayment.id', 'campaignPayment.amount', 'campaignPayment.currency', 'campaignPayment.payment_mode', 'campaignPayment.amount_usd',
                'campaignPayment.influencer_payable_amount', 'campaignPayment.influencer_payable_amount_usd', 'campaignPayment.payment_status', 'campaignPayment.is_admin_paid',
                'campaignPayment.transaction_id', 'campaignPayment.invoice_url', 'campaignPayment.created_at', 'campaignPayment.updated_at', 'campaignPayment.invoice_receipt'])
            .orderBy('campaignPayment.created_at', 'DESC');

        if (queryParam.campaign_id) {
            query.andWhere('campaign.id = :campaign_id', { campaign_id: queryParam.campaign_id });
        }

        if (queryParam.payment_status == 'completed') {
            query.andWhere("campaignPayment.payment_status = 'completed' AND campaignPayment.is_admin_paid = true");
            query.andWhere("campaignPayment.payment_mode != 'offline'");
        }
        if (queryParam.payment_status == 'pending') {
            query.andWhere(new Brackets(qb => {
                qb.where("campaignPayment.payment_status = 'failed' OR campaignPayment.is_admin_paid = false");
            }));
        }
        if (queryParam.payment_status == 'offline') {
            query.andWhere("campaignPayment.payment_mode = 'offline'");
        }

        if (queryParam.search) {
            query = query.andWhere(`(campaign.campaign_name ILIKE :search OR CONCAT(users.first_name, ' ', users.last_name) ILIKE :search)`, { search: `%${queryParam.search}%` })
        }
        const [results, total] = await query.skip(offset).take(100).getManyAndCount();

        return {
            data: results,
            total: total,
            hasMoreResults: total > nextOffset ? 1 : 0
        }
    }

    async paymentStatisticForInfluencer(queryParam: any, auth: any) {
        const statisticsQuery = this.CampaignPayment.createQueryBuilder('campaignPayment')
            .leftJoin('campaignPayment.influencer', 'influencer')
            .where('influencer.id = :influencerId', { influencerId: auth.id })
            .select([
                'COUNT(campaignPayment.id) AS totalJobs',
                'SUM(campaignPayment.influencer_payable_amount_usd) AS totalAmount',
                'SUM(CASE WHEN campaignPayment.is_admin_paid = true AND campaignPayment.payment_status = \'completed\' THEN campaignPayment.influencer_payable_amount_usd ELSE 0 END) AS paidAmount',
                'SUM(CASE WHEN campaignPayment.is_admin_paid = false THEN campaignPayment.influencer_payable_amount_usd ELSE 0 END) AS remainingAmount'
            ]);

        const paymentStatistics = await statisticsQuery.getRawOne();
        return { paymentStatistics }
    }

    async report(request: ReportDto) {

    }

    async cometChatWebhook(request: any) {

        try {
            if (request.sender == 'app_system' || request.category != 'message' || request.receiverType != 'group') return;

            const sender = await this.Users.findOne({ where: { id: request.sender } });

            const COMET_CHAT_API_KEY = await this.encryptService.decrypt(process.env.COMET_CHAT_API_KEY);
            const headers = { accept: 'application/json', 'content-type': 'application/json', apikey: COMET_CHAT_API_KEY };
            const groupMembers = await axios.get(process.env.COMET_CHAT_URL + 'groups/' + request.receiver + '/members', { headers });

            for (const member of groupMembers?.data?.data) {
                if (member.uid == request.sender) continue;
                const campaignName = request?.data?.entities?.receiver?.entity?.description;
                const senderName = sender.role == 'influencer' ? sender.display_name : `${sender.first_name} ${sender.last_name}`;
                const notificationMessage = notificationMessages.en.descriptions.comet_chat_webhook.replace(':sender', senderName).replace(':campaign_name', campaignName);
                const campaign = await this.Campaign.findOne({ where: { campaign_name: campaignName } });
                if (member.uid == 'admin') {
                    this.adminNewMessageNotification(notificationMessage, sender, campaign);
                    continue;
                }
                const receiver = await this.campaignRepository.checkUserExists(member.uid);
                const minutes = notificationSettings.comet_chat_webhook.after_minutes; //  (await (this.Settings.findOne({ where: { type: 'comet_chat_message_notification' } }))).value;

                const cutoff = moment.utc().subtract(minutes, 'minutes').toISOString();

                const already = await this.Notifications.createQueryBuilder('n')
                    .where('n.receiver_id = :rid::uuid', { rid: receiver.id })
                    .andWhere('n.sender_id = :sid::uuid', { sid: sender.id })
                    .andWhere('n.created_at >= :cutoff::timestamptz', { cutoff })
                    .andWhere('n.description = :msg', { msg: notificationMessage })
                    .andWhere('n.deleted_at IS NULL')
                    .orderBy('n.created_at', 'DESC').getCount();
                console.log("already", already);
                if (already) return;
                if (receiver) {
                    if (campaign) {
                        let redirect_type = RedirectType.CAMPAIGN_MANAGEMENT;
                        let campaignInfluencer = await this.CampaignInfluencer.findOne({ relations: ['influencer'], where: [{ id: request.receiver }] });
                        if (receiver.role == 'influencer') {
                            redirect_type = RedirectType.JOB_INPROGRESS;
                            if (campaignInfluencer.status == InfluencerStatus.WAITING_FOR_QUOTATIONS) {
                                redirect_type = RedirectType.INVITED
                            } else if (campaignInfluencer.status == InfluencerStatus.QUOTATION_AVAILABLE) {
                                redirect_type = RedirectType.APPLIED;
                            }
                        }
                        const metadata = { campaign_id: campaign.id, status: campaignInfluencer.status, brand_status: campaignInfluencer.status, influencer: { id: campaignInfluencer.influencer.id }, campaignName: campaign.campaign_name }
                        this.sendCampaignNotification({
                            type: 'comet_chat_webhook',
                            receiver: receiver,
                            description: notificationMessage,
                            sender: sender,
                            redirectType: redirect_type,
                            redirectId: campaign.id,
                            redirectSlug: campaign.slug,
                            metadata: metadata,
                        });
                    }
                }
            }
        } catch (err) {
            console.log("Error in comet chat webhook", err);
        }
    }

    /**
     * This function sends a notification to admin if there is a new message in
     * comet chat. It checks if the same notification is already sent in the
     * last 15 minutes and if yes, it doesn't send the notification again.
     *
     * @param notificationMessage The message to be sent as a notification.
     * @param authUser The user who is sending the message.
     * @param campaign The campaign in which the message is sent.
     */
    async adminNewMessageNotification(notificationMessage, authUser, campaign) {
        const receiver = await this.Users.findOne({ where: { role: 'admin', is_active: true } });
        const minutes = (await (this.Settings.findOne({ where: { type: 'comet_chat_message_notification' } }))).value;
        const cutoff = moment.utc().subtract(minutes, 'minutes').toISOString();
        const already = await this.Notifications.createQueryBuilder('n')
            .where('n.receiver_id = :rid::uuid', { rid: receiver.id })
            .andWhere('n.sender_id = :sid::uuid', { sid: authUser.id })
            .andWhere('n.created_at >= :cutoff::timestamptz', { cutoff })
            .andWhere('n.description = :msg', { msg: notificationMessage })
            .andWhere('n.deleted_at IS NULL')
            .orderBy('n.created_at', 'DESC').getCount();
        if (already) return;
        this.campaignRepository.adminNotification(notificationMessage, authUser, RedirectType.CAMPAIGN_LIST, campaign.id, campaign.slug);
    }

    async deleteCampaignInfluencer(params: CampaignInfluencerIdParamDto) {
        let campaignInfluencer = await this.CampaignInfluencer.findOne({ where: [{ id: params.campaign_influencer_id }] })
        if (!campaignInfluencer) {
            throw new HttpException(responseMessages.en.campaign.campaign_influencer_not_exists, HttpStatus.NOT_FOUND)
        }
        if (campaignInfluencer.status != InfluencerStatus.UNCONNTACTED) {
            throw new HttpException(responseMessages.en.campaign.campaign_influencer_delete_error, HttpStatus.BAD_REQUEST)
        }
        await this.CampaignInfluencer.delete({ id: params.campaign_influencer_id });
        return;
    }
    //The campaign stayed at "Posting Deliverable" status for more than 24 hours, then send notification and email on a daily basis.
    async campaignInfluencerStatusNotification() {

        const queryBuilder = await this.campaignRepository.commonCampaignStatusNotificationQuery(InfluencerStatus.POSTING_DELIVERABLES);
        const result = await queryBuilder.getMany();
        if (result.length == 0) return;
        for (const campaignInfluencer of result) {
            console.log(campaignInfluencer);
            const campaign = campaignInfluencer.campaign;
            const influencer = campaignInfluencer.influencer;
            const notificationMessage = notificationMessages.en.descriptions.posting_deliverable_remainder_notification.replace(':campaign_name', `${campaign.campaign_name}`);
            //await this.campaignRepository.notification(influencer, notificationMessage, null, RedirectType.JOB_INPROGRESS, campaign.id, campaign.slug, { campaignName: campaign.campaign_name, status: campaignInfluencer.status });
            this.sendCampaignNotification({
                type: 'posting_deliverables_reminder',
                receiver: influencer,
                description: notificationMessage,
                sender: null,
                redirectType: RedirectType.JOB_INPROGRESS,
                redirectId: campaign.id,
                redirectSlug: campaign.slug,
                metadata: { campaign_id: campaign.id, campaignName: campaign.campaign_name, status: campaignInfluencer.status, brand_status: campaignInfluencer.status, influencer: { id: influencer.id } }
            });

            const notification_reminder_at = moment.utc().add(Constants.CAMPAIGN_INFLUENCER_STATUS_NOTIFICATION_REMINDER, 'hours').format('YYYY-MM-DD HH:mm:ss');
            this.CampaignInfluencer.update(campaignInfluencer.id, { notification_reminder_at: notification_reminder_at });
        }
    }
    //The campaign stayed at "Common" status for more than 24 hours, then send notification and email on a daily basis.
    async campaignBrandStatusNotification(status, type) {
        const queryBuilder = await this.campaignRepository.commonCampaignBrandStatusNotificationQuery(status);
        const result = await queryBuilder.getMany();
        if (result.length == 0) return;
        for (const campaignInfluencer of result) {

            const campaign = campaignInfluencer.campaign;
            const brand = campaign.users;
            const notificationMessage = notificationMessages.en.descriptions[type].replace(':userName', `${brand.first_name} ${brand.last_name}`).replace(':campaign_name', `${campaign.campaign_name}`);
            const metadata = { campaign_id: campaign.id, status: campaignInfluencer.status, brand_status: campaignInfluencer.status, campaignName: campaign.campaign_name, influencer: { id: campaignInfluencer.influencer.id } };
            this.sendCampaignNotification({
                type: type,
                receiver: brand,
                description: notificationMessage,
                sender: null,
                redirectType: RedirectType.CAMPAIGN_MANAGEMENT,
                redirectId: campaign.id,
                redirectSlug: campaign.slug,
                metadata: metadata
            });
            //  await this.campaignRepository.notification(brand, notificationMessage, null, RedirectType.CAMPAIGN_MANAGEMENT, campaign.id, campaign.slug, metadata);
            if (status == InfluencerStatus.DRAFT_AVAILABLE && campaignInfluencer.draft_notification_sent >= 3) {
                await this.CampaignInfluencer.update(campaignInfluencer.id, { notification_reminder_at: null });
                continue;
            }
            const notification_reminder_at = moment.utc().add(48, 'hours').format('YYYY-MM-DD HH:mm:ss');
            const updatePayload: any = { notification_reminder_at };
            if (status == InfluencerStatus.DRAFT_AVAILABLE) {
                updatePayload.draft_notification_sent = (campaignInfluencer.draft_notification_sent || 0) + 1;
            }
            await this.CampaignInfluencer.update(campaignInfluencer.id, updatePayload);
        }
    }

    async campaignCompleteStatusNotification() {
        const campaigns = await this.Campaign.find({ where: { status: CampaignStatus.RUNNING, notification_reminder_at: LessThan(moment().format('YYYY-MM-DD HH:mm:ss')) }, relations: ['users'] });
        for (const campaign of campaigns) {
            const campaignCompleteStatus = await this.campaignRepository.commonCampaignInfluencerCompletedQuery(campaign.id);
            console.log(campaignCompleteStatus);
            if (campaignCompleteStatus) {
                const brand = campaign.users;
                const notificationMessage = notificationMessages.en.descriptions.campaign_completed_reminder_notification.replace(':campaign_name', `${campaign.campaign_name}`);
                const metadata = { campaign_id: campaign.id, campaignName: campaign.campaign_name, status: InfluencerStatus.COMPLETION_REQUEST };
                // await this.campaignRepository.notification(brand, notificationMessage, null, RedirectType.CAMPAIGN_MANAGEMENT, campaign.id, campaign.slug, metadata);
                this.sendCampaignNotification({
                    type: 'campaign_completed_reminder_notification',
                    receiver: brand,
                    description: notificationMessage,
                    sender: null,
                    redirectType: RedirectType.CAMPAIGN_MANAGEMENT,
                    redirectId: campaign.id,
                    redirectSlug: campaign.slug,
                    metadata: metadata
                });
                const notification_reminder_at = moment.utc().add(7, 'days').format('YYYY-MM-DD HH:mm:ss');
                this.Campaign.update(campaign.id, { notification_reminder_at: notification_reminder_at });
            }
        }
    }

    /**
     * Finds campaign_influencer records where status is COMPLETION_REQUEST and
     * completion_request_at is at least 30 days old. Returns the list so callers
     * (e.g. cron jobs) can process them. This does not mutate data by itself.
     */
    async processCompletionRequests30Days() {
        try {
            const cutoff = moment.utc().subtract(31, 'days').format('YYYY-MM-DD');
            const results = await this.CampaignInfluencer.createQueryBuilder('ci')
                .leftJoinAndSelect('ci.dispute', 'dispute')
                .leftJoinAndSelect('ci.campaign', 'campaign')
                .leftJoinAndSelect('campaign.users', 'users')
                .leftJoinAndSelect('ci.influencer', 'influencer')
                .where('ci.status = :status', { status: InfluencerStatus.COMPLETION_REQUEST })
                //   .andWhere('users.offline_payment_allowed != :offlinePaymentAllowed', { offlinePaymentAllowed: 'forced' })
                .andWhere('ci.influencer_budget > 0')
                .andWhere('ci.completion_request_at IS NOT NULL')
                .andWhere('ci.completion_request_at <= :cutoff', { cutoff })
                .andWhere('ci.payment_mode = :paymentMode', { paymentMode: PaymentMode.ONLINE })
                .andWhere('ci.auto_payment_attempts < :maxAttempts', { maxAttempts: 1 })
                .andWhere('dispute.id IS NULL')
                .getMany();
            for (const ci of results) {
                try {
                    const campaign = ci?.campaign;
                    if (campaign) {
                        let payment = await this.campaignRepository.influencerPayment(ci.id, campaign.users)
                        const influencerName = ci.influencer.display_name;
                        if (payment?.response?.status == false) {
                            const message = notificationMessages.en.descriptions.campaign_auto_payment_failed.replace(':influencer_name', `${influencerName}`).replace(':campaign_name', `${campaign.campaign_name}`);
                            const metadata = { campaign_id: campaign.id, campaignName: campaign.campaign_name, status: ci.status, brand_status: ci.status, influencer: { id: ci.influencer.id } };
                            // this.campaignRepository.autoPaymentNotification(campaign.users, message, null, RedirectType.CAMPAIGN_MANAGEMENT, campaign.id, campaign.slug, metadata);
                            this.sendCampaignNotification({
                                type: 'auto_payment_failed',
                                receiver: campaign.users,
                                description: message,
                                sender: null,
                                redirectType: RedirectType.CAMPAIGN_MANAGEMENT,
                                redirectId: campaign.id,
                                redirectSlug: campaign.slug,
                                metadata: metadata
                            });
                            //this.campaignRepository.adminNotificationWithEmail(message, null, RedirectType.CAMPAIGN_LIST, campaign.id, campaign.slug);
                            this.CampaignInfluencer.update(ci.id, { auto_payment_attempts: ci.auto_payment_attempts + 1 });
                            continue;
                        }
                        await this.changeInfluencerStatus({ campaign_influencer_id: ci.id }, { status: 'closed' }, campaign.users);
                        const message = notificationMessages.en.descriptions.campaign_auto_payment_success.replace(':influencer_name', `${influencerName}`).replace(':campaign_name', `${campaign.campaign_name}`);
                        const metadata = { campaign_id: campaign.id, campaignName: campaign.campaign_name, status: ci.status, brand_status: ci.status, influencer: { id: ci.influencer.id } };
                        //     this.campaignRepository.autoPaymentNotification(campaign.users, message, null, RedirectType.CAMPAIGN_MANAGEMENT, campaign.id, campaign.slug, metadata);
                        this.sendCampaignNotification({
                            type: 'auto_payment_success',
                            receiver: campaign.users,
                            description: message,
                            sender: null,
                            redirectType: RedirectType.CAMPAIGN_MANAGEMENT,
                            redirectId: campaign.id,
                            redirectSlug: campaign.slug,
                            metadata: metadata
                        });
                    }
                } catch (error) {
                    console.error(`Error processing campaign_influencer ID ${ci.id}:`, error);
                }
            }

        } catch (error) {
            console.error("Error processing completion requests 30 days:", error);
        }
        return;
    }



    async dailyApplicationSummaryNotification() {
        const cutoff = moment.utc().subtract(24, 'hours').toDate();
        const applications = await this.CampaignInfluencer.createQueryBuilder('ci')
            .leftJoinAndSelect('ci.campaign', 'campaign')
            .leftJoinAndSelect('campaign.users', 'users')
            .where('ci.is_applied = :isApplied', { isApplied: true })
            .andWhere('ci.status = :status', { status: InfluencerStatus.QUOTATION_AVAILABLE })
            .andWhere('campaign.deleted_at IS NULL')
            .getMany();

        if (!applications.length) return;

        const campaignMap = new Map<string, { campaign: any; pendingCount: number; newCount: number }>();

        for (const application of applications) {
            const campaign = application.campaign;
            if (!campaign?.id) continue;

            if (!campaignMap.has(campaign.id)) {
                campaignMap.set(campaign.id, { campaign, pendingCount: 0, newCount: 0 });
            }

            const campaignStats = campaignMap.get(campaign.id);
            campaignStats.pendingCount += 1;

            if (application.createdAt && new Date(application.createdAt) >= cutoff) {
                campaignStats.newCount += 1;
            }
        }

        for (let { campaign, pendingCount, newCount } of campaignMap.values()) {
            // Trigger only when at least one new application is received in the last 24 hours.
            if (newCount <= 0) continue;
            pendingCount = (pendingCount - newCount) > 0 ? (pendingCount - newCount) : 0;
            const message = notificationMessages.en.descriptions.daily_application_summary.replace(':campaign_name', `${campaign.campaign_name}`).replace(':new_applications', `${newCount}`).replace(':pending_applications', `${pendingCount}`);
            const metadata = {
                campaign_id: campaign.id,
                status: InfluencerStatus.QUOTATION_AVAILABLE,
                campaignName: campaign.campaign_name,
                newApplications: newCount,
                pendingApplications: pendingCount
            };

            await this.sendCampaignNotification({
                type: 'daily_application_summary',
                receiver: campaign.users,
                description: message,
                sender: null,
                redirectType: RedirectType.CAMPAIGN_MANAGEMENT,
                redirectId: campaign.id,
                redirectSlug: campaign.slug,
                metadata
            });
        }
    }

    async weeklyCompletionRequestNotification() {
        const days = notificationSettings.completion_request_reminder_notification.days_array;
        const cutoffs = days.map(d => moment.utc().subtract(d, 'days').format('YYYY-MM-DD'));

        const results = await this.CampaignInfluencer.createQueryBuilder('ci')
            .leftJoinAndSelect('ci.campaign', 'campaign')
            .leftJoinAndSelect('ci.dispute', 'dispute')
            .leftJoinAndSelect('campaign.users', 'users')
            .leftJoinAndSelect('ci.influencer', 'influencer')
            .where('ci.status = :status', { status: InfluencerStatus.COMPLETION_REQUEST })
            .andWhere('ci.completion_request_at IS NOT NULL')
            .andWhere('ci.payment_mode = :paymentMode', { paymentMode: PaymentMode.ONLINE })
            .andWhere('DATE(ci.completion_request_at) IN (:...cutoffs)', { cutoffs })
            .andWhere('dispute.id IS NULL')
            .getMany();

        for (const ci of results) {
            try {
                const campaign = ci?.campaign;
                if (campaign) {
                    const message = notificationMessages.en.descriptions.completion_request_reminder_notification.replace(':campaign_name', `${campaign.campaign_name}`);
                    const metadata = {
                        campaign_id: campaign.id,
                        status: ci.status,
                        brand_status: ci.status,
                        influencer: { id: ci.influencer.id },
                        campaignName: campaign.campaign_name,
                        influencerName: ci.influencer.display_name,
                        daysAgo: Math.abs(moment.utc(ci.completion_request_at).diff(moment.utc(), 'days'))
                    };
                    await this.campaignRepository.weeklyCompletionRequestNotification(campaign.users, message, null, RedirectType.CAMPAIGN_MANAGEMENT, campaign.id, campaign.slug, metadata);
                }
            } catch (error) {
                console.error(`Error processing campaign_influencer ID ${ci.id}:`, error);
            }
        }
    }

    /**
     * Syncs CometChat groups for all campaign influencers on running campaigns.
     * - tags: campaign influencer status (single tag)
     * - name: influencer name
     * - icon (avatar): influencer image
     * - description: campaign name
     */
    async updateRunningCampaignInfluencerCometChatGroups() {
        const campaignInfluencers = await this.CampaignInfluencer.createQueryBuilder('ci')
            .innerJoinAndSelect('ci.campaign', 'campaign')
            .leftJoinAndSelect('ci.influencer', 'influencer')
            .where('campaign.status = :status', { status: CampaignStatus.RUNNING })
            .andWhere('campaign.deleted_at IS NULL')
            .getMany();

        let updated = 0;
        let skipped = 0;

        for (const ci of campaignInfluencers) {
            try {
                const tag = ci.status;
                if (!tag) {
                    skipped++;
                    continue;
                }

                const influencerName = ci.influencer?.display_name ||
                    `${ci.influencer?.first_name || ''} ${ci.influencer?.last_name || ''}`.trim() ||
                    '';
                // FULL URL
                const avatarUrl = ci.influencer?.image ?? process.env.AWS_FILE_PATH + 'images/profile/placeholder.jpg';

                const campaignName = ci.campaign?.campaign_name || '';
                const campaignId = ci.campaign?.id || '';
                await this.campaignRepository.updateCometChatGroupTags(
                    ci.id,
                    [tag, campaignId],
                    {
                        campaign_id: campaignId,
                        status: ci.status,
                        brand_status: ci.status,
                        campaignName,
                        campaign_influencer_id: ci.id,
                        influencer: ci.influencer ? { id: ci.influencer.id } : undefined,
                    },
                    {
                        name: influencerName,
                        icon: avatarUrl,
                        description: campaignName,
                    },
                );
                updated++;
            } catch (error: any) {
                skipped++;
                console.error(`Error updating CometChat group for campaign_influencer ID ${ci.id}:`, error);
            }
        }

        return { total: campaignInfluencers.length, updated, skipped };
    }

    /**
     * Backfill force-offline campaign payments for brands with
     * offline_payment_allowed = 'forced'.
     * - Converts existing unpaid / offline payments to force_offline
     * - Creates missing force_offline payment rows for closed jobs
     */
    async forceOfflinePaymentsForForcedBrands() {
        const result = {
            brands: 0,
            paymentsUpdated: 0,
            paymentsCreated: 0,
            paymentsSkipped: 0,
            errors: [] as { campaign_influencer_id?: string; payment_id?: string; message: string }[],
        };

        const forcedBrands = await this.Users.find({
            where: { offline_payment_allowed: 'forced' },
            select: ['id', 'email', 'first_name', 'last_name', 'company_name'],
        });
        result.brands = forcedBrands.length;
        if (!forcedBrands.length) {
            return result;
        }

        const brandIds = forcedBrands.map((brand) => brand.id);

        const existingPayments = await this.CampaignPayment.createQueryBuilder('campaignPayment')
            .innerJoin('campaignPayment.brand', 'brand')
            .where('brand.id IN (:...brandIds)', { brandIds })
            .andWhere('campaignPayment.payment_mode = :paymentMode', { paymentMode: PaymentMode.OFFLINE })
            .getMany();

        for (const payment of existingPayments) {
            try {
                if (payment.payment_mode === CampaignPaymentMode.FORCE_OFFLINE) {
                    result.paymentsSkipped++;
                    continue;
                }

                await this.CampaignPayment.update(payment.id, {
                    payment_mode: CampaignPaymentMode.FORCE_OFFLINE,
                    is_admin_paid: false,
                    payment_status: PAYMENT_STATUS.COMPLETED,
                });
                result.paymentsUpdated++;
            } catch (error: any) {
                result.errors.push({ payment_id: payment.id, message: error.message });
            }
        }

        return result;
    }


}
