import nodemailer from 'nodemailer';
import { SentMessageInfo } from 'nodemailer/lib/smtp-transport';
import { Options } from 'nodemailer/lib/mailer';
import * as fs from 'fs';
import * as path from 'path';
import Config from '#config';

interface SendResult {
	mode: 'smtp' | 'fallback';
	info: SentMessageInfo | string;
}

class MailService {
	private static instance: MailService;
	transporter: nodemailer.Transporter<SentMessageInfo> | null;
	private fallbackLogPath: string;

	private constructor() {
		// Use SMTP only when host + credentials are configured. Otherwise fall back
		// to a console/file logger so dev environments can still test the email
		// verification flow without setting up Gmail/SendGrid.
		const smtpConfigured =
			Config.email.host &&
			Config.email.host !== 'smtp.example.com' &&
			Config.email.user &&
			Config.email.pass;

		if (smtpConfigured) {
			this.transporter = nodemailer.createTransport({
				host: Config.email.host,
				port: Config.email.port,
				secure: false,
				auth: {
					user: Config.email.user,
					pass: Config.email.pass,
				},
			});
		} else {
			this.transporter = null;
		}

		// Fallback log goes next to the rest of dev artefacts so it's easy to find.
		this.fallbackLogPath = path.resolve(process.cwd(), 'dev-mail.log');
	}

	static getInstance(): MailService {
		MailService.instance ??= new MailService();
		return MailService.instance;
	}

	private logFallback(mailOptions: Options): string {
		const entry = [
			'====== DEV MAIL (no SMTP configured) ======',
			`At:    ${new Date().toISOString()}`,
			`From:  ${mailOptions.from ?? Config.email.from}`,
			`To:    ${(mailOptions.to ?? '').toString()}`,
			`Subj:  ${mailOptions.subject ?? '(no subject)'}`,
			'----- TEXT -----',
			mailOptions.text ?? '(no text body)',
			'----- HTML -----',
			mailOptions.html ?? '(no html body)',
			'=============================================',
			'',
		].join('\n');

		// eslint-disable-next-line no-console
		console.log(`\n[email fallback] ${(mailOptions.to ?? '').toString()} — see dev-mail.log`);
		try {
			fs.appendFileSync(this.fallbackLogPath, entry, 'utf8');
		} catch (err) {
			// eslint-disable-next-line no-console
			console.warn('[email fallback] failed to append dev-mail.log:', err);
		}

		return entry;
	}

	async sendmail(mailOptions: Options): Promise<SendResult> {
		if (this.transporter) {
			const info = await this.transporter.sendMail(mailOptions);
			return { mode: 'smtp', info };
		}
		const logged = this.logFallback(mailOptions);
		return { mode: 'fallback', info: logged };
	}
}

export default MailService;
export type { SendResult };
