import nodemailer from 'nodemailer';
import type Mail from 'nodemailer/lib/mailer/index.js';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import db from '../../database/models/index.js';
import handlebars from 'handlebars';
import { registerEmailPartials, normalizeCtaPlaceholders } from './handlebars-email.js';
import {
    getNodemailerTransportOptions,
    getSmtpConfig,
    isSmtpEnabled
} from '../../services/smtpConfig.service.js';
import { getFrontendBaseUrl } from './frontendUrl.js';

export { getFrontendBaseUrl } from './frontendUrl.js';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

type TransporterMode = 'none' | 'smtp' | 'ethereal';

let transporter: nodemailer.Transporter | null = null;
let transporterMode: TransporterMode = 'none';
let transporterPromise: Promise<nodemailer.Transporter | null> | null = null;

/** Call after initializeSmtpConfig() so the next send picks up GSM / .env SMTP. */
export function resetEmailTransporter(): void {
    transporter = null;
    transporterMode = 'none';
    transporterPromise = null;
}

const initTransporter = async (): Promise<nodemailer.Transporter | null> => {
    if (transporter) return transporter;
    if (transporterPromise) return transporterPromise;

    transporterPromise = (async () => {
        if (isSmtpEnabled()) {
            const options = getNodemailerTransportOptions();
            if (options) {
                transporter = nodemailer.createTransport({
                    host: options.host,
                    port: options.port,
                    secure: options.secure,
                    auth: options.auth
                });
                transporterMode = 'smtp';
                console.log(
                    `[Email Service] SMTP transport: ${options.host}:${options.port} (secure=${options.secure})`
                );
                return transporter;
            }
            console.warn(
                '[Email Service] ENABLE_SMTP=true but host/user/password missing — email not sent'
            );
            transporterMode = 'none';
            return null;
        }

        const account = await nodemailer.createTestAccount();
        transporter = nodemailer.createTransport({
            host: account.smtp.host,
            port: account.smtp.port,
            secure: account.smtp.secure,
            auth: {
                user: account.user,
                pass: account.pass
            }
        });
        transporterMode = 'ethereal';
        console.log(
            `[Email Service] ENABLE_SMTP=false — Ethereal test transport: ${account.user}`
        );
        return transporter;
    })();

    return transporterPromise;
};

const { EmailTemplate } = db;

/** If DB row missing, try these codes (then filesystem under that code). */
const TEMPLATE_CODE_FALLBACKS: Record<string, string[]> = {
    INTERVIEW_SCHEDULED_APPLICANT: ['INTERVIEW_SCHEDULED'],
    INTERVIEW_SCHEDULED_PANELIST: ['INTERVIEW_SCHEDULED'],
    INTERVIEW_RESCHEDULED_APPLICANT: ['INTERVIEW_SCHEDULED'],
    INTERVIEW_RESCHEDULED_PANELIST: ['INTERVIEW_SCHEDULED']
};

function findTemplateFile(templatesRoot: string, code: string): string | null {
    const lowerFile = path.join(templatesRoot, `${code.toLowerCase()}.html`);
    const exactFile = path.join(templatesRoot, `${code}.html`);
    if (fs.existsSync(lowerFile)) return lowerFile;
    if (fs.existsSync(exactFile)) return exactFile;
    return null;
}

async function resolveEmailTemplate(templateCode: string) {
    const codesToTry = [templateCode, ...(TEMPLATE_CODE_FALLBACKS[templateCode] || [])];

    for (const code of codesToTry) {
        const dbTemplate = await EmailTemplate.findOne({
            where: { templateCode: code, isActive: true }
        });
        if (dbTemplate) {
            return { kind: 'db' as const, resolvedCode: code, dbTemplate };
        }
    }

    const templatesRoot = path.join(__dirname, '../../emailtemplates');
    for (const code of codesToTry) {
        const filePath = findTemplateFile(templatesRoot, code);
        if (filePath) {
            return { kind: 'file' as const, resolvedCode: code, filePath };
        }
    }

    return null;
}

export interface SendEmailOptions {
    attachments?: Array<{
        filename: string;
        path?: string;
        content?: Buffer | string;
        contentType?: string;
    }>;
}

export const sendEmail = async (
    to: string,
    subject: string,
    templateCode: string,
    replacements: Record<string, string>,
    options?: SendEmailOptions
) => {
    try {
        let finalHtml = '';
        let finalSubject = subject;
        const ensureHeaderFooter = (html: string) => {
            const hasHeader = /class=["']header["']/i.test(html);
            const hasFooter = /class=["']footer["']/i.test(html);
            if (hasHeader && hasFooter) return html;
            const wrapper = handlebars.compile(`{{> email_header}}\n{{{body}}}\n{{> email_footer}}`);
            return wrapper({
                ...replacements,
                year: new Date().getFullYear().toString(),
                body: html
            });
        };

        const resolved = await resolveEmailTemplate(templateCode);
        if (!resolved) {
            throw new Error(
                `Template not found: ${templateCode} (run: npm run seed:interview-templates)`
            );
        }

        registerEmailPartials(handlebars);
        const allReplacements = normalizeCtaPlaceholders({
            ...replacements,
            year: new Date().getFullYear().toString()
        });

        if (resolved.kind === 'db') {
            const subjectTemplate = handlebars.compile(resolved.dbTemplate.subject);
            finalSubject = subjectTemplate(allReplacements);
            const bodyTemplate = handlebars.compile(resolved.dbTemplate.body);
            finalHtml = ensureHeaderFooter(bodyTemplate(allReplacements));
        } else {
            const source = fs.readFileSync(resolved.filePath, 'utf-8');
            const bodyTemplate = handlebars.compile(source);
            finalHtml = ensureHeaderFooter(bodyTemplate(allReplacements));
        }

        if (resolved.resolvedCode !== templateCode) {
            console.warn(
                `[Email Service] Template ${templateCode} → fallback ${resolved.resolvedCode} (${resolved.kind})`
            );
        }

        const readyTransporter = await initTransporter();
        if (!readyTransporter) {
            return;
        }

        const fromAddress = getSmtpConfig().from || 'Royal Enfield <noreply@royalenfield.com>';

        const mail: Mail.Options = {
            from: fromAddress,
            to,
            subject: finalSubject,
            html: finalHtml,
            ...(options?.attachments?.length ? { attachments: options.attachments } : {})
        };

        const info = await readyTransporter.sendMail(mail);

        const rejected = (info as { rejected?: string[] }).rejected;
        console.log(
            `[Email Service] Sent template=${templateCode} via ${transporterMode} from="${fromAddress}" to="${to}" messageId=${info.messageId}`
        );
        if (rejected?.length) {
            console.warn(`[Email Service] SMTP rejected recipients for ${to}:`, rejected);
        }
        if (transporterMode === 'ethereal') {
            const preview = nodemailer.getTestMessageUrl(info);
            if (preview) console.log(`[Email Service] Preview URL: ${preview}`);
        }

        return info;
    } catch (error) {
        console.error(`Failed to send email (${templateCode}) to ${to}:`, error);
        throw error;
    }
};

export const sendOpportunityEmail = async (
    to: string,
    applicantName: string,
    location: string,
    applicationId: string
) => {
    const link = `${getFrontendBaseUrl()}/questionnaire/${applicationId}`;
    await sendEmail(to, 'Action Required: Royal Enfield Dealership Opportunity', 'OPPORTUNITY', {
        applicantName,
        location,
        applicationId,
        link,
        ctaLabel: 'Complete Questionnaire'
    });
};

export const sendNonOpportunityEmail = async (to: string, applicantName: string, location: string) => {
    await sendEmail(to, 'Update on your Royal Enfield Dealership Application', 'NON_OPPORTUNITY', {
        applicantName,
        location
    });
};

export const sendInterviewScheduledEmail = async (
    to: string,
    name: string,
    applicationId: string,
    interview: { scheduleDate: string | Date; level: string; interviewType: string; linkOrLocation: string; status: string }
) => {
    const date = new Date(interview.scheduleDate);
    const time = date.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' });
    const formattedDate = date.toLocaleDateString('en-US', {
        weekday: 'long',
        year: 'numeric',
        month: 'long',
        day: 'numeric'
    });
    const dateTime = `${formattedDate} ${time}`;

    await sendEmail(to, `Interview Scheduled: ${applicationId}`, 'INTERVIEW_SCHEDULED', {
        name,
        applicationId,
        level: interview.level,
        dateTime,
        type: interview.interviewType,
        location: interview.linkOrLocation,
        status: interview.status
    });
};

export const sendUserAssignedEmail = async (
    to: string,
    userName: string,
    applicationId: string,
    dealerName: string,
    participantType: string
) => {
    await sendEmail(to, `New Application Assignment: ${applicationId}`, 'USER_ASSIGNED', {
        userName,
        applicationId,
        dealerName,
        participantType
    });
};

export const sendQuestionnaireAckEmail = async (
    to: string,
    applicantName: string,
    location: string,
    applicationId: string
) => {
    await sendEmail(to, `Questionnaire Submitted Successfully: ${applicationId}`, 'QUESTIONNAIRE_SUBMITTED', {
        applicantName,
        location,
        applicationId
    });
};

export const sendShortlistedEmail = async (
    to: string,
    applicantName: string,
    location: string,
    applicationId: string
) => {
    const portalLink = `${getFrontendBaseUrl()}/login`;
    await sendEmail(to, `Congratulations! You are Shortlisted: ${applicationId}`, 'APPLICANT_SHORTLISTED', {
        applicantName,
        location,
        applicationId,
        portalLink,
        ctaLabel: 'Visit Dealer Portal'
    });
};
