import { Injectable, OnModuleInit, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { EmailSettings } from './entities/email-setting.entity';
import { EmailTemplate } from './entities/email-template.entity';
import * as amqp from 'amqplib';
import * as nodemailer from 'nodemailer';
import * as handlebars from 'handlebars';

@Injectable()
export class EmailService implements OnModuleInit {
    private readonly logger = new Logger(EmailService.name);
    private connection: any; // Use any to bypass strict type check on amqplib if wrapper types are elusive
    private channel: any;

    constructor(
        @InjectRepository(EmailSettings)
        private settingsRepo: Repository<EmailSettings>,
        @InjectRepository(EmailTemplate)
        private templateRepo: Repository<EmailTemplate>,
    ) { }

    async onModuleInit() {
        this.connectLavinMQ();
    }

    async connectLavinMQ() {
        try {
            const settings = await this.settingsRepo.findOne({ where: { isActive: true }, order: { updatedAt: 'DESC' } });
            if (!settings || !settings.amqpUrl) {
                this.logger.warn('LavinMQ URL not configured in email_settings. Worker disabled.');
                return;
            }

            this.logger.log(`Connecting to LavinMQ...`);
            this.connection = await amqp.connect(settings.amqpUrl);
            this.channel = await this.connection.createChannel();

            const queue = 'mail_queue';
            await this.channel.assertQueue(queue, { durable: true });
            this.channel.prefetch(1);

            this.logger.log(`Waiting for messages in ${queue}...`);

            this.channel.consume(queue, async (msg) => {
                if (msg !== null) {
                    this.logger.log(`Received message: ${msg.content.toString()}`);
                    try {
                        const job = JSON.parse(msg.content.toString());
                        await this.processJob(job);
                        this.channel.ack(msg);
                    } catch (error) {
                        this.logger.error(`Failed to process message: ${error.message}`);
                        // Nack? Or Ack to discard? Discard for now to verify.
                        this.channel.nack(msg, false, false); // requeue=false
                    }
                }
            });
        } catch (error) {
            this.logger.error(`Failed to connect to LavinMQ: ${error.message}`);
            setTimeout(() => this.connectLavinMQ(), 5000); // Retry
        }
    }

    async processJob(job: { to: string; template: string; variables: any }) {
        const { to, template: slug, variables } = job;

        // 1. Get Template
        const tmpl = await this.templateRepo.findOne({ where: { slug } });
        if (!tmpl) {
            throw new Error(`Template '${slug}' not found`);
        }

        // 2. Compile
        const compiledSubject = handlebars.compile(tmpl.subject)(variables);
        const compiledBody = handlebars.compile(tmpl.bodyHtml)(variables);

        // 3. Get Settings
        const settings = await this.settingsRepo.findOne({ where: { isActive: true } });
        if (!settings) throw new Error('Email settings not found');

        // 4. Send
        const transporter = nodemailer.createTransport({
            host: settings.smtpHost,
            port: settings.smtpPort,
            secure: settings.smtpSecure,
            auth: {
                user: settings.smtpUser,
                pass: settings.smtpPass, // Assumption: Plaintext or handled by app logic
            },
        });

        await transporter.sendMail({
            from: `"${settings.senderName}" <${settings.senderEmail}>`,
            to,
            subject: compiledSubject,
            html: compiledBody,
        });

        this.logger.log(`Email sent to ${to} (Template: ${slug})`);
    }
}
