import { Injectable, HttpException, HttpStatus, Inject, forwardRef, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
// BullMQ disabled — AI reports run via in-process background (setImmediate)
// import { InjectQueue } from '@nestjs/bullmq';
// import { Queue } from 'bullmq';
import { Repository } from 'typeorm';
import { responseMessages } from '../../messages/response-messages';
import { Constants } from 'src/common/constants';
import { CampaignRepository } from './campaign.repository';
import { Campaign } from 'src/entities/campaign.entity';
import { CampaignInfluencer, InfluencerStatus } from 'src/entities/campaign_influencer.entity';
import { KeyType, Settings } from 'src/entities/settings.entity';
import { UsersServices } from 'src/common/services/users/users.service';
import { RedirectType } from 'src/entities/notifications.entity';
import { OpenAIService } from 'src/common/services/openai/openai.service';
import { AiReportLog, AiReportStatus } from 'src/entities/ai_report_log.entity';
import { CampaignAiReport } from 'src/entities/campaign-ai-report.entity';
import { CreateAiReportDto } from './dtos/ai-report.dto';
import { CampaignService } from './campaign.services';
import { notificationMessages } from 'src/messages/notifications-messages';
// import {
//     AI_REPORT_QUEUE,
//     AI_REPORT_JOB,
//     AI_REPORT_JOB_OPTIONS,
//     AiReportJobData,
// } from 'src/common/services/queue/queue.constants';

@Injectable()
export class AiReportService {
    private readonly logger = new Logger(AiReportService.name);

    constructor(
        private campaignRepository: CampaignRepository,
        private readonly OpenAIService: OpenAIService,

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

        @Inject(forwardRef(() => CampaignService))
        private campaignService: CampaignService,

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

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

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

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

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

        // @InjectQueue(AI_REPORT_QUEUE)
        // private readonly aiReportQueue: Queue<AiReportJobData>,
    ) { }

    /**
     * Start / regenerate an AI agent report for a campaign (Applicant page → AI Agent).
     * Creates ai_report_log (Pending), then processes asynchronously (LLM → Completed).
     */
    async createAiReport(params: { campaign_id: string }, body: CreateAiReportDto, authUser: any) {
        const campaign = await this.campaignRepository.checkCampaignExists(params.campaign_id, authUser);
        if (!campaign.is_active) {
            throw new HttpException(responseMessages.en.campaign.campaign_inactive, HttpStatus.BAD_REQUEST);
        }

        const activeJob = await this.AiReportLog.findOne({
            where: [
                { campaign: { id: campaign.id }, status: AiReportStatus.PENDING },
                { campaign: { id: campaign.id }, status: AiReportStatus.IN_PROGRESS },
            ],
        });
        if (activeJob) {
            // BullMQ/Redis check disabled — block while DB shows Pending/In_progress
            // const redisState = await this.getRedisJobState(activeJob.id);
            // if (redisState === 'active' || redisState === 'waiting' || redisState === 'delayed') { ... }
            this.logger.warn(
                `[AI Report ${activeJob.id}] DB=${activeJob.status} | Start blocked (in progress)`,
            );
            throw new HttpException(
                {
                    statusCode: HttpStatus.CONFLICT,
                    message: responseMessages.en.campaign.ai_report_in_progress,
                    report_id: activeJob.id,
                    status: activeJob.status,
                },
                HttpStatus.CONFLICT,
            );
        }

        let quotaDeducted = false;
        // const permission = await this.UsersServices.getUserPermission(authUser.id, 'ai_agent_report');
        // if (permission) {
        //     if (!permission.remaining) {
        //         throw new HttpException(responseMessages.en.campaign.ai_report_quota_exceeded, HttpStatus.FORBIDDEN);
        //     }
        //     await this.UsersServices.decreasePermissionCount(authUser.id, 'ai_agent_report');
        //     quotaDeducted = true;
        // }

        const languageCode = authUser.language || 'en';
        const topN = await this.getAiReportTopN();
        const requestJson = await this.buildAiReportRequestJson(campaign.id, body, languageCode, topN);

        // if (!requestJson.influencers?.length) {
        //     if (quotaDeducted) {
        //         await this.UsersServices.increasePermissionCount(authUser.id, 'ai_agent_report');
        //     }
        //     throw new HttpException(responseMessages.en.campaign.ai_report_no_eligible, HttpStatus.BAD_REQUEST);
        // }

        const report = this.AiReportLog.create({
            campaign: { id: campaign.id } as any,
            user: { id: authUser.id } as any,
            status: AiReportStatus.PENDING,
            number_of_applicant: requestJson.influencers.length,
            request_json: requestJson,
            metadata_json: {
                applicant_ids: requestJson.influencers.map((i: any) => i.campaign_influencer_id),
                top_n: topN,
                objective: body.objective,
                remark: body.remark || null,
            },
            quota_deducted: quotaDeducted,
        });
        const saved = await this.AiReportLog.save(report);
        await this.setCampaignAiReportInProgress(campaign.id, true);
        this.logger.log(
            `[AI Report ${saved.id}] DB=Pending | campaign=${campaign.id} | applicants=${saved.number_of_applicant} | created`,
        );

        // BullMQ disabled — always run in local background process
        // await this.dispatchAiReport(saved.id);
        this.runAiReportInBackground(saved.id);

        return {
            data: {
                report_id: saved.id,
                status: saved.status,
                number_of_applicant: saved.number_of_applicant,
                ai_report_inprogress: true,
            },
        };
    }

    async getAiReportStatus(params: { campaign_id: string }, authUser: any) {
        const campaign = await this.campaignRepository.checkCampaignExists(params.campaign_id, authUser);

        const latest = await this.AiReportLog.findOne({
            where: { campaign: { id: params.campaign_id } },
            order: { created_at: 'DESC' },
        });

        const pendingCount = await this.CampaignInfluencer.createQueryBuilder('ci')
            .where('ci.campaign_id = :campaignId', { campaignId: params.campaign_id })
            .andWhere('(ci.ai_score_pending = true OR ci.ai_score IS NULL)')
            .getCount();

        const isProcessing = Boolean(campaign.ai_report_inprogress) || (
            latest
                ? [AiReportStatus.PENDING, AiReportStatus.IN_PROGRESS].includes(latest.status)
                : false
        );

        return {
            data: {
                ai_report_inprogress: Boolean(campaign.ai_report_inprogress),
                latest_report: latest
                    ? {
                        id: latest.id,
                        status: latest.status,
                        number_of_applicant: latest.number_of_applicant,
                        response_json: latest.status === AiReportStatus.COMPLETED ? latest.response_json : null,
                        error_message: latest.error_message,
                        created_at: latest.created_at,
                        completed_at: latest.completed_at,
                        objective: latest.metadata_json?.objective ?? latest.request_json?.objective ?? null,
                        remark: latest.metadata_json?.remark ?? latest.request_json?.remark ?? null,
                    }
                    : null,
                new_applicant_count: pendingCount,
                is_processing: isProcessing,
            },
        };
    }

    async listAiReports(params: { campaign_id: string }, authUser: any) {
        await this.campaignRepository.checkCampaignExists(params.campaign_id, authUser);

        const reports = await this.AiReportLog.find({
            where: { campaign: { id: params.campaign_id } },
            order: { created_at: 'DESC' },
            select: [
                'id',
                'status',
                'number_of_applicant',
                'created_at',
                'completed_at',
                'started_at',
                'error_message',
                'metadata_json',
            ],
        });

        return {
            data: reports.map((r) => ({
                id: r.id,
                status: r.status,
                number_of_applicant: r.number_of_applicant,
                created_at: r.created_at,
                completed_at: r.completed_at,
                started_at: r.started_at,
                error_message: r.error_message,
                objective: r.metadata_json?.objective ?? null,
                remark: r.metadata_json?.remark ?? null,
            })),
        };
    }

    async getAiReport(params: { campaign_id: string; report_id: string }, authUser: any) {
        await this.campaignRepository.checkCampaignExists(params.campaign_id, authUser);

        const report = await this.AiReportLog.findOne({
            where: { id: params.report_id, campaign: { id: params.campaign_id } },
        });
        if (!report) {
            throw new HttpException(responseMessages.en.campaign.ai_report_not_exists, HttpStatus.NOT_FOUND);
        }

        return { data: report };
    }

    /**
     * Recovery: re-dispatch Pending/In_progress reports via local background process.
     */
    async processPendingAiReports(limit = 3) {
        const pending = await this.AiReportLog.find({
            where: [
                { status: AiReportStatus.PENDING },
                { status: AiReportStatus.IN_PROGRESS },
            ],
            order: { created_at: 'ASC' },
            take: limit,
        });
        const started: string[] = [];
        for (const report of pending) {
            try {
                if (report.status === AiReportStatus.IN_PROGRESS) {
                    await this.AiReportLog.update(
                        { id: report.id },
                        { status: AiReportStatus.PENDING },
                    );
                }
                // await this.dispatchAiReport(report.id);
                this.runAiReportInBackground(report.id);
                started.push(report.id);
            } catch (error: any) {
                this.logger.error(`Failed recovering AI report ${report.id}: ${error?.message || error}`);
            }
        }
        return { data: { enqueued_count: started.length, report_ids: started } };
    }

    /**
     * BullMQ dispatch disabled — kept for reference.
     */
    // private async dispatchAiReport(reportId: string): Promise<'queued' | 'local'> {
    //     try {
    //         await this.enqueueAiReportJob(reportId);
    //         return 'queued';
    //     } catch (err: any) {
    //         this.logger.warn(
    //             `[AI Report ${reportId}] BullMQ unavailable (${err?.message || err}) | running locally in background`,
    //         );
    //         this.runAiReportInBackground(reportId);
    //         return 'local';
    //     }
    // }

    private runAiReportInBackground(reportId: string) {
        this.logger.log(`[AI Report ${reportId}] starting local background process`);
        setImmediate(() => {
            void this.processAiReportJob(reportId, { isLastAttempt: true }).catch((error: any) => {
                this.logger.error(
                    `[AI Report ${reportId}] local background run failed: ${error?.message || error}`,
                );
            });
        });
    }

    // private async getRedisJobState(reportId: string): Promise<string | null> {
    //     try {
    //         const existing = await this.aiReportQueue.getJob(reportId);
    //         if (!existing) {
    //             return null;
    //         }
    //         return existing.getState();
    //     } catch (error: any) {
    //         this.logger.warn(`[AI Report ${reportId}] could not read Redis job state: ${error?.message || error}`);
    //         return null;
    //     }
    // }

    // async enqueueAiReportJob(reportId: string): Promise<boolean> {
    //     const existing = await this.aiReportQueue.getJob(reportId);
    //     if (existing) {
    //         const state = await existing.getState();
    //         if (['waiting', 'delayed', 'active', 'paused'].includes(state)) {
    //             this.logger.log(`[AI Report ${reportId}] QUEUE=${state} | already in Redis, skip add`);
    //             return false;
    //         }
    //         this.logger.log(`[AI Report ${reportId}] QUEUE=${state} | removing old Redis job before re-add`);
    //         await existing.remove();
    //     }
    //
    //     await this.aiReportQueue.add(
    //         AI_REPORT_JOB,
    //         { reportId },
    //         {
    //             jobId: reportId,
    //             attempts: AI_REPORT_JOB_OPTIONS.attempts,
    //             backoff: {
    //                 type: 'exponential',
    //                 delay: AI_REPORT_JOB_OPTIONS.backoffDelayMs,
    //             },
    //         },
    //     );
    //     this.logger.log(`[AI Report ${reportId}] QUEUE=waiting | added to Redis (job generate)`);
    //     return true;
    // }

    async processAiReportJob(
        reportId: string,
        options?: { isLastAttempt?: boolean; isRetry?: boolean },
    ) {
        const report = await this.AiReportLog.findOne({
            where: { id: reportId },
            relations: ['campaign', 'user'],
        });
        if (!report) {
            return;
        }
        this.logger.log(`[AI Report ${reportId}] DB=${report.status} | processor loaded row`);
        if (report.status === AiReportStatus.COMPLETED) {
            return;
        }
        // Another runner already claimed this job (avoid double OpenAI calls).
        if (report.status === AiReportStatus.IN_PROGRESS && !options?.isRetry) {
            this.logger.log(`[AI Report ${reportId}] DB=In_progress | skip duplicate processor`);
            return;
        }

        const now = new Date();
        const claimStatuses = options?.isRetry
            ? [AiReportStatus.PENDING, AiReportStatus.IN_PROGRESS, AiReportStatus.FAILED]
            : [AiReportStatus.PENDING, AiReportStatus.FAILED];
        const claim = await this.AiReportLog.createQueryBuilder()
            .update(AiReportLog)
            .set({
                status: AiReportStatus.IN_PROGRESS,
                started_at: report.started_at ?? now,
            })
            .where('id = :id', { id: reportId })
            .andWhere('status IN (:...statuses)', {
                statuses: claimStatuses,
            })
            .execute();

        if (!claim.affected) {
            this.logger.log(`[AI Report ${reportId}] claim missed | another runner owns it`);
            return;
        }

        report.status = AiReportStatus.IN_PROGRESS;
        report.started_at = report.started_at ?? now;

        const startedMs = Date.now();
        const isLastAttempt = options?.isLastAttempt ?? true;
        try {
            const aiResult = await this.OpenAIService.createAiAgentReport(report.request_json);
            const influencers = Array.isArray(aiResult.content?.influencers)
                ? aiResult.content.influencers
                : [];

            if (!report.campaign?.id) {
                throw new Error('AI report campaign is missing');
            }

            const reportNumber = await this.getNextCampaignReportNumber(report.campaign.id);

            const storedCount = await this.persistAiReportResults(report.campaign.id, reportNumber, influencers);
            const criteria = this.normalizeCriteria(aiResult.content?.criteria);
            if (criteria?.length) {
                const criteriaMap = this.toCriteriaMap(report.campaign.ai_criteria);
                criteriaMap[reportNumber] = criteria;
                await this.Campaign.update({ id: report.campaign.id }, { ai_criteria: criteriaMap });
                report.campaign.ai_criteria = criteriaMap;
            }
            this.logger.log(`[AI Report ${reportId}] stored ${storedCount} campaign_ai_reports rows as ${reportNumber}`);

            report.response_json = aiResult.content;
            report.response_id = aiResult.response_id;
            report.model_name = aiResult.model_name;
            report.input_token = aiResult.input_token;
            report.output_token = aiResult.output_token;
            report.total_token = aiResult.total_token;
            report.metadata_json = {
                ...(report.metadata_json || {}),
                report_number: reportNumber,
                latency_ms: Date.now() - startedMs,
                scored_count: influencers.length,
                stored_count: storedCount,
                criteria,
            };
            report.status = AiReportStatus.COMPLETED;
            report.completed_at = new Date();
            report.error_message = null;
            await this.AiReportLog.save(report);
            await this.setCampaignAiReportInProgress(report.campaign?.id, false);
            this.logger.log(
                `[AI Report ${reportId}] DB=Completed | scored=${influencers.length} | tokens=${report.total_token} | ${Date.now() - startedMs}ms`,
            );

            if (report.user) {
                try {
                    const campaign = report.campaign;
                    const campaignName = campaign?.campaign_name || '';
                    const notificationMessage = notificationMessages.en.descriptions.ai_agent_report.replace(
                        ':campaign_name',
                        campaignName,
                    );
                    await this.campaignService.sendCampaignNotification({
                        type: 'ai_agent_report',
                        receiver: report.user,
                        description: notificationMessage,
                        sender: null,
                        redirectType: RedirectType.AI_REPORT,
                        redirectId: campaign?.id,
                        redirectSlug: campaign?.slug,
                        metadata: {
                            campaign_id: campaign?.id,
                            campaignName,
                            report_id: reportId,
                            number_of_applicant: report.number_of_applicant,
                        },
                    });
                    this.logger.log(`[AI Report ${reportId}] notification sent via sendCampaignNotification`);
                } catch (notifyError: any) {
                    this.logger.error(
                        `[AI Report ${reportId}] notification failed: ${notifyError?.message || notifyError}`,
                    );
                }
            }
        } catch (error: any) {
            report.metadata_json = {
                ...(report.metadata_json || {}),
                latency_ms: Date.now() - startedMs,
                error: error?.message || String(error),
            };

            if (isLastAttempt) {
                report.status = AiReportStatus.FAILED;
                report.completed_at = new Date();
                report.error_message = error?.message || 'AI report generation failed';
                await this.AiReportLog.save(report);
                await this.setCampaignAiReportInProgress(report.campaign?.id, false);
                this.logger.error(
                    `[AI Report ${reportId}] DB=Failed | last attempt | ${report.error_message}`,
                );
            } else {
                await this.AiReportLog.save(report);
                this.logger.warn(
                    `[AI Report ${reportId}] DB=In_progress | will retry | ${error?.message || error}`,
                );
            }
            throw error;
        }
    }

    private async persistAiReportResults(campaignId: string, reportNumber: string, influencers: any[]) {
        const eligible = influencers.filter((item) => item?.campaign_influencer_id);
        if (!eligible.length) {
            return 0;
        }

        const reportRows: any[] = [];
        for (const item of eligible) {
            const campaignInfluencerId = item.campaign_influencer_id;
            const aiScore = Number.isFinite(Number(item.ai_score)) ? Number(item.ai_score) : null;

            await this.CampaignInfluencer.update(
                { id: campaignInfluencerId },
                {
                    ai_score: aiScore,
                    ai_note: item.ai_note ? String(item.ai_note).slice(0, 2000) : null,
                    ai_score_pending: false,
                },
            );

            reportRows.push({
                campaign: { id: campaignId },
                campaign_influencer: { id: campaignInfluencerId },
                report_number: reportNumber,
                response_json: item.criteria_scores,
            });
        }

        await this.CampaignAiReport.save(reportRows);
        return reportRows.length;
    }

    private async getNextCampaignReportNumber(campaignId: string): Promise<string> {
        const result = await this.CampaignAiReport.createQueryBuilder('report')
            .select('COUNT(DISTINCT report.report_number)', 'count')
            .where('report.campaign_id = :campaignId', { campaignId })
            .getRawOne();
        return `report-${(Number(result?.count) || 0) + 1}`;
    }

    private toCriteriaMap(existing: any): Record<string, string[]> {
        if (!existing) {
            return {};
        }
        if (Array.isArray(existing)) {
            return existing.length ? { 'report-1': existing } : {};
        }
        if (typeof existing === 'object') {
            return { ...existing };
        }
        return {};
    }

    private normalizeCriteria(criteria: any): string[] | null {
        if (!Array.isArray(criteria) || !criteria.length) {
            return null;
        }
        const labels = criteria
            .map((item) => (typeof item === 'string' ? item : item?.criteria ?? item?.label ?? item?.name))
            .map((label) => String(label || '').trim())
            .filter(Boolean);
        return labels.length ? labels : null;
    }

    private async getAiReportTopN(): Promise<number> {
        try {
            const setting = await this.Settings.findOne({ where: { type: KeyType.AI_AGENT_REPORT_TOP_N } });
            const parsed = parseInt(setting?.value, 10);
            if (Number.isFinite(parsed) && parsed > 0) {
                return parsed;
            }
        } catch (error: any) {
            this.logger.warn(`Failed to load AI_AGENT_REPORT_TOP_N from settings: ${error?.message || error}`);
        }
        return Constants.AI_AGENT_REPORT_TOP_N;
    }

    private async buildAiReportRequestJson(campaignId: string, body: CreateAiReportDto, brandLanguageCode: string, topN: number) {
        const languageCode = 'en';
        const campaignQuery = await this.campaignRepository.commonQuery(languageCode);
        const campaign = await campaignQuery.andWhere('campaign.id = :campaignId', { campaignId }).getOne();
        if (!campaign) {
            throw new HttpException(responseMessages.en.campaign.campaign_not_exists, HttpStatus.NOT_FOUND);
        }

        const applicants = await this.CampaignInfluencer.createQueryBuilder('ci')
            .leftJoinAndSelect('ci.influencer', 'influencer')
            .leftJoinAndSelect('influencer.influencerCategories', 'influencerCategories')
            .leftJoinAndSelect('influencerCategories.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 },
            )
            .leftJoinAndSelect('influencer.userKeywords', 'userKeywords')
            .leftJoinAndSelect('userKeywords.keywords', 'keywords')
            .leftJoinAndSelect('influencer.social_medias', 'social_medias')
            .leftJoinAndSelect('social_medias.instagramMedias', 'instagramMedias')
            .leftJoinAndSelect('social_medias.tikTokVideos', 'tikTokVideos')
            .leftJoinAndSelect('social_medias.facebookPosts', 'facebookPosts')
            .leftJoinAndSelect('social_medias.youtubeVideos', 'youtubeVideos')
            .where('ci.campaign_id = :campaignId', { campaignId })
            // .andWhere('(ci.ai_score_pending = true OR ci.ai_score IS NULL)')
            .andWhere('ci.status = :status', { status: InfluencerStatus.QUOTATION_AVAILABLE })
            .orderBy('ci.recommended_score', 'DESC')
            .take(topN)
            .getMany();

        const influencerIds = [...new Set(applicants.map((ci) => ci.influencer?.id).filter(Boolean))];
        const collaborationRows = influencerIds.length
            ? await this.CampaignInfluencer.createQueryBuilder('collab')
                .innerJoin('collab.campaign', 'campaign')
                .withDeleted()
                .select('collab.influencerId', 'influencer_id')
                .addSelect('COUNT(DISTINCT campaign.users)', 'brand_collaboration_count')
                .where('collab.influencerId IN (:...influencerIds)', { influencerIds })
                .andWhere('collab.status = :closedStatus', { closedStatus: InfluencerStatus.CLOSED })
                .andWhere('collab.is_archived = false')
                .groupBy('collab.influencerId')
                .getRawMany()
            : [];
        const brandCollaborationCountByInfluencerId = Object.fromEntries(
            collaborationRows.map((row) => [row.influencer_id, Number(row.brand_collaboration_count) || 0]),
        );

        const categories = (campaign.campaignCategory || []).map((cc: any) => ({
            id: cc.category?.id,
            title: cc.category?.translations?.[0]?.title || cc.category?.title,
            slug: cc.category?.translations?.[0]?.slug || cc.category?.slug,
        }));

        const platforms = Array.isArray(campaign.platforms)
            ? campaign.platforms
            : campaign.platforms
                ? Object.values(campaign.platforms)
                : [];

        return {
            objective: body.objective,
            remark: body.remark || null,
            campaign: {
                id: campaign.id,
                campaign_name: campaign.campaign_name,
                slug: campaign.slug,
                currency: campaign.currency,
                budget_per_influencer: campaign.budget_per_influencer,
                number_of_influencers: campaign.number_of_influencers,
                total_budget: campaign.total_budget,
                content_type: campaign.content_type,
                influencer_type: campaign.influencer_type,
                platforms,
                job_requirement: campaign.job_requirement,
                posting_start_date: campaign.posting_start_date,
                posting_end_date: campaign.posting_end_date,
                first_draft_date: campaign.first_draft_date,
                campaign_country: campaign.campaign_country,
                campaign_requirements: campaign.campaign_requirements,
                profile_privacy: campaign.profile_privacy,
                job_privacy: campaign.job_privacy,
                is_barter: campaign.is_barter,
                product_value: campaign.product_value,
                status: campaign.status,
                applied: campaign.applied,
                categories: categories,
                brand_language_code: brandLanguageCode,
            },
            influencers: applicants.map((ci) => {
                const influencer = ci.influencer;
                const influencerCategories = (influencer?.influencerCategories || []).map((ic: any) => ({
                    id: ic.category?.id,
                    title: ic.category?.translations?.[0]?.title || ic.category?.title,
                    slug: ic.category?.translations?.[0]?.slug || ic.category?.slug,
                }));
                const keywords = (influencer?.userKeywords || [])
                    .map((uk: any) => uk.keywords?.translations?.[0]?.title || uk.keywords?.title)
                    .filter(Boolean);
                const socialMedias = (influencer?.social_medias || []).map((sm: any) => ({
                    social_type: sm.social_type,
                    username: sm.social_user_name,
                    followers: sm.follower_count,
                    engagement_rate: sm.engagement_rate,
                    instagramMedias: (sm.instagramMedias || []).map((im: any) => ({
                        id: im.id,
                        caption: im.caption || null,
                        like_count: im.like_count,
                        comment_count: im.comments_count,
                        reel_views_count: im.reel_views_count,
                        interactions: (Number(im.like_count) || 0) + (Number(im.comments_count) || 0),
                    })),
                    tikTokVideos: (sm.tikTokVideos || []).map((tk: any) => ({
                        id: tk.id,
                        caption: tk.title || null,
                        like_count: tk.like_count,
                        share_count: tk.share_count,
                        comment_count: tk.comment_count,
                        view_count: tk.view_count,
                        interactions: (Number(tk.like_count) || 0) + (Number(tk.comment_count) || 0) + (Number(tk.share_count) || 0),
                    })),
                    facebookPosts: (sm.facebookPosts || []).map((fb: any) => ({
                        id: fb.id,
                        caption: fb.message || fb.title || null,
                        like_count: fb.like_counts,
                        comment_count: fb.comment_count,
                        interactions: (Number(fb.like_counts) || 0) + (Number(fb.comment_count) || 0),
                    })),
                    youtubeVideos: (sm.youtubeVideos || []).map((yt: any) => ({
                        id: yt.id,
                        caption: yt.description || yt.title || null,
                        title: yt.title || null,
                        like_count: yt.like_counts,
                        comment_count: yt.comment_count,
                        view_counts: yt.view_counts,
                        interactions: (Number(yt.like_counts) || 0) + (Number(yt.comment_count) || 0),
                    })),
                }));

                return {
                    campaign_influencer_id: ci.id,
                    name: influencer?.display_name || `${influencer?.first_name || ''} ${influencer?.last_name || ''}`.trim(),
                    recommended_score: Number(ci.recommended_score || 0),
                    country: influencer?.country,
                    platforms: influencer?.platforms,
                    categories: influencerCategories,
                    keywords,
                    about_me: influencer?.about_me,
                    ai_summary: influencer?.ai_summary || null,
                    message_to_brand: ci.message,
                    social_medias: socialMedias,
                    predefined_criteria: {
                        influencer_budget: ci.influencer_budget,
                        total_followers: influencer?.total_followers,
                        engagement_ratio: influencer?.engagement_ratio,
                        brand_collaboration_count: brandCollaborationCountByInfluencerId[influencer?.id] || 0,
                    }
                };
            }),
        };
    }

    private async setCampaignAiReportInProgress(campaignId: string, inProgress: boolean) {
        await this.Campaign.update({ id: campaignId }, { ai_report_inprogress: inProgress });
    }

    async getAiReportResults(params: any, query: any, authUser: any) {
        const campaign = await this.campaignRepository.checkCampaignExists(params.campaign_id, authUser);
        const languageCode = authUser.language || 'en';
        const campaignAiReport = this.CampaignAiReport.createQueryBuilder('car')
            .leftJoin('car.campaign_influencer', 'campaign_influencer')
            .leftJoin('campaign_influencer.influencer', 'influencer')
            .leftJoin('influencer.influencerCategories', 'influencerCategories')
            .leftJoin('influencerCategories.category', 'category')
            .leftJoin("category.translations", "translation_req", "translation_req.language_code = :languageCode AND translation_req.deleted_at IS NULL", { languageCode })
            .leftJoin("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 })
            .leftJoin('influencer.social_medias', 'social_medias')
            .where('car.campaign_id = :campaignId', { campaignId: params.campaign_id })
            // .andWhere('campaign_influencer.status = :status', { status: InfluencerStatus.QUOTATION_AVAILABLE })
            .select([
                'car.id',
                'car.report_number',
                'car.response_json',
                'car.created_at',
                'campaign_influencer.id',
                'campaign_influencer.status',
                'campaign_influencer.ai_note',
                'campaign_influencer.ai_score',
                'campaign_influencer.ai_score_pending',
                'campaign_influencer.influencer_budget',
                'campaign_influencer.currency',
                'influencer.id',
                'influencer.display_name',
                'influencer.slug',
                'influencer.country',
                'influencer.platforms',
                'influencer.about_me',
                'influencer.total_followers',
                'influencer.engagement_ratio',
                'influencerCategories.id',
                'category.id',
                'translation.title',
                'translation.slug',
                'social_medias.social_type',
                'social_medias.social_user_name',
                'social_medias.follower_count',
                'social_medias.engagement_rate',
            ]);

        let reportNumber = query.report_number;
        if (!reportNumber) {
            const latest = await this.CampaignAiReport.findOne({
                where: { campaign: { id: params.campaign_id } },
                order: { created_at: 'DESC' },
            });
            reportNumber = latest?.report_number;
        }
        if (reportNumber) {
            campaignAiReport.andWhere('car.report_number = :reportNumber', { reportNumber });
        }

        const data = await campaignAiReport.getMany();
        const new_applicant_count = await this.CampaignInfluencer.createQueryBuilder('ci')
            .where('ci.campaign_id = :campaignId', { campaignId: params.campaign_id })
            .andWhere('(ci.ai_score_pending = true OR ci.ai_score IS NULL)')
            .getCount();
        const campaignInfo = {
            id: campaign.id,
            campaign_name: campaign.campaign_name,
            slug: campaign.slug,
            ai_criteria: campaign.ai_criteria,
            ai_report_inprogress: campaign.ai_report_inprogress,
        }
        return { data, new_applicant_count, campaignInfo };
    }
}
