import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import slugify from 'slugify';
import { PlanPermission } from 'src/entities/plan-permission.entity';
import { Interval, Plan } from 'src/entities/plan.entity';
import { MoreThan, Repository } from 'typeorm';
import { StripeServices } from 'src/common/services/payment-gateway/stripe/stripe.service';
import { responseMessages } from 'src/messages/response-messages';
import { Console, time } from 'console';
import { PaymentRepository } from './payment.reporitory';
import { Card } from 'src/entities/card.entity';
import { Subscription, SubscriptionStatus } from 'src/entities/subscription.entity';
const moment = require('moment');

import { permission } from 'process';
import { notificationMessages } from 'src/messages/notifications-messages';
import { RedirectType } from 'src/entities/notifications.entity';
import { SubscriptionHistory } from 'src/entities/subscription-history.entity';

// Entities Files
import * as ExcelJS from 'exceljs';
import e from 'express';
import { start } from 'repl';
import { UserPermissions } from 'src/entities/user-permissions.entity';
import { UserRoles, Users } from 'src/entities/users.entity';
import { AwsPage } from 'twilio/lib/rest/accounts/v1/credential/aws';
import { ReactiveSubscription } from './dto/reactive-subscription';
import { auth } from 'firebase-admin';
@Injectable()
export class PaymentService {
    constructor(
        private paymentRepository: PaymentRepository,
        private stripeService: StripeServices,

        @InjectRepository(Plan)
        private readonly Plan: Repository<Plan>,
        @InjectRepository(Users)
        private readonly Users: Repository<Users>,
        @InjectRepository(Card)
        private readonly Card: Repository<Card>,
        @InjectRepository(Subscription)
        private readonly Subscription: Repository<Subscription>,

        @InjectRepository(SubscriptionHistory)
        private readonly SubscriptionHistory: Repository<SubscriptionHistory>,

        @InjectRepository(UserPermissions)
        private UserPermissions: Repository<UserPermissions>,
    ) { }


    async saveCard(cardData: any, authUser: any) {
        const { source_id } = cardData;
        const metadata = { user: authUser };
        const { card_id, response, meta } = await this.stripeService.saveCard(source_id, metadata);
        if (card_id) {
            const cardObj = {
                card_id: card_id,
                card_holder_name: authUser.first_name,
                last4: response.card.last4,
                brand: response.card.brand,
                exp_month: response.card.exp_month,
                exp_year: response.card.exp_year,
                is_default: true,
                card_response: response,
                users: authUser,
                user_id: authUser.id
            }
            this.Card.save(cardObj);
            return;
        }
        throw new HttpException(meta.message, HttpStatus.BAD_REQUEST);
    }

    async getCard(authUser: any) {
        const cards = await this.Card.find({ where: { users: { id: authUser.id } } });
        return { data: cards };
    }

    async subscriptionList(authUser) {
        const subscription = await this.Subscription.createQueryBuilder('subscription')
            .leftJoinAndSelect('subscription.plan', 'plan')
            .leftJoinAndSelect('subscription.subscription_history', 'subscription_history')
            .where('subscription.usersId = :userId', { userId: authUser.id })
            .orderBy('subscription.created_at', 'DESC')
            .getMany();
        return { data: subscription };
    }

    async subscriptionHistory(queryParam, authUser) {
        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 = {
            plan_name: "plan.name",
            start_date: "history.start_date",
            created_at: "history.created_at",
            end_date: "history.end_date",
            user_name: "user.first_name",
            amount: "plan.amount",
        }

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

        let query = this.SubscriptionHistory.createQueryBuilder('history').withDeleted()
            .leftJoin('history.subsciption', 'subscription')
            .leftJoin('history.plan', 'plan')
            .leftJoin('subscription.users', 'user')
            .select(['history.id', 'history.interval', 'history.created_at', 'history.start_date', 'history.end_date', 'history.status', 'subscription.id', 'history.amount_usd', 'history.final_amount',
                'plan.id', 'plan.name', 'plan.slug', 'plan.description', 'plan.plan_type', 'plan.currency', 'plan.currency', 'subscription.amount', 'subscription.interval',
                'user.id', 'user.slug', 'user.first_name', 'user.last_name', 'user.email', 'user.image', 'user.country', 'user.about_me', 'user.is_offline_payment_allowed', 'user.offline_payment_allowed'])
            .orderBy(sort_by, sort_type);

        if (queryParam.search) {
            query = query.where("(plan.name LIKE :search OR user.first_name LIKE :search OR user.last_name LIKE :search)", { search: `%${queryParam.search}%` })
        }
        if (queryParam.status != 'all') {
            query = query.andWhere("history.status = :status", { status: queryParam.status })
        }
        console.log(authUser.role)
        if (authUser.role == UserRoles.BRAND) {
            query = query.andWhere("user.id = :brand_id", { brand_id: authUser.id });
        }
        const [results, total] = await query.skip(offset).take(pageSize).getManyAndCount();
        return {
            data: results,
            total: total,
            hasMoreResults: (total > nextOffset) ? 1 : 0
        }
    }

    async subscriptionHistoryExport(queryParam: any, response: any) {
        let query = this.SubscriptionHistory.createQueryBuilder('history').withDeleted()
            .innerJoin('history.subsciption', 'subscription')
            .leftJoin('subscription.plan', 'plan')
            .leftJoin('subscription.users', 'users')
            .select(['history.id', 'history.start_date', 'history.end_date', 'history.status', 'history.created_at',
                'history.transaction_fee_usd', 'history.final_amount',
                'subscription.id', 'subscription.amount', 'subscription.interval',
                'plan.id', 'plan.name', 'plan.slug', 'plan.description', 'plan.plan_type', 'plan.currency', 'plan.currency',
                '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('DATE(history.created_at) BETWEEN :start_date AND :end_date', { start_date: queryParam.start_date + ' 00:00:00', end_date: queryParam.end_date + ' 23:59:59' });

        if (queryParam.status != 'all') {
            query = query.andWhere("history.status = :status", { status: queryParam.status })
        }
        if (queryParam.plan_id) {
            query = query.andWhere("plan.id = :plan_id", { plan_id: queryParam.plan_id })
        }
        if (queryParam.brand_id) {
            query = query.andWhere("users.id = :brand_id", { brand_id: queryParam.brand_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('Subscription History');

        const headers = ["SR.NO", "USER NAME", "EMAIL", "PLAN NAME", "AMOUNT", "CURRENCY", "TRANSACTION FEE USD", "FINAL AMOUNT", "INTERVAL", "STATUS", "START DATE", "END DATE"];
        worksheet.addRow(headers); // Add the headers as the first row
        let cnt = 1;
        for (const payment of results) {
            let row = {
                uuid: cnt++,
                user_name: payment.subsciption.users.first_name + ' ' + payment.subsciption.users.last_name,
                email: payment.subsciption.users.email,
                plan_name: payment.subsciption.plan.name,
                amount: payment.subsciption.amount,
                currency: payment.subsciption.plan.currency,
                transaction_fee_usd: payment.transaction_fee_usd,
                final_amount: payment.final_amount,
                interval: payment.subsciption.interval,
                status: payment.status.toUpperCase(),
                start_date: moment(payment.start_date).format('YYYY-MM-DD'),
                end_date: moment(payment.end_date).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 = 'subscription-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 getFreePlan() {
        return await this.Plan.findOne({ where: { plan_type: "free" } });
    }

    async subscription(body: any, authUser: any) {

        try {
            const { plan_id, card_id, interval, user_id } = body;
            authUser = authUser.role == 'admin' ? await this.Users.findOne({ where: { id: user_id } }) : authUser;

            if (!authUser) {
                throw new HttpException(responseMessages.en.user.user_not_exists, HttpStatus.NOT_FOUND);
            }
            const planExists = await this.Plan.findOne({ relations: ['permission'], where: { id: plan_id } });
            if (!planExists) {
                throw new HttpException(responseMessages.en.plan.plan_not_found, HttpStatus.NOT_FOUND);
            }

            const intervalStr = interval === Interval.MONTH ? 'months' : 'years'
            // CANCEL OLD SUBSCRIPTION FROM STRIPE
            let subscriptionStartDate = moment().format('YYYY-MM-DD HH:mm:ss')
            let subscriptionEndDate = moment().add(1, intervalStr).format('YYYY-MM-DD HH:mm:ss')
            let subscriptionStatus = card_id ? SubscriptionStatus.PENDING : SubscriptionStatus.ACTIVE

            const isAlreadySubscribed = await this.Subscription.count({ where: { users: { id: authUser.id } } });
            if (isAlreadySubscribed == 0) {
                await this.Users.update({ id: authUser.id }, { subscription_start_date: subscriptionStartDate, subscription_end_date: subscriptionEndDate });
            }
            const activeSubscription = await this.Subscription.findOne({ relations: ['plan'], where: { users: { id: authUser.id }, is_active: true } });
            let isPermissionChanged = true;
            if (activeSubscription) {
                console.log("activeSubscription", activeSubscription);
                if (activeSubscription.interval == interval && activeSubscription.plan.id == plan_id) {
                    throw new HttpException(responseMessages.en.subscription.subscription_already_exists, HttpStatus.BAD_REQUEST);
                }
                await this.Subscription.update(activeSubscription.id, { status: SubscriptionStatus.CANCELED, is_active: false });
            }

            const subscription = {
                users: authUser,
                plan: planExists,
                status: subscriptionStatus,
                start_date: subscriptionStartDate, // Current date
                end_date: subscriptionEndDate, // 1 month or 1 year later
                amount: interval === Interval.MONTH ? planExists.month_amount : planExists.year_amount,
                interval: interval,
                payment_mode: card_id ? 'online' : 'offline',
                is_active: true,
            }
            await this.Users.update({ id: authUser.id }, { subscription_end_date: subscriptionEndDate });
            console.log("subscription", subscription);
            const saveSubscription = await this.paymentRepository.createSubscription(subscription, isPermissionChanged);

            // const successMessage = notificationMessages.en.descriptions.brand_subscription.replace(':userName', `${authUser.first_name} ${authUser.last_name}`)
            //     .replace(':plan', planExists.name + ' (' + interval + ')');
            // this.paymentRepository.brandNotification(authUser, successMessage, null, RedirectType.BRAND_SUBSCRIPTION, authUser.id, authUser.slug);
            return;
        } catch (error) {
            console.log(error)
            throw new HttpException(error.message, HttpStatus.BAD_REQUEST);
        }
    }

    /**
     * Creates a subscription for a user.
     * 
     * This function checks if the specified plan exists and if the user already has an active subscription.
     * If an active subscription exists, it is canceled. Then, a new subscription is created with the given
     * plan, card, and interval information. The subscription is also created in Stripe, and notifications
     * are sent out accordingly.
     * 
     * @param body - Object containing subscription details such as plan_id, card_id, and interval.
     * @param authUser - The authenticated user who is requesting the subscription.
     * 
     * @throws {HttpException} If the plan does not exist or another error occurs during subscription creation.
     */

    async subscriptionBk(body: any, authUser: any) {

        try {
            const { plan_id, card_id, interval, user_id } = body;
            authUser = authUser.role == 'admin' ? await this.Users.findOne({ where: { id: user_id } }) : authUser;

            if (!authUser) {
                throw new HttpException(responseMessages.en.user.user_not_exists, HttpStatus.NOT_FOUND);
            }
            const planExists = await this.Plan.findOne({ relations: ['permission'], where: { id: plan_id } });
            if (!planExists) {
                throw new HttpException(responseMessages.en.plan.plan_not_found, HttpStatus.NOT_FOUND);
            }

            const intervalStr = interval === Interval.MONTH ? 'months' : 'years'
            // CANCEL OLD SUBSCRIPTION FROM STRIPE
            let subscriptionStartDate = moment().format('YYYY-MM-DD HH:mm:ss')
            let subscriptionEndDate = moment().add(1, intervalStr).format('YYYY-MM-DD HH:mm:ss')
            let subscriptionStatus = card_id ? SubscriptionStatus.PENDING : SubscriptionStatus.ACTIVE

            const isAlreadySubscribed = await this.Subscription.count({ where: { users: { id: authUser.id } } });
            if (isAlreadySubscribed == 0) {
                await this.Users.update({ id: authUser.id }, { subscription_start_date: subscriptionStartDate, subscription_end_date: subscriptionEndDate });
            }
            const activeSubscription = await this.Subscription.findOne({ relations: ['plan'], where: { users: { id: authUser.id }, is_active: true } });
            let isPermissionChanged = true;
            if (activeSubscription) {
                console.log("activeSubscription", activeSubscription);
                if (activeSubscription.interval == interval && activeSubscription.plan.id == plan_id) {
                    throw new HttpException(responseMessages.en.subscription.subscription_already_exists, HttpStatus.BAD_REQUEST);
                }
                if (activeSubscription.payment_mode == 'online' && activeSubscription.status == SubscriptionStatus.ACTIVE) {
                    const param = { subscription_id: activeSubscription.id }
                    await this.subscriptionCancel(param, authUser);
                }
                await this.Subscription.update(activeSubscription.id, { status: SubscriptionStatus.CANCELED });
                // IF SAME PLAN PURCHASED OR PLAN PRIORITY IS LOWER THAN EXISTING PLAN 1 > 2
                if (activeSubscription.plan.id == plan_id || activeSubscription.plan.priority > planExists.priority) {
                    subscriptionStartDate = moment(activeSubscription.end_date).add(1, 'minutes').format('YYYY-MM-DD HH:mm:ss');
                    subscriptionEndDate = moment(activeSubscription.end_date).add(1, intervalStr).format('YYYY-MM-DD HH:mm:ss');
                    isPermissionChanged = false; // No permission change if same plan or lower priority
                    subscriptionStatus = SubscriptionStatus.UPCOMING; // Keep status active if same plan or lower priority
                } else {
                    await this.Subscription.update(activeSubscription.id, { is_active: false });
                }
                //    await this.Users.update({ id: authUser.id }, { subscription_end_date: subscriptionEndDate });
            }
            const upcomingSubscription = await this.Subscription.findOne({ where: { users: { id: authUser.id }, status: SubscriptionStatus.UPCOMING }, order: { created_at: 'DESC' } });
            if (upcomingSubscription) {
                // IF SAME PLAN PURCHASED OR PLAN PRIORITY IS LOWER THAN EXISTING PLAN
                if (upcomingSubscription.payment_mode == 'online') {
                    await this.stripeService.cancelSubscriptionSchedule(upcomingSubscription.schedule_subscription_id);
                }
                subscriptionStartDate = moment(upcomingSubscription.start_date).format('YYYY-MM-DD HH:mm:ss');
                subscriptionEndDate = moment(upcomingSubscription.start_date).add(1, intervalStr).format('YYYY-MM-DD HH:mm:ss');
                isPermissionChanged = false; // No permission change if same plan or lower priority
                subscriptionStatus = SubscriptionStatus.UPCOMING;
                //   await this.Users.update({ id: authUser.id }, { subscription_end_date: subscriptionEndDate });
                await this.Subscription.softDelete({ id: upcomingSubscription.id });
            }
            let trailEndDate = null;
            if (!authUser.is_trial_used && planExists.trial_days > 0) {
                trailEndDate = moment().add(planExists.trial_days, 'days').format('YYYY-MM-DD HH:mm:ss');
                subscriptionStartDate = moment(trailEndDate).add(1, 'minutes').format('YYYY-MM-DD HH:mm:ss');
                subscriptionEndDate = moment(trailEndDate).add(1, intervalStr).format('YYYY-MM-DD HH:mm:ss');
            }
            const subscription = {
                users: authUser,
                plan: planExists,
                status: subscriptionStatus,
                start_date: subscriptionStartDate, // Current date
                end_date: subscriptionEndDate, // 1 month or 1 year later
                amount: interval === Interval.MONTH ? planExists.month_amount : planExists.year_amount,
                interval: interval,
                payment_mode: card_id ? 'online' : 'offline',
                is_active: subscriptionStatus == SubscriptionStatus.UPCOMING ? false : true,
                is_trial_active: trailEndDate ? true : false,
                trial_end_date: trailEndDate
            }
            await this.Users.update({ id: authUser.id }, { subscription_end_date: subscriptionEndDate, is_trial_used: trailEndDate ? true : authUser.is_trial_used });
            console.log("subscription", subscription);
            const saveSubscription = await this.paymentRepository.createSubscription(subscription, isPermissionChanged);
            if (card_id && saveSubscription) {
                const price_id = interval == Interval.MONTH ? planExists.month_price_id : planExists.year_price_id
                const metadata = {
                    plan_id, card_id, price_id, trailEndDate,
                    subscription_id: saveSubscription.id, user_id: authUser.id,
                };
                let stripeResponse = null;
                if (subscriptionStatus == SubscriptionStatus.UPCOMING) {
                    stripeResponse = await this.stripeService.subscriptionSchedules(authUser.stripe_customer_id, price_id, card_id, subscriptionStartDate, metadata);
                } else {
                    stripeResponse = await this.stripeService.createSubscription(authUser.stripe_customer_id, price_id, card_id, metadata);
                }

                if (stripeResponse.status == false) {
                    return stripeResponse.response;
                }
                // CREATE NOTIFICATION MESSAGE
                const adminMessage = notificationMessages.en.descriptions.plan_subscription.replace(':userName', `${authUser.first_name} ${authUser.last_name}`);
                this.paymentRepository.notification(adminMessage, authUser, RedirectType.BRAND_LIST, authUser.id, authUser.slug);


                let count = 0;
                while (count < 3) {
                    await this.sleep(2000);
                    const isActive = await this.Subscription.count({
                        where: { id: saveSubscription.id, status: SubscriptionStatus.ACTIVE }
                    });

                    if (isActive) break; // Exit if subscription is active
                    count++;
                }
                // if (count == 3) {
                //     throw new HttpException(responseMessages.en.subscription.subscription_not_done, HttpStatus.BAD_REQUEST);
                // }
            }
            // const successMessage = notificationMessages.en.descriptions.brand_subscription.replace(':userName', `${authUser.first_name} ${authUser.last_name}`)
            //     .replace(':plan', planExists.name + ' (' + interval + ')');
            // this.paymentRepository.brandNotification(authUser, successMessage, null, RedirectType.BRAND_SUBSCRIPTION, authUser.id, authUser.slug);
            return;
        } catch (error) {
            console.log(error)
            throw new HttpException(error.message, HttpStatus.BAD_REQUEST);
        }
    }
    async sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }


    async subscriptionCancel(param: any, authUser: any) {
        const { subscription_id } = param;
        const subscription = await this.Subscription.findOne({ where: { id: subscription_id } });
        if (!subscription) {
            throw new HttpException(responseMessages.en.subscription.subscription_not_exists, HttpStatus.NOT_FOUND);
        }
        if (subscription.payment_mode == 'online') {
            await this.stripeService.cancelSubscription(subscription.stripe_subscription_id);
        }
        subscription['status'] = SubscriptionStatus.CANCELED;
        this.Subscription.save(subscription);
        return;
    }

    async subscriptionCancelSchedule(param: any, authUser: any) {
        const { subscription_id } = param;
        const subscription = await this.Subscription.findOne({ relations: ['users'], where: { id: subscription_id } });
        if (!subscription) {
            throw new HttpException(responseMessages.en.subscription.subscription_not_exists, HttpStatus.NOT_FOUND);
        }
        if (subscription.status != SubscriptionStatus.UPCOMING) {
            throw new HttpException(responseMessages.en.subscription.subscription_not_upcoming, HttpStatus.BAD_REQUEST);
        }
        if (subscription.payment_mode == 'online') {
            const { response } = await this.stripeService.cancelSubscriptionSchedule(subscription.schedule_subscription_id);
            if (!response) {
                throw new HttpException(responseMessages.en.subscription.subscription_cancel_error, HttpStatus.BAD_GATEWAY);
            }
        }
        this.Users.update({ id: subscription.users.id }, { subscription_end_date: subscription.start_date });
        await this.Subscription.softDelete({ id: subscription_id });
    }

    async deleteCard(param: any, authUser: any) {
        const { card_id } = param;
        const cardCount = await this.Card.count({ where: { users: { id: authUser.id } } });
        if (cardCount == 1) {
            throw new HttpException(responseMessages.en.payment.at_least_one_card_required, HttpStatus.BAD_REQUEST);
        }
        const card = await this.Card.findOne({ where: { id: card_id } });
        if (!card) {
            throw new HttpException(responseMessages.en.payment.card_not_exists, HttpStatus.NOT_FOUND);
        }
        const { response } = await this.stripeService.deletePaymentMethod(card.card_id);
        console.log(response)
        if (response) {
            await this.Card.softDelete({ id: card_id });
            return;
        }
        throw new HttpException(responseMessages.en.payment.delete_card_error, HttpStatus.BAD_GATEWAY);
    }

    async getUserPermissions(authUser: any) {
        const data = await this.UserPermissions.find({ where: { users: { id: authUser.id } } });
        let permissions: any = data.reduce((acc, item) => {
            acc[item.type] = !!item.remaining;
            return acc;
        }, {});
        const activeSubscription = await this.Subscription.findOne({ relations: ['plan'], where: { users: { id: authUser.id }, is_active: true } });
        const brandPlanPriority = activeSubscription ? activeSubscription.plan.priority : 0;

        permissions.upgrade_plan = await this.Plan.exists({ where: { priority: MoreThan(brandPlanPriority) }, order: { priority: 'ASC' } });

        return { permissions };
    }

    async clientSecret(authUser: any) {
        const { client_secret } = await this.stripeService.clientSecret(authUser);
        return { data: client_secret };
    }

    async activatedSubscription(authUser: any) {
        const activatedSubscription = await this.Subscription.findOne({ relations: ['plan'], where: { users: { id: authUser.id }, is_active: true } });
        const upcomingSubscription = await this.Subscription.findOne({ relations: ['plan'], where: { users: { id: authUser.id }, status: SubscriptionStatus.UPCOMING } });
        return { activatedSubscription, upcomingSubscription };
    }

    async billingPortal(authUser: any) {
        const url = await this.stripeService.billingPortal(authUser.stripe_customer_id);
        return { url }
    }

    async getInvoiceLink(param: { subscription_history_id: string }, authUser: any) {
        const { subscription_history_id } = param;

        const history = await this.SubscriptionHistory.findOne({
            relations: ['subsciption', 'subsciption.users'],
            where: { id: subscription_history_id },
        });

        if (!history) {
            throw new HttpException(responseMessages.en.subscription.subscription_history_not_exists, HttpStatus.NOT_FOUND);
        }

        if (authUser.role === UserRoles.BRAND && history.subsciption?.users?.id !== authUser.id) {
            throw new HttpException(responseMessages.en.subscription.subscription_history_not_exists, HttpStatus.NOT_FOUND);
        }

        const subscription = history.subsciption;
        if (!subscription) {
            throw new HttpException(responseMessages.en.subscription.subscription_not_exists, HttpStatus.NOT_FOUND);
        }

        let invoiceId: string | null = null;

        if (history.payment_response?.id) {
            invoiceId = history.payment_response.id;
        }

        if (!invoiceId) {
            const paymentResponse = subscription.payment_response;
            if (paymentResponse?.latest_invoice) {
                invoiceId = typeof paymentResponse.latest_invoice === 'string'
                    ? paymentResponse.latest_invoice
                    : paymentResponse.latest_invoice?.id;
            }
        }

        if (!invoiceId) {
            throw new HttpException(responseMessages.en.subscription.invoice_not_found, HttpStatus.NOT_FOUND);
        }

        const url = await this.stripeService.getInvoiceLink(invoiceId);
        return { url };
    }

    async subscriptionLink(body: any, authUser: any) {
        const { plan_id, interval } = body;

        const planExists = await this.Plan.findOne({ where: { id: plan_id } });
        if (!planExists) {
            throw new HttpException(responseMessages.en.plan.plan_not_found, HttpStatus.NOT_FOUND);
        }
        const priceId = interval === Interval.MONTH ? planExists.month_price_id : planExists.year_price_id;
        const customerId = authUser.stripe_customer_id ? authUser.stripe_customer_id : await this.stripeService.createCustomer(authUser);
        let metadata: any = {
            plan_id,
            user_id: authUser.id,
            interval,
        };
        if (authUser.is_trial_used == false && planExists.trial_days > 0) {
            metadata.trial_period_days = planExists.trial_days;
        }
        let link: any = null;

        const subscription = await this.Subscription.findOne({ where: { users: { id: authUser.id }, is_active: true } });
        if (!subscription) {
            throw new HttpException(responseMessages.en.subscription.subscription_not_exists, HttpStatus.NOT_FOUND);
        }
        const itemId = subscription?.payment_response?.items?.data[0]?.id || null;
        if (!itemId) {
            link = await this.stripeService.createCheckoutSession(customerId, priceId, metadata);
        } else {
            link = await this.stripeService.subscriptionUpdateConfirm(customerId, subscription.stripe_subscription_id, itemId, priceId);
            if (!link) {
                link = await this.stripeService.createCheckoutSession(customerId, priceId, metadata);
            }
        }

        return { link };
    }

    async reactivateSubscription(param: ReactiveSubscription, authUser: any) {
        const { subscription_id } = param;
        const subscription = await this.Subscription.findOne({ where: { id: subscription_id } });
        if (!subscription) {
            throw new HttpException(responseMessages.en.subscription.subscription_not_exists, HttpStatus.NOT_FOUND);
        }
        if (subscription.payment_mode == 'online') {
            const { status, response, message } = await this.stripeService.reactivateSubscription(subscription.stripe_subscription_id, subscription.end_date, subscription_id);
            if (status) {
                const upcomingSubscription = await this.Subscription.findOne({ where: { users: { id: authUser.id }, status: SubscriptionStatus.UPCOMING }, order: { created_at: 'DESC' } });
                if (upcomingSubscription) {
                    // CANCEL UPCOMING SUBSCRIPTION FROM STRIPE
                    if (upcomingSubscription.payment_mode == 'online') {
                        await this.stripeService.cancelSubscriptionSchedule(upcomingSubscription.schedule_subscription_id);
                    }
                    this.Users.update({ id: authUser.id }, { subscription_end_date: subscription.end_date });
                    await this.Subscription.softDelete({ id: upcomingSubscription.id });
                }
            } else {
                throw new HttpException(message, HttpStatus.BAD_REQUEST);
            }
        }
        subscription['status'] = SubscriptionStatus.ACTIVE;
        await this.Subscription.save(subscription);
    }
}
