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"
const moment = require('moment');

// Entities Files
import * as ExcelJS from 'exceljs';
import { CampaignRepository } from "src/modules/campaign/campaign.repository"
import { Campaign } from "src/entities/campaign.entity"
import { Users } from "src/entities/users.entity"
import { responseMessages } from "src/messages/response-messages"
import { CampaignPayment, PAYMENT_STATUS } from "src/entities/campaign_payment.entity"
import { CampaignPaymentHistory, PAYMENT_STATUS as PaymentType } from "src/entities/campaign_payment_history.entity"
import { Posts } from "src/entities/posts.entity";
import { CampaignRecommended } from "src/entities/campaign_recommended.entity";
import { notificationMessages } from "src/messages/notifications-messages";
import { RedirectType } from "src/entities/notifications.entity";
import { PaymentIdParamDto } from "./dto/payment-id-param.dto";
import { CampaignService } from "src/modules/campaign/campaign.services";

// DTO Files

@Injectable()
export class CampaignServiceForAdmin {
    constructor(
        private campaignRepository: CampaignRepository,
        private campaignService: CampaignService,

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

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

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

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

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

    ) { }

    async list(queryParam: 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 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 = queryParam.language_code || 'en';
        let queryBuilder = await this.campaignRepository.commonQuery(languageCode); // Resolve the promise

        let query = queryBuilder.orderBy(sort_by, sort_type)
        query = query.where("campaign.deleted_at IS NULL")
        if (queryParam.search) {
            query = query.andWhere(`(campaign.campaign_name ILIKE :search)`, { search: `%${queryParam.search}%` })
        }

        if (queryParam.status) {
            query = query.andWhere(`(campaign.status = :status)`, { status: queryParam.status })
        }
        if (queryParam.is_active) {
            query = query.andWhere(`(campaign.is_active = :is_active)`, { is_active: queryParam.is_active })
        }

        if (queryParam.profile_privacy) {
            query = query.andWhere(`(campaign.profile_privacy = :profile_privacy)`, { profile_privacy: queryParam.profile_privacy })
        }

        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 recommended(queryParam: 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 sortByArr = {
            first_name: "influencer.first_name",
            total_followers: "influencer.total_followers",
            engagement_ratio: "influencer.engagement_ratio",
            created_at: "campaignRecommended.createdAt",
            score: "campaignRecommended.score",
        }

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

        let query = this.CampaignRecommended.createQueryBuilder('campaignRecommended')
            .leftJoin('campaignRecommended.influencer', 'influencer')
            .select(['campaignRecommended.id', 'campaignRecommended.createdAt', 'campaignRecommended.score',
                'campaignRecommended.is_admin_recommended',
                'influencer.id', 'influencer.first_name', 'influencer.last_name', 'influencer.email', 'influencer.display_name',
                'influencer.phone_number', 'influencer.image', 'influencer.gender', 'influencer.slug',
                'influencer.total_followers', 'influencer.engagement_ratio'])
            .where('campaignRecommended.campaign_id = :campaign_id', { campaign_id: queryParam.campaign_id })
            .orderBy('campaignRecommended.is_admin_recommended', 'DESC')
            .addOrderBy('campaignRecommended.score', 'DESC')
        if (queryParam.search) {
            query = query.where(
                `(
                    CONCAT(influencer.first_name, ' ', influencer.last_name) ILIKE :search OR
                    influencer.display_name ILIKE :search OR
                    influencer.company_name ILIKE :search OR  influencer.phone_number ILIKE :search OR influencer.email ILIKE :search
                )`, { search: `%${queryParam.search}%` });
        }

        const [results, total] = await query.skip(offset).take(pageSize).getManyAndCount()

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

    async addRecommended(body: any, authUser: any) {
        const campaign = await this.campaignRepository.checkCampaignExists(body.campaign_id, authUser);
        if (campaign) {
            let campaignRecommended = [];
            for (const id of body.influencer_ids) {
                const influencer = await this.Users.findOne({ where: { id: id } });
                if (influencer) {
                    const isRecommended = await this.CampaignRecommended.count({ where: { campaign: { id: campaign.id }, influencer: { id: influencer.id } } });
                    if (isRecommended) {
                        continue;
                    }
                    campaignRecommended.push({
                        campaign: campaign,
                        influencer: influencer,
                        is_admin_recommended: 1
                    });
                }
            }
            await this.CampaignRecommended.save(campaignRecommended);
        }
        return
    }

    async campaignDetail(param: any) {
        const languageCode = '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)
        }

        let campaignInfluencer = await (await this.campaignRepository.campaignInfluencerCommonQuery(languageCode)).where("campaign.slug = :slug", { slug: param.slug }).getOne();

        let brandDetails = await this.campaignRepository.getCampaignBrandDetails(campaign.users.id);

        return { data: { campaign, campaignInfluencer, brandDetails } }
    }

    async paymentList(queryParam: 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 sortByArr = {
            campaign_name: "campaign.campaign_name",
            created_at: "campaignPayment.created_at",
            updated_at: "campaignPayment.updated_at",
            amount: "campaignPayment.amount",
            influencer_payable_amount: "campaignPayment.influencer_payable_amount",
        }

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

        let query = this.CampaignPayment.createQueryBuilder('campaignPayment')
            .leftJoin('campaignPayment.campaign', 'campaign').withDeleted()
            .leftJoin('campaignPayment.influencer', 'influencer')
            .leftJoin("campaignPayment.brand", "brand")
            .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', "campaign.product_value",
                "brand.id", "brand.slug", "brand.first_name", "brand.last_name", "brand.email", "brand.image", "brand.country", "brand.about_me", "brand.is_offline_payment_allowed", "brand.offline_payment_allowed",
                "influencer.id", "influencer.slug", "influencer.first_name", "influencer.last_name", "influencer.email", "influencer.image", "influencer.country", "influencer.about_me", "influencer.display_name", "influencer.country_code", "influencer.phone_number",
                'campaignPayment.id', 'campaignPayment.amount', 'campaignPayment.comment', 'campaignPayment.currency', 'campaignPayment.payment_mode', 'campaignPayment.amount_usd', 'campaignPayment.final_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'
            ]).orderBy(sort_by, sort_type)
            .where("campaignPayment.deleted_at IS NULL")

        if (queryParam.payment_status == 'completed') {
            query.andWhere("campaignPayment.is_admin_paid = true");
            query.andWhere("campaignPayment.payment_mode != 'offline'");
        }
        if (queryParam.payment_status == 'force_offline') {
            query.andWhere("campaignPayment.is_admin_paid = false");
            query.andWhere("campaignPayment.payment_mode = 'force_offline'");
        }
        if (queryParam.payment_status == 'pending') {
            query.andWhere("campaignPayment.is_admin_paid = false");
            query.andWhere("campaignPayment.payment_status != 'failed'");
            query.andWhere("campaignPayment.payment_mode != 'force_offline'");
        }
        if (queryParam.payment_status == 'failed') {
            query.andWhere("campaignPayment.payment_status = 'failed'");
        }
        if (queryParam.payment_status == 'offline') {
            query.andWhere("campaignPayment.payment_mode = 'offline'");
        }

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

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

    async changePaymentStatus(params: PaymentIdParamDto, body: any, authUser: any) {
        const payment = await this.CampaignPayment.findOne({ where: { id: params.payment_id } });
        if (!payment) {
            throw new HttpException(responseMessages.en.campaign.payment_not_exists, HttpStatus.NOT_FOUND)
        }
        payment.payment_status = body.payment_status;
        payment.comment = body.comment;
        await this.CampaignPayment.save(payment);
        return;
    }

    async paymentHistory(queryParam) {
        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 sortByArr = {
            campaign_name: "campaign.campaign_name",
            created_at: "campaignPaymentHistory.created_at",
            updated_at: "campaignPaymentHistory.updated_at",
            amount: "campaignPaymentHistory.amount",
        }

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

        let query = this.CampaignPaymentHistory.createQueryBuilder('campaignPaymentHistory')
            .leftJoin('campaignPaymentHistory.campaign', 'campaign').withDeleted()
            .leftJoin('campaignPaymentHistory.users', 'users')
            .select(['campaignPaymentHistory', 'campaign.id', 'campaign.slug', 'campaign.campaign_name', 'campaign.posting_start_date', 'campaign.posting_end_date',
                'campaign.influencer_type', 'campaign.job_requirement', 'campaign.content_type', "campaign.product_value",
                "users.id", "users.slug", "users.first_name", "users.last_name", "users.email", "users.display_name", "users.image", "users.country", "users.about_me", "users.is_offline_payment_allowed", "users.offline_payment_allowed",
            ]).orderBy(sort_by, sort_type);


        if (queryParam.payment_status != 'all') {
            query.andWhere("campaignPaymentHistory.payment_status = :status", { status: queryParam.payment_status });
        }

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

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

    async export(queryParam, response) {

        let query = this.CampaignPaymentHistory.createQueryBuilder('campaignPaymentHistory').withDeleted()
            .leftJoin('campaignPaymentHistory.campaign', 'campaign')
            .leftJoin('campaignPaymentHistory.users', 'users')
            .select(['campaignPaymentHistory', '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",
            ])
            .where('campaignPaymentHistory.created_at BETWEEN :start_date AND :end_date', { start_date: queryParam.start_date + ' 00:00:00', end_date: queryParam.end_date + ' 23:59:59' })
            .orderBy('campaignPaymentHistory.created_at', 'ASC');

        if (queryParam.payment_status != 'all') {
            query.andWhere("campaignPaymentHistory.payment_status = :status", { status: queryParam.payment_status });
        }
        if (queryParam.campaign_id) {
            query.andWhere("campaign.id = :id", { id: queryParam.campaign_id });
        }
        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 = await query.getMany();
        if (results.length == 0) {
            throw new HttpException(responseMessages.en.file_manager.no_record_found, HttpStatus.BAD_REQUEST);
        }
        const workbook = new ExcelJS.Workbook();
        const worksheet = workbook.addWorksheet('Payment History');

        const headers = ["SR.NO", "CAMPAIGN NAME", "USER NAME", "AMOUNT", "CURRENCY", "USD AMOUNT", "EXCHANGE RATE", "TRANSACTION FEE USD", "FINAL AMOUNT", "PAYMENT STATUS", "TRANSACTION DATE"];
        worksheet.addRow(headers); // Add the headers as the first row
        let cnt = 1;
        for (const payment of results) {
            let row = {
                uuid: cnt++,
                campaign_name: payment.campaign.campaign_name,
                user_name: payment.users.first_name + ' ' + payment.users.last_name,
                amount: payment.amount,
                currency: payment.currency,
                usd_amount: payment.amount_usd,
                exchange_rate: payment.exchange_rate,
                transaction_fee_usd: payment.transaction_fee_usd,
                final_amount_usd: payment.payment_status == PaymentType.CREDITED ? payment.final_amount_usd : payment.amount_usd,
                payment_status: payment.payment_status.toUpperCase(),
                transaction_date: moment(payment.created_at).format('YYYY-MM-DD'),
            }
            worksheet.addRow(Object.values(row));
        }

        // Styling (optional)
        worksheet.getRow(1).font = { bold: true }; // Bold headers
        worksheet.columns.forEach((column) => {
            column.width = 20; // Adjust column width
        });
        const fileName = 'campaign-payment-history-' + 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();
    }


    async postsExport(queryParam, response) {
        const startDate = queryParam.start_date + ' 00:00:00';
        const endDate = queryParam.end_date + ' 23:59:59';

        let query = this.Posts.createQueryBuilder("posts")
            .innerJoin("posts.campaign", "campaign").withDeleted()
            .where("posts.created_at >= :startDate AND posts.created_at <= :endDate", { startDate, endDate })
            .leftJoin('posts.influencer', 'influencer')
            .select(['posts', 'campaign.id', 'campaign.campaign_name', 'influencer.id', 'influencer.first_name', 'influencer.total_followers', 'influencer.last_name', 'influencer.slug', 'influencer.image', 'influencer.display_name']);

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

        const results = await query.getMany();
        if (results.length == 0) {
            throw new HttpException(responseMessages.en.file_manager.no_record_found, HttpStatus.BAD_REQUEST);
        }
        const workbook = new ExcelJS.Workbook();
        const worksheet = workbook.addWorksheet('Campaign Analysis');

        const headers = ["SR.NO", "CAMPAIGN NAME", "INFLUENCER NAME", "POST LINK", "LIKE COUNT", "COMMENT COUNT", "PLATFORM", "VIEW COUNT", "ENGAGEMENTS COUNT", "POST DATE"];
        worksheet.addRow(headers); // Add the headers as the first row
        let cnt = 1;
        for (const post of results) {
            let row = {
                uuid: cnt++,
                campaign_name: post.campaign.campaign_name,
                user_name: post.influencer.first_name + ' ' + post.influencer.last_name + ' (' + post.influencer.display_name + ')',
                post_link: post.post_link,
                like_counts: post.like_counts,
                comment_count: post.comment_count,
                platform: post.platform,
                view_counts: post.view_counts,
                engagement_rate: post.engagement_rate,
                post_date: moment(post.created_at).format('YYYY-MM-DD'),
            }
            worksheet.addRow(Object.values(row));
        }

        // Styling (optional)
        worksheet.getRow(1).font = { bold: true }; // Bold headers
        worksheet.columns.forEach((column) => {
            column.width = 20; // Adjust column width
        });
        const fileName = 'campaign-analysis-' + 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();
    }

    async paidToInfluencer(param, body, auth) {
        let payment = await this.CampaignPayment.findOne({ 'relations': ['influencer', 'campaign'], where: { id: param.payment_id } });
        if (!payment) {
            throw new HttpException(responseMessages.en.campaign.payment_not_exists, HttpStatus.NOT_FOUND)
        }
        if (!payment.influencer) {
            throw new HttpException(responseMessages.en.campaign.influencer_not_exists, HttpStatus.NOT_FOUND)
        }
        console.log(payment);
        const paymentPdf = {
            name: payment.influencer.first_name + ' ' + payment.influencer.last_name + ' (' + payment.influencer.display_name + ')',
            amount: payment.influencer_payable_amount,
            currency: payment.currency,
            date: payment.created_at,
            platform_fees: 0,
            receipt_number: payment.receipt_number + 1,
            total_amount: payment.influencer_payable_amount
        }
        const response = await this.campaignRepository.createInvoiceAndUpload(paymentPdf);
        payment.payment_status = PAYMENT_STATUS.COMPLETED;
        payment.is_admin_paid = true;
        payment.comment = body.comment;
        // payment.invoice_receipt = response.fileName;
        payment.payed_by = auth;
        await this.CampaignPayment.save(payment);

        const campaignPaymentHistory = {
            users: payment.influencer,
            campaign: payment.campaign,
            campaign_payment: payment,
            amount: payment.influencer_payable_amount,
            currency: payment.currency,
            amount_usd: payment.influencer_payable_amount_usd,
            transaction_status: PAYMENT_STATUS.COMPLETED,
            payment_status: PaymentType.DEBITED
        }
        this.CampaignPaymentHistory.save(campaignPaymentHistory);

        const notificationMessage = notificationMessages.en.descriptions.admin_paid_campaign.replace(':userName', `${auth.first_name} ${auth.last_name}`).replace(":campaign_name", payment.campaign.campaign_name);
        const metadata = { campaign_id: payment.campaign.id, status: 'closed', campaignName: payment.campaign.campaign_name, influencer: { id: payment.influencer.id } };
        // this.campaignRepository.notification(payment.influencer, notificationMessage, auth, RedirectType.PAYMENT_LIST, payment.campaign.id, payment.campaign.slug);
        this.campaignService.sendCampaignNotification({
            type: 'admin_paid_campaign',
            receiver: payment.influencer,
            description: notificationMessage,
            sender: auth,
            redirectType: RedirectType.PAYMENT_LIST,
            redirectId: payment.campaign.id,
            redirectSlug: payment.campaign.slug,
            metadata: metadata
        });
        return;
    }
}
