import { Injectable, HttpException, HttpStatus, Res, Inject } from "@nestjs/common"
import { InjectRepository } from "@nestjs/typeorm"
import { Repository, Like, Not, ILike, Brackets } 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 } 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 } 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 { Posts } from "src/entities/posts.entity"

import * as ejs from 'ejs';
import * as puppeteer from 'puppeteer';
import * as fs from 'fs';
import * as path from 'path';
import { throwError } from "rxjs"
const moment = require('moment');

// DTO Files

@Injectable()
export class ReportService {
    constructor(
        private campaignRepository: CampaignRepository,
        private UsersServices: UsersServices,
        private SocketGateway: SocketGateway,
        private CommonService: CommonService,
        private StripeService: StripeServices,

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

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

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


    ) { }

    async campaignNameList(auth) {
        let results = await this.Campaign.find({ where: { users: { id: auth.id } }, select: ["id", "campaign_name"] })
        return results;
    }

    async report(request: ReportDto, auth: any) {
        request['user_id'] = auth.id;
        const barChart = await this.barChart(request);
        const pieChart = await this.pieChart(request);
        const postRows = await this.postRows(request);
        const statistics = await this.statistics(request);
        return { statistics, barChart, pieChart, postRows }
    }

    async commonQuery(startDate, endDate, request) {
        let query = this.Posts.createQueryBuilder("posts").withDeleted()
            .innerJoin("posts.campaign", "campaign")
            .where("posts.created_at >= :startDate AND posts.created_at <= :endDate", { startDate, endDate })
            .andWhere("campaign.users = :user_id", { user_id: request.user_id });
        if (request.platforms.length) {
            query = query.andWhere("posts.platform IN(:...platforms)", { platforms: request.platforms });
        }

        if (request.campaign_ids.length) {
            query = query.andWhere("campaign.id IN(:...campaign_ids)", { campaign_ids: request.campaign_ids });
        }

        if (request.status) {
            query = query.andWhere("campaign.status = :status", { status: request.status });
        }

        return query;
    }

    async statistics(request) {
        const type = request.type == "YEAR" ? "months" : "days";
        const { startDate, endDate } = await this.getLastDateRange(request.days, type);
        const query = await this.commonQuery(startDate, endDate, request);
        query.leftJoin('posts.social_media', 'social_media')
        const statistics = await query.select(['COUNT(posts.id) as total_posts',
            "COALESCE(SUM(posts.comment_count),'0') as comment_count",
            "COALESCE(SUM(posts.like_counts),'0')  as total_likes",
            "COALESCE(SUM(posts.view_counts),'0')  as total_views",
            "COALESCE(SUM(social_media.follower_count),'0')  as total_followers"])
            .getRawOne();
        if (statistics) {
            const totalFollowers = parseInt(statistics.total_followers);
            const totalEngagements = parseInt(statistics.total_likes, 10) + parseInt(statistics.comment_count, 10) + parseInt(statistics.total_views, 10);

            statistics.engagement_ratio = totalFollowers > 0 ? ((totalEngagements / totalFollowers) * 100).toFixed(2) : "0";
        }
        return statistics;
    }

    // ################################################## GET ROW DATA ##################################################
    async postRows(request) {
        const type = request.type == "YEAR" ? "months" : "days";
        const { startDate, endDate } = await this.getLastDateRange(request.days, type);
        const query = await this.commonQuery(startDate, endDate, request);
        query.leftJoin('posts.influencer', 'influencer')
            .select(['posts', 'influencer.id', 'influencer.first_name', 'influencer.total_followers', 'influencer.last_name', 'influencer.slug', 'influencer.image', 'influencer.display_name']);
        const postRows = await query.getMany();
        return postRows;
    }
    // ################################################## GET PIE CHART DATA ##################################################

    async pieChart(request) {
        let barChart = [];
        const type = request.type == "YEAR" ? "months" : "days";
        const { startDate, endDate } = await this.getLastDateRange(request.days, type);
        const platforms = ['instagram', 'facebook', 'tiktok', 'youtube', 'xiaohongshu'];
        let totalPosts = 0;
        for (const platform of platforms) {
            const postCount = await this.pieChartQuery(startDate, endDate, platform, request);
            barChart.push({
                platform: platform,
                posts: postCount
            });
            totalPosts += postCount;
        }

        const percentageData = barChart.map(item => ({
            platform: item.platform,
            posts: item.posts,
            percentage: totalPosts > 0 ? ((item.posts / totalPosts) * 100).toFixed(2) : "0"
        }));

        return percentageData;
    }

    async getLastDateRange(days, type) {
        const endDate = moment().format('YYYY-MM-DD HH:mm:ss'); // Today's date
        const startDate = moment().subtract(days, type).format('YYYY-MM-DD HH:mm:ss'); // type = months,days
        return { startDate, endDate };
    }


    async pieChartQuery(startDate, endDate, platform, request) {
        const query = (await this.commonQuery(startDate, endDate, request)).andWhere("posts.platform = :platform", { platform })
        return query.getCount();
    }
    // ################################################## GET BAR CHART DATA ##################################################
    async barChart(request) {
        let barChart = [];
        if (request.type == "YEAR") {
            const months = await this.getLastMonths(request.days);
            for (const month of months) {
                const postCount = await this.barChartQuery(month.startDate, month.endDate, request);
                barChart.push({
                    day: month.month,
                    posts: postCount
                });
            }
        } else {
            const days = await this.getLastNDays(request.days);
            for (const day of days) {
                const postCount = await this.barChartQuery(day.startDatetime, day.endDatetime, request);
                barChart.push({
                    day: day.day,
                    posts: postCount
                });
            }
        }
        return barChart;
    }


    async barChartQuery(startDate, endDate, request) {
        const query = await this.commonQuery(startDate, endDate, request)
        return query.getCount();
    }

    async getLastMonths(monthsCount: number) {
        const months = [];
        let currentDate = new moment();

        for (let i = 0; i < monthsCount; i++) {
            const month = currentDate.format('MMM-YYYY'); // Example: 'Feb-2025'

            // Start date: 1st day of the month
            const startDate = currentDate.clone().startOf('month').format('YYYY-MM-DD');

            // End date: Last day of the month
            const endDate = currentDate.clone().endOf('month').format('YYYY-MM-DD');

            months.push({
                month,
                startDate,
                endDate
            });

            // Move to previous month
            currentDate = currentDate.subtract(1, 'months');
        }
        return months.reverse(); // Reverse to maintain chronological order
    }

    async getLastNDays(days) {
        const daysArray = [];
        let currentDate = moment(); // Start from today

        for (let i = 0; i < days; i++) {
            const startDatetime = currentDate.clone().startOf('day').format('YYYY-MM-DD HH:mm:ss');
            const endDatetime = currentDate.clone().endOf('day').format('YYYY-MM-DD HH:mm:ss');

            daysArray.push({
                day: currentDate.format('YYYY-MM-DD'), // Just for reference
                startDatetime,
                endDatetime
            });

            // Move to the previous day
            currentDate = currentDate.subtract(1, 'days');
        }

        return daysArray.reverse(); // Reverse to get in chronological order
    }

    async exportReport(queryParam, res: any, auth) {

        let request = {
            user_id: auth.id,
            campaign_ids: queryParam.campaign_ids.split(",") || [],
            platforms: queryParam.platforms ? queryParam.platforms.split(",") : [],
            type: queryParam.type,
            days: queryParam.days,
            status: queryParam.status
        };

        if (request.campaign_ids.length != 1) {
            throw new HttpException(responseMessages.en.report.export_single_campaign, HttpStatus.BAD_REQUEST)
        }

        const postRows = await this.postRows(request);
        const currentDate = moment().format('YYYY-MM-DD');
        const campaign = await this.Campaign.findOne({ where: { id: request.campaign_ids[0] } });
        // Path to the EJS template

        const templatePath = path.join(__dirname, '..', '..', '..', 'src', 'templates', 'reports', `campaign-report.ejs`);
        const footerTemplatePath = path.join(__dirname, '..', '..', '..', 'src', 'templates', 'reports', `footer.ejs`)

        // Read the EJS template
        const template = fs.readFileSync(templatePath, 'utf-8');
        const templateFooter = fs.readFileSync(footerTemplatePath, 'utf-8');
        const profilePath = process.env.AWS_FILE_PATH + 'images/profile/';

        // Render the EJS template with dynamic data
        const htmlContent = ejs.render(template, { postRows, campaign, currentDate, profilePath });

        // Launch Puppeteer / executablePath: '/usr/bin/chromium-browser'
        // const browser = await puppeteer.launch({
        //     headless: true,
        //     executablePath: '/usr/bin/chromium-browser',
        //     args: [
        //         '--no-sandbox',
        //         '--disable-setuid-sandbox',
        //         '--disable-dev-shm-usage',
        //         '--disable-gpu'
        //     ]
        // });
        const browser = await puppeteer.launch({
            headless: true,
            executablePath: '/usr/bin/google-chrome', // or '/usr/bin/google-chrome-stable'
            args: ['--no-sandbox', '--disable-setuid-sandbox']
        });
        const page = await browser.newPage();

        // Set the HTML content in Puppeteer
        await page.setContent(htmlContent, { waitUntil: 'networkidle0' });

        // Generate the PDF
        const pdfBuffer = await page.pdf({
            format: 'A4',
            displayHeaderFooter: true, // Enable footer
            footerTemplate: templateFooter,
            printBackground: true
        });

        await browser.close();
        // Send the PDF as a response
        res.setHeader('Content-Type', 'application/pdf');
        res.setHeader('Content-Disposition', 'inline; filename="document.pdf"');
        res.end(pdfBuffer);
    }
}
