import { Injectable, HttpException, HttpStatus, OnModuleInit } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import Stripe from 'stripe';
import { PaymentGateway } from '../payment-gateway.interface';
import { Interval, Plan } from 'src/entities/plan.entity';
import { Repository } from 'typeorm';
import { PlanPermission } from 'src/entities/plan-permission.entity';
import { Users } from 'src/entities/users.entity';
import { response } from 'express';
import { Subscription, SubscriptionStatus } from 'src/entities/subscription.entity';
import { SubscriptionHistory, SubscriptionHistoryStatus } from 'src/entities/subscription-history.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 { EncryptionService } from '../../encryption/encryption.service';
import { SendMailService } from 'src/common/config/send-mail.service';
import { emailMessages } from 'src/messages/email-messages';
import { url } from 'inspector';
const moment = require('moment');
import * as ejs from 'ejs';
import * as puppeteer from 'puppeteer';
import * as fs from 'fs';
import * as path from 'path';
import { PaymentRepository } from 'src/modules/payment/payment.reporitory';
import { notificationMessages } from 'src/messages/notifications-messages';
import { RedirectType } from 'src/entities/notifications.entity';
import { json } from 'body-parser';
import { Card } from 'src/entities/card.entity';
import { runWithSimpleLock } from '../../redis/runWithSimpleLock';
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
@Injectable()
export class StripeServices implements OnModuleInit, PaymentGateway {

    private stripe: Stripe;
    private s3: S3Client;
    public s3Config = {};
    constructor(
        private encryptionService: EncryptionService,
        private sendMailService: SendMailService,
        private paymentRepository: PaymentRepository,
        @InjectRepository(Users)
        private Users: Repository<Users>,
        @InjectRepository(Plan)
        private Plan: Repository<Plan>,
        @InjectRepository(Subscription)
        private Subscription: Repository<Subscription>,
        @InjectRepository(SubscriptionHistory)
        private SubscriptionHistory: Repository<SubscriptionHistory>,

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

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

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

    ) {
        // Replace with your Stripe secret key
    }

    async onModuleInit() {
        const STRIPE_SECRETE_KEY = await this.encryptionService.decrypt(process.env.STRIPE_SECRETE_KEY);
        this.stripe = new Stripe(STRIPE_SECRETE_KEY);

        const AWS_ACCESS_KEY_ID = await this.encryptionService.decrypt(process.env.AWS_ACCESS_KEY_ID);
        const AWS_SECRET_ACCESS_KEY = await this.encryptionService.decrypt(process.env.AWS_SECRET_ACCESS_KEY);
        this.s3 = new S3Client({
            region: process.env.AWS_DEFAULT_REGION,
            credentials: {
                accessKeyId: AWS_ACCESS_KEY_ID,
                secretAccessKey: AWS_SECRET_ACCESS_KEY,
            },
        });
        this.s3Config = {
            accessKeyId: AWS_ACCESS_KEY_ID,
            secretAccessKey: AWS_SECRET_ACCESS_KEY,
            region: process.env.AWS_DEFAULT_REGION,
        }
    }


    /**
     * Create a product in Stripe
     */
    async createProduct(name: string, description: string) {
        const product = await this.stripe.products.create({
            name,
            description,
        });
        return product;
    }


    async updateProduct(productId: string, name: string, description: string) {
        const product = await this.stripe.products.update(productId, {
            name,
            description,
        });
        return product;
    }

    async archiveProduct(productId: string, status) {
        const product = await this.stripe.products.update(productId, {
            active: status,
        });
        return product;
    }

    /**
    * Create a price for the product in Stripe
    */
    async createPrice(productId: string, amount: number, currency: string, interval: string) {
        let request: any = {
            unit_amount: Math.round(amount * 100),
            currency,
            recurring: {
                interval: interval,
            },
            product: productId,
        };
        return await this.stripe.prices.create(request);
    }


    async saveCard(source_id: string, metadata?: Record<string, any>) {
        try {
            const user = metadata.user;
            let customerId = user.stripe_customer_id;
            if (!customerId) {
                const customer = await this.stripe.customers.create({
                    email: user.email, // Add customer email (optional)
                    name: `${user.first_name} ${user.last_name}`
                });

                customerId = customer.id;
                await this.Users.update({ id: user.id }, { stripe_customer_id: customerId });
            }

            const customerSource = await this.stripe.paymentMethods.attach(source_id, {
                customer: customerId,
            });

            if (customerSource.type !== 'card') {
                throw new Error(`The source created is not a card. Object type: ${customerSource.object}`);
            }

            return { card_id: customerSource.id, response: customerSource, meta: null };
        } catch (error) {
            return { card_id: null, response: null, meta: { message: error.message } };
            //throw new HttpException(responseMessages.en.payment.card_error, HttpStatus.BAD_REQUEST); 
        }

    }

    async createSubscription(customerId: string, priceId: string, payment_method_id: string, metadata?: Record<string, any>) {
        try {

            const subscriptionPayload: any = {
                customer: customerId,
                items: [{ price: priceId }],
                default_payment_method: payment_method_id,
                metadata: metadata,
                expand: ['latest_invoice.payment_intent'],
            };

            // Optional: If metadata.trailEndDate is provided, convert it to a UNIX timestamp
            if (metadata.trial_period_days) {
                subscriptionPayload.trial_period_days = metadata.trial_period_days;
            }
            const response = await this.stripe.subscriptions.create(subscriptionPayload);
            if (typeof response.latest_invoice !== 'string') {
                const invoice = response.latest_invoice as Stripe.Invoice;
                console.log("invoice", invoice);
                const paymentIntent = invoice.payment_intent as Stripe.PaymentIntent;
                console.log("paymentIntent", paymentIntent);
                if (paymentIntent.status === 'requires_action' || paymentIntent.status === 'requires_confirmation') {
                    // Tell client to complete 3D Secure or other authentication
                    const action = {
                        subscriptionId: response.id,
                        clientSecret: paymentIntent.client_secret,
                        requiresAction: true,
                    }
                    return { response: action, status: false }
                }
            }
            return { response: response, status: true };
        } catch (error) {
            console.log(error.message)
            return { response: null, status: false }
            //  throw new HttpException(error.message, HttpStatus.BAD_REQUEST); 
        }
    }
    async createCustomer(user) {
        const customer = await this.stripe.customers.create({
            email: user.email, // Add customer email (optional)
            name: `${user.first_name} ${user.last_name}`
        });
        await this.Users.update({ id: user.id }, { stripe_customer_id: customer.id });
        return customer.id;
    }


    async createCheckoutSession(customerId: string, priceId: string, metadata?: Record<string, any>) {
        try {
            const checkOut: any = {
                payment_method_types: ['card'],
                customer: customerId,
                line_items: [
                    {
                        price: priceId,  // Your pre-existing price ID here
                        quantity: 1,
                    },
                ],
                mode: 'subscription',
                allow_promotion_codes: true,
                success_url: process.env.FRONTEND_URL + 'brand/subscription?session_id={CHECKOUT_SESSION_ID}',
                cancel_url: process.env.FRONTEND_URL + 'brand/subscription',
            };

            // Optional: If metadata.trailEndDate is provided, convert it to a UNIX timestamp
            if (metadata.trial_period_days) {
                checkOut.subscription_data = {
                    trial_period_days: metadata.trial_period_days,
                };
            }
            const session = await this.stripe.checkout.sessions.create(checkOut);
            return session.url
        } catch (error) {
            console.log(error)
            return ""
            //  throw new HttpException(error.message, HttpStatus.BAD_REQUEST); 
        }
    }

    async subscriptionUpdateConfirm(customerId: string, subscriptionId: string, subscriptionItemId: string, priceId: string) {
        try {
            const session = await this.stripe.billingPortal.sessions.create({
                customer: customerId,
                return_url: process.env.FRONTEND_URL + 'brand/subscription',
                flow_data: {
                    type: 'subscription_update_confirm',
                    subscription_update_confirm: {
                        subscription: subscriptionId,
                        items: [
                            {
                                id: subscriptionItemId,
                                quantity: 1,
                                price: priceId, // Your pre-existing price ID here
                            },
                        ],
                    },
                },
            });
            return session.url
        } catch (error) {
            console.log(error)
            return ""
        }
    }

    async subscriptionSchedules(customerId: string, priceId: string, payment_method_id: string, start_date: string, metadata?: Record<string, any>) {
        try {
            const futureTimestamp = Math.floor(new Date(start_date).getTime() / 1000); // Convert to Unix timestamp in seconds
            const response = await this.stripe.subscriptionSchedules.create({
                customer: customerId,
                start_date: futureTimestamp,
                default_settings: {
                    default_payment_method: payment_method_id, // ✅ Already 3DS-confirmed
                    collection_method: 'charge_automatically', // Automatically charge the customer
                },
                phases: [{
                    items: [{ price: priceId }],
                }],
                end_behavior: 'release',
                metadata: metadata
            });
            if (response) {
                await this.Subscription.update({ id: metadata.subscription_id }, { schedule_subscription_id: response.id });
            }

            return { response: response, status: true };
        } catch (error) {
            console.log(error.message)
            return { response: null, status: false }
            // throw new HttpException(error.message, HttpStatus.BAD_REQUEST); 
        }
    }

    async cancelSubscriptionSchedule(scheduleId: string) {
        try {
            await this.stripe.subscriptionSchedules.cancel(scheduleId);
            return { response: true };
        } catch (error) {
            console.log(error.message)
            return { response: false };;
        }
    }


    async cancelSubscription(subscriptionId: string) {
        try {
            //  await this.stripe.subscriptions.update(subscriptionId, { cancel_at_period_end: true });
            await this.stripe.subscriptions.cancel(subscriptionId);
            return { response: true };
        } catch (error) {
            console.log(error.message)
            return { response: false };;
        }
    }

    async deletePaymentMethod(paymentMethodId: string) {
        try {
            await this.stripe.paymentMethods.detach(paymentMethodId);
            return { response: true };
        } catch (error) {
            console.log(error.message)
            return { response: false };
        }
    }


    async webhook(request) {

        const sig = request.headers['stripe-signature'];

        const endpointSecret = await this.encryptionService.decrypt(process.env.STRIPE_WEBHOOK_KEY);

        let event: Stripe.Event;
        try {
            console.log("webhook", endpointSecret);
            // Verify the event using the signature
            event = this.stripe.webhooks.constructEvent(request.body, sig, endpointSecret);
        } catch (err) {
            console.error(`⚠️ Webhook signature verification failed.`, err.message);
            return;
        }
        let status = false;
        await this.sleep(2000);
        // Handle the event
        await runWithSimpleLock(`stripe:event:${event.id}`, 60000, async () => {
            switch (event.type) {
                case 'customer.subscription.created':
                    const subscriptionCreated = event.data.object as Stripe.Subscription;
                    this.saveSubscription(subscriptionCreated, 'customer.subscription.created');
                    console.log('Subscription created:', JSON.stringify(subscriptionCreated));
                    status = true;
                    break;

                case 'customer.subscription.updated':
                    const subscriptionUpdated = event.data.object as Stripe.Subscription;
                    this.saveSubscription(subscriptionUpdated, 'customer.subscription.updated');
                    console.log('Subscription updated:', JSON.stringify(subscriptionUpdated));
                    break;

                case 'customer.subscription.deleted':
                    const subscriptionDeleted = event.data.object as Stripe.Subscription;
                    console.log('Subscription DELETED:', subscriptionDeleted);
                    this.Subscription.update({ stripe_subscription_id: subscriptionDeleted.id }, { status: SubscriptionStatus.CANCELED });
                    this.SubscriptionHistory.update({ stripe_subscription_id: subscriptionDeleted.id }, { status: SubscriptionHistoryStatus.CANCELED })
                    status = true;
                    break;

                case 'invoice.payment_succeeded': {
                    await this.sleep(3000);
                    const invoice = event.data.object as Stripe.Invoice;
                    this.paymentSucceeded(invoice);
                    status = true;
                    break;
                }
                case 'invoice.payment_failed': {
                    await this.sleep(2000);
                    const invoice = event.data.object as Stripe.Invoice;
                    this.paymentFailed(invoice);
                    status = true;
                    // Handle failed payment (e.g., notify the customer or retry logic)
                    break;
                }
                case 'charge.succeeded':
                    await this.sleep(2000);
                    const charge = event.data.object as Stripe.Charge;
                    console.log(`💰 Charge successful: ${charge.id}, Amount: ${charge.amount / 100} ${charge.currency.toUpperCase()}`);
                    this.chargeSucceed(charge);
                    // TODO: Update your database for successful payment
                    break;

                case 'charge.failed':
                    await this.sleep(2000);
                    const failedCharge = event.data.object as Stripe.Charge;
                    console.log(`❌ Charge failed: ${failedCharge.id}, Reason: ${failedCharge.failure_message}`);
                    // TODO: Handle failed charge (e.g., notify user)
                    status = await this.chargeFailed(failedCharge);
                    break;
                case 'subscription_schedule.canceled':
                    const subscriptionScheduleCanceled = event.data.object as Stripe.SubscriptionSchedule;
                    console.log('Subscription schedule canceled:', JSON.stringify(subscriptionScheduleCanceled));
                    this.Subscription.delete({ schedule_subscription_id: subscriptionScheduleCanceled.id });
                    status = true;
                    break;
                case 'subscription_schedule.created':
                    const subscriptionScheduleCreated = event.data.object as Stripe.SubscriptionSchedule;
                    console.log('Subscription schedule created:', JSON.stringify(subscriptionScheduleCreated));
                    this.subscriptionScheduleCreated(subscriptionScheduleCreated);
                    status = true;
                    break;
                case 'subscription_schedule.completed':
                    const schedule = event.data.object as Stripe.SubscriptionSchedule;
                    this.Subscription.delete({ schedule_subscription_id: schedule.id });
                    status = true;
                    break;
                default:
            }
        });
        return true;
    }


    async subscriptionSchedulesCanceled(subscriptionScheduleCanceled: any) {
        const subscription = await this.Subscription.findOne({ where: { schedule_subscription_id: subscriptionScheduleCanceled.id } });
        if (subscription) {
            this.Users.update({ id: subscription.users.id }, { subscription_end_date: subscription.start_date });
            await this.Subscription.softDelete({ id: subscription.id });
        }
    }

    async subscriptionScheduleCreated(subscriptionScheduleCreated: any) {

        console.log("subscriptionScheduleCreated", JSON.stringify(subscriptionScheduleCreated));
        const { created, id, phases, customer, subscription } = subscriptionScheduleCreated;

        // Retrieve the upcoming invoice for the subscription
        const invoice = await this.stripe.invoices.retrieveUpcoming({
            customer
        });

        const phaseObj = phases[phases.length - 1];
        const priceId = invoice.lines.data[invoice.lines.data.length - 1].price.id;

        const plan = await this.Plan.findOne({
            relations: ['permission'],
            where: [
                { year_price_id: priceId },
                { month_price_id: priceId }
            ]
        });
        const user = await this.Users.findOne({ where: { stripe_customer_id: customer } });
        if (plan && user) {
            const amount = plan.year_price_id === priceId ? plan.year_amount : plan.month_amount;
            const interval = plan.year_price_id === priceId ? Interval.YEAR : Interval.MONTH;
            const subscriptionObj = {
                users: { id: user.id },
                plan: plan,
                status: SubscriptionStatus.UPCOMING,
                start_date: moment.unix(phaseObj.end_date).format('YYYY-MM-DD HH:mm:ss'), // Current date
                end_date: null, // 1 month or 1 year later
                amount: amount,
                interval: interval,
                payment_mode: 'online',
                is_active: false,
                stripe_subscription_id: null,
                schedule_subscription_id: id,
            }
            this.paymentRepository.createSubscription(subscriptionObj, false)
            const { first_name, last_name, email, id: userId, slug } = user;
            const name = `${first_name} ${last_name}`;
        }
    }

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

    async generateReceiptNumber() {
        const part1 = Math.floor(1000 + Math.random() * 9000); // Random 4-digit number
        const part2 = Math.floor(1000 + Math.random() * 9000); // Random 4-digit number
        return `${part1}-${part2}`;
    }
    /**
     * Handle successful charge event from Stripe
     * @param {Stripe.Charge} charge - Charge object from Stripe event
     */
    async chargeSucceed(charge) {
        let campaignPayment = await this.CampaignPayment.findOne({
            relations: ['campaign', 'brand'],
            where: { transaction_id: charge.payment_intent }
        });
        if (campaignPayment) {
            const charges = await this.stripe.charges.retrieve(charge.id as string);

            const balanceTransaction = await this.stripe.balanceTransactions.retrieve(charges.balance_transaction as string);
            let transaction_fee_usd = balanceTransaction.fee / 100;
            let exchange_rate = balanceTransaction.exchange_rate || 0;
            campaignPayment.payment_status = PAYMENT_STATUS.COMPLETED;
            campaignPayment.invoice_response = charge;
            campaignPayment.charge_id = charge.id;
            campaignPayment.transaction_fee_usd = transaction_fee_usd;
            campaignPayment.exchange_rate = exchange_rate;
            campaignPayment.final_amount_usd = campaignPayment.amount_usd - transaction_fee_usd
            //campaignPayment.invoice_url = charge.receipt_url;
            campaignPayment.receipt_number = await this.generateReceiptNumber();//charges.receipt_number
            await this.CampaignPayment.save(campaignPayment);


            //  CREATE CHARGE HISTORY
            const campaignPaymentHistory = {
                users: campaignPayment.brand,
                campaign: campaignPayment.campaign,
                campaign_payment: campaignPayment,
                amount: campaignPayment.amount,
                currency: campaignPayment.currency,
                amount_usd: campaignPayment.amount_usd,
                transaction_status: PAYMENT_STATUS.COMPLETED,
                payment_status: PaymentType.CREDITED,
                exchange_rate: exchange_rate,
                transaction_fee_usd: transaction_fee_usd,
                final_amount_usd: campaignPayment.amount_usd - transaction_fee_usd
            }
            await this.CampaignPaymentHistory.save(campaignPaymentHistory);
            this.checkPaymentStatus(campaignPayment.id);
            return true;
        }
        return false;
    }

    async checkPaymentStatus(id) {
        let count = 0;
        while (count < 3) {
            await this.sleep(1000);
            const isCompleted = await this.CampaignPayment.count({
                where: { id: id, payment_status: PAYMENT_STATUS.COMPLETED }
            });

            if (isCompleted) {
                const payment = await this.CampaignPayment.findOne({ where: { id } });
                const paymentPdf = {
                    name: "Connector Club",
                    amount: payment.influencer_payable_amount,
                    currency: payment.currency,
                    date: payment.created_at,
                    platform_fees: payment.platform_fees,
                    receipt_number: payment.receipt_number,
                    total_amount: Number(payment.influencer_payable_amount) + Number(payment.platform_fees)
                }
                const response = await this.createInvoiceAndUpload(paymentPdf);

                this.CampaignPayment.update({ id: payment.id }, { invoice_url: response.fileName });
                break;
            }
            count++;
        }
    }


    async createInvoiceAndUpload(paymentPdf: any) {
        const currentDate = moment().format('MMMM DD, YYYY, h:mm A');

        // Path to the EJS template

        const templatePath = path.join(__dirname, '..', '..', '..', '..', '..', 'src', 'templates', 'reports', `campaign-receipt.ejs`);
        // Read the EJS template
        const template = fs.readFileSync(templatePath, 'utf-8');
        // Render the EJS template with dynamic data
        const htmlContent = ejs.render(template, { paymentPdf, currentDate });
        // Launch Puppeteer / executablePath: '/usr/bin/chromium-browser'
        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',
            printBackground: true
        });
        const bucketName = process.env.AWS_BUCKET;
        const fileName = `${paymentPdf.receipt_number}.pdf`;
        let key = `files/campaign/receipt/${fileName}`;
        await browser.close();
        const params = {
            Bucket: bucketName,
            Key: key,
            Body: pdfBuffer,
            ContentType: 'application/pdf',
            // ACL: 'public-read',
        };
        const data = await this.s3.send(new PutObjectCommand(params));
        return { fileName }
    }

    /**
     * Handle failed charge event from Stripe
     * @param {Stripe.Charge} charge - Charge object from Stripe event
     */
    async chargeFailed(charge) {
        let campaignPayment = await this.CampaignPayment.findOne({
            relations: ['campaign', 'brand'],
            where: {
                transaction_id: charge.payment_intent
            }
        });
        if (campaignPayment) {
            campaignPayment.payment_status = PAYMENT_STATUS.FAILED;
            campaignPayment.invoice_response = charge;
            this.CampaignPayment.save(campaignPayment);

            const campaignPaymentHistory = {
                users: campaignPayment.brand,
                campaign: campaignPayment.campaign,
                campaign_payment: campaignPayment,
                amount: campaignPayment.amount,
                currency: campaignPayment.currency,
                amount_usd: campaignPayment.amount_usd,
                transaction_status: PAYMENT_STATUS.FAILED
            }
            this.CampaignPaymentHistory.save(campaignPaymentHistory);
            return true;
        }
        return false;
    }

    async paymentSucceeded(invoice) {
        console.log("paymentSucceeded", JSON.stringify(invoice));
        const subscriptionId = invoice.subscription;
        const subscription = await this.Subscription.findOne({ relations: ['users'], where: { stripe_subscription_id: subscriptionId } });

        if (subscription) {
            console.log("SUBSCRIPTION FOUND", subscription);
            //   this.paymentRepository.cancelActiveSubscription(subscription.users, subscription.plan, subscription.interval);
            const priceId = invoice.lines.data[invoice.lines.data.length - 1].price.id;
            const plan = await this.Plan.findOne({
                where: [
                    { year_price_id: priceId },
                    { month_price_id: priceId }
                ]
            });
            const interval = subscription.interval === Interval.MONTH ? 'months' : 'years'

            let chargeId = invoice.charge;
            let transaction_fee_usd = 0;
            let exchange_rate = 0;
            let amount_usd = invoice.amount_paid / 100; // Convert cents to dollars
            if (chargeId) {
                const charge = await this.stripe.charges.retrieve(chargeId as string);
                const balanceTransaction = await this.stripe.balanceTransactions.retrieve(charge.balance_transaction as string);
                console.log(balanceTransaction);
                transaction_fee_usd = balanceTransaction.fee / 100;
                exchange_rate = balanceTransaction.exchange_rate || 0;
                console.log(`Transaction Fee: ${balanceTransaction.fee / 100} ${balanceTransaction.currency.toUpperCase()}`);
            }
            const period = invoice.lines.data[invoice.lines.data.length - 1].period;
            let subscriptionStartDate = moment.unix(period.start).format('YYYY-MM-DD HH:mm:ss')
            let subscriptionEndDate = moment.unix(period.end).format('YYYY-MM-DD HH:mm:ss')
            await this.SubscriptionHistory.update({ subsciption: { id: subscription.id } }, { status: SubscriptionHistoryStatus.CANCELED });
            const subscriptionHistory = {
                subsciption: subscription,
                plan: plan,
                stripe_subscription_id: subscriptionId,
                status: SubscriptionHistoryStatus.ACTIVE,
                start_date: subscriptionStartDate, // Current date
                end_date: subscriptionEndDate,
                payment_response: invoice,
                transaction_fee_usd: transaction_fee_usd,
                exchange_rate: exchange_rate,
                invoice_receipt: invoice.hosted_invoice_url,
                amount_usd: amount_usd,
                final_amount: amount_usd - transaction_fee_usd,
                interval: subscription.interval
            }

            // SAVE SUBSCRIPTION HISTORY
            await this.SubscriptionHistory.save(subscriptionHistory);
            const brandUser = subscription.users;

            const { first_name, last_name, email, id, slug } = brandUser;
            const name = `${first_name} ${last_name}`;
            const {
                hello_user_name,
                subscription: { subject, title, description },
                copyright,
                all_rights_reserved,
            } = emailMessages.en;

            const mailObj = {
                subject: subject,
                hello_user_name: hello_user_name.replace(':user_name', name),
                main_title: title,
                descriptions: description
                    .replace(':INVOICE_ID', invoice.id)
                    .replace(':AMOUNT', `${amount_usd}`)
                    .replace(':INVOICE_URL', invoice.hosted_invoice_url),
                copyright,
                all_rights_reserved,
            };

            const successMessage = notificationMessages.en.descriptions.brand_subscription.replace(':plan', plan.name + ' (' + interval + ')');
            this.paymentRepository.brandNotification('brand_subscription', brandUser, successMessage, null, RedirectType.BRAND_SUBSCRIPTION, null, null, mailObj);

            // UPDATE SUBSCRIPTION ID & RESPONSE
            subscription['status'] = SubscriptionStatus.ACTIVE;
            subscription['is_active'] = true;
            //  subscription['end_date'] = subscriptionEndDate;
            // this.paymentRepository.createSubscription(subscription, true);
            // let userObj: any = {
            //     is_subscribed: true,
            //     subscription_end_date: subscriptionEndDate
            // }
            // if (subscription.is_trial_active) {
            //     userObj.is_trial_used = true
            // }
            // await this.Users.update({ id: id }, userObj);

            // SEND NOTIFICATION TO ADMINS
            const adminMessage = notificationMessages.en.descriptions.plan_subscription.replace(':userName', name);
            this.paymentRepository.notification('admin_subscription', adminMessage, brandUser, RedirectType.BRAND_LIST, id, slug);


        }
    }

    async paymentFailed(invoice) {
        const subscriptionId = invoice.subscription;
        const subscription = await this.Subscription.findOne({ relations: ['plan', 'users'], where: { stripe_subscription_id: subscriptionId } });
        if (subscription) {
            const plan = subscription.plan;
            const interval = subscription.interval === Interval.MONTH ? 'months' : 'years'
            const subscriptionHistory = {
                subsciption: subscription,
                stripe_subscription_id: subscriptionId,
                status: SubscriptionHistoryStatus.INCOMPLETE,
                start_date: moment().format('YYYY-MM-DD HH:mm:ss'), // Current date
                end_date: moment().add(1, interval).format('YYYY-MM-DD HH:mm:ss'),
                payment_response: invoice
            }

            // SAVE SUBSCRIPTION HISTORY
            this.SubscriptionHistory.save(subscriptionHistory);
            // UPDATE SUBSCRIPTION ID & RESPONSE
            subscription['status'] = SubscriptionStatus.CANCELED;
            this.Subscription.save(subscription)
            this.Users.update({ id: subscription.users.id }, { is_subscribed: false, subscription_end_date: moment().format('YYYY-MM-DD HH:mm:ss') });
        }
    }

    async saveSubscriptionBk(subscriptionCreated) {
        console.log("saveSubscription", JSON.stringify(subscriptionCreated));
        const { id, metadata, status, schedule } = subscriptionCreated;
        const subscription = await this.Subscription.findOne({
            where: [
                { id: metadata.subscription_id },
                { schedule_subscription_id: schedule }
            ]
        });
        if (!subscription) {
            console.log("SUBSCRIPTION NOT FOUND");
            return false;
        }

        if (subscription.stripe_subscription_id == id) {
            console.log("ALREADY SUBSCRIPTION DETAIL STORED");
            return false;
        }

        const users = await this.Users.findOne({ where: { id: metadata.user_id } });
        if (!users) {
            console.log("USER NOT FOUND");
            return false;
        }
        await this.Subscription.update({ users: { id: users.id } }, { is_active: false });

        // UPDATE SUBSCRIPTION ID & RESPONSE
        subscription['payment_response'] = subscriptionCreated;
        subscription['stripe_subscription_id'] = id;
        subscription['status'] = SubscriptionStatus.ACTIVE;
        subscription['is_active'] = true
        await this.Subscription.save(subscription)

        this.Users.update({ id: users.id }, { is_subscribed: true });
    }

    async saveSubscription(subscription, from: string) {
        console.log("saveSubscription", subscription);
        const { created, id, metadata, status, items, current_period_end, trial_end, customer, cancel_at_period_end, default_payment_method } = subscription;
        const price = items.data[0].plan;

        let planWhere: any = price.interval === 'month' ? { month_price_id: price.id } : { year_price_id: price.id };
        const plan = await this.Plan.findOne({ relations: ['permission'], where: planWhere });
        const user = await this.Users.findOne({ where: { stripe_customer_id: customer } });

        if (plan && user) {
            if (from === 'customer.subscription.created') {
                this.paymentRepository.cancelFreeActiveSubscription(user, plan, price.interval);
            }

            const subscriptionObj: any = {
                users: { id: user.id },
                plan: plan,
                status: cancel_at_period_end ? SubscriptionStatus.CANCELED : SubscriptionStatus.ACTIVE,
                start_date: moment.unix(created).format('YYYY-MM-DD HH:mm:ss'), // Current date
                end_date: moment.unix(current_period_end).format('YYYY-MM-DD HH:mm:ss'), // 1 month or 1 year later
                amount: price.amount / 100,
                interval: price.interval,
                payment_mode: 'online',
                is_active: true,
                is_trial_active: trial_end ? true : false,
                trial_end_date: trial_end ? moment.unix(trial_end).format('YYYY-MM-DD HH:mm:ss') : null,
                stripe_subscription_id: id,
                schedule_subscription_id: subscription.schedule,
                payment_response: subscription
            }
            const subscriptionExist = await this.Subscription.findOne({ relations: ['plan'], where: { stripe_subscription_id: id } });
            let planChanged = true;
            if (subscriptionExist) {
                subscriptionObj.id = subscriptionExist.id;
                subscriptionObj.is_trial_active = false;
                planChanged = subscriptionExist.plan.id !== plan.id;
            }
            let oldPlanName = subscriptionExist ? subscriptionExist.plan.name : 'Free Plan';
            let oldPlanInterval = subscriptionExist ? subscriptionExist.interval : 'month';
            await this.paymentRepository.createSubscription(subscriptionObj, planChanged)

            if (subscription.schedule == null) {
                // DELETE UPCOMING SUBSCRIPTION IF SCHEDULE IS NULL
                await this.Subscription.delete({ status: SubscriptionStatus.UPCOMING, users: { id: user.id } });
            }
            this.savePaymentMethod(default_payment_method, user);
            if (planChanged) {
                const { first_name, last_name, email, id: userId, slug } = user;
                const name = `${first_name} ${last_name}`;

                const brandMessage = notificationMessages.en.descriptions.brand_switch_subscription
                    .replace(':from_plan_name', oldPlanName + ' (' + oldPlanInterval + ')')
                    .replace(':to_plan_name', plan.name + ' (' + price.interval + ')');

                const mailObj = {
                    subject: emailMessages.en.subscription_switched.subject,
                    hello_user_name: emailMessages.en.hello_user_name.replace(':user_name', name),
                    main_title: emailMessages.en.subscription_switched.title,
                    descriptions: emailMessages.en.subscription_switched.description
                        .replace(':from_plan_name', oldPlanName + ' (' + oldPlanInterval + ')')
                        .replace(':to_plan_name', plan.name + ' (' + price.interval + ')'),
                };
                this.Users.update({ id: user.id }, { is_invoice_sent: false });
                this.paymentRepository.brandNotification('brand_switch_subscription', user, brandMessage, null, RedirectType.BRAND_SUBSCRIPTION, userId, slug, mailObj);
            }
            this.Users.update({ id: user.id }, { is_subscribed: true, is_trial_used: true, subscription_end_date: subscriptionObj.end_date });
        }
    }

    async savePaymentMethod(paymentMethodId, authUser) {
        const paymentMethod = await this.stripe.paymentMethods.retrieve(paymentMethodId);
        const card = await this.Card.findOne({ where: { card_id: paymentMethod.id } });
        if (!card) {
            const cardObj = {
                card_id: paymentMethod.id,
                card_holder_name: paymentMethod?.billing_details?.name || authUser.first_name,
                last4: paymentMethod.card.last4,
                brand: paymentMethod.card.brand,
                exp_month: paymentMethod.card.exp_month,
                exp_year: paymentMethod.card.exp_year,
                is_default: true,
                card_response: paymentMethod,
                users: authUser,
                user_id: authUser.id
            }
            this.Card.save(cardObj);
        }
    }

    async clientSecret(user: any) {
        let customerId = user.stripe_customer_id;
        if (!user.stripe_customer_id) {
            const customer = await this.stripe.customers.create({
                email: user.email, // Add customer email (optional)
                name: `${user.first_name} ${user.last_name}`
            });

            customerId = customer.id;
            await this.Users.update({ id: user.id }, { stripe_customer_id: customerId });
        }
        const intent = await this.stripe.setupIntents.create({
            customer: customerId,
            payment_method_types: ['card'],
        });

        return { client_secret: intent.client_secret } //intent.client_secret;
    }

    async paymentIntents(amount: number, currency: string, cardToken: string, customerId?: string, metadata?: Record<string, any>) {
        try {
            const amountPayable = Math.round(amount * 100);
            const paymentIntent = await this.stripe.paymentIntents.create({
                amount: amountPayable, // Convert to cents
                currency,
                customer: customerId,
                payment_method: cardToken,
                confirm: true,
                metadata: metadata,
                automatic_payment_methods: {
                    enabled: true,
                    allow_redirects: 'never', // Prevents Stripe from using redirect-based methods
                }
            });
            console.log("paymentIntent", paymentIntent);
            if (paymentIntent.status === 'requires_action' || paymentIntent.status === 'requires_confirmation') {

                // Tell client to complete 3D Secure or other authentication
                const action = {
                    clientSecret: paymentIntent.client_secret,
                    requiresAction: true,
                    id: paymentIntent.id,
                }
                return { response: { paymentIntent: action, status: false } }
            }
            return { response: { paymentIntent, status: true } }
        } catch (error) {
            console.log(error);
            return { response: { charge: null, error: error.message, status: false } }
        }
    }

    async refundPayment(chargeId: string) {
        try {
            // Create a refund for the charge
            const refund = await this.stripe.refunds.create({
                charge: chargeId,
            });

            return refund;
        } catch (error) {
            return false;
        }
    }

    async billingPortal(customerId: string) {
        try {
            const session = await this.stripe.billingPortal.sessions.create({
                customer: customerId,
                return_url: process.env.FRONTEND_URL + 'brand/profile/my-subscriptions',
            });
            return session.url;
        } catch (error) {
            console.log(error);
            throw new HttpException(error.message, HttpStatus.BAD_REQUEST);
        }
    }

    async getInvoiceLink(invoiceId: string) {
        try {
            const invoice = await this.stripe.invoices.retrieve(invoiceId);
            if (!invoice.hosted_invoice_url) {
                throw new HttpException(responseMessages.en.subscription.invoice_not_found, HttpStatus.NOT_FOUND);
            }
            return invoice.hosted_invoice_url;
        } catch (error) {
            throw new HttpException(responseMessages.en.subscription.invoice_not_found, HttpStatus.BAD_REQUEST);
        }
    }

    async reactivateSubscription(subscriptionId, startDate, id) {
        try {
            const response = await this.stripe.subscriptions.update(subscriptionId, { cancel_at_period_end: false });
            // const subscription = await this.stripe.subscriptions.retrieve(subscriptionId);
            // if (subscription) {
            //     return await this.subscriptionSchedules(subscription.customer as string, subscription.items.data[0].price.id, subscription.default_payment_method as string, startDate, { subscription_id: id });
            // }
            return { status: true, response: response, message: 'success' };
        } catch (error) {
            console.log(error);
            return { status: false, response: null, message: error.message };
        }
    }

    async updateCustomerBillingPortal(products) {
        await this.stripe.billingPortal.configurations.update(process.env.STRIPE_BILLING_PORTAL_ID, {
            features: {
                subscription_update: {
                    enabled: true,
                    products: products,
                },
            },
        });
    }
}