import { mailConfig } from '../../config/mail.js';
import { ErrorFactory } from '../../exceptions/ZintrustError.js';
import { SesDriver } from './drivers/Ses.js';
import { resolveAttachments } from './attachments.js';
import { MailDriverRegistry } from './MailDriverRegistry.js';
import { Storage } from '../storage/index.js';
const resolveFrom = (input) => {
    const address = input?.address ?? mailConfig.from.address;
    const name = input?.name ?? mailConfig.from.name;
    if (address.trim() === '') {
        const err = ErrorFactory.createConfigError('Mail: missing from address (set MAIL_FROM_ADDRESS or pass from.address)');
        throw err;
    }
    return { email: address, name: name.trim() === '' ? undefined : name };
};
function assertStorageDiskLike(value) {
    if (typeof value !== 'object' || value === null) {
        const err = ErrorFactory.createConfigError('Storage disk is invalid (expected object)');
        throw err;
    }
    const v = value;
    const driver = v['driver'];
    if (typeof driver !== 'object' || driver === null) {
        const err = ErrorFactory.createConfigError('Storage disk driver is invalid (expected object)');
        throw err;
    }
    const d = driver;
    if (typeof d['get'] !== 'function') {
        const err = ErrorFactory.createConfigError('Storage disk driver is missing get()');
        throw err;
    }
    if (typeof d['exists'] !== 'function') {
        const err = ErrorFactory.createConfigError('Storage disk driver is missing exists()');
        throw err;
    }
}
const getDiskSafe = (disk) => {
    const diskValue = Storage.getDisk(disk);
    assertStorageDiskLike(diskValue);
    return diskValue;
};
const createStorageWrapper = () => ({
    async get(disk, path) {
        const d = getDiskSafe(disk);
        const result = await Promise.resolve(d.driver.get(d.config, path));
        return result;
    },
    async exists(disk, path) {
        const d = getDiskSafe(disk);
        const result = await Promise.resolve(d.driver.exists(d.config, path));
        return Boolean(result);
    },
});
const sendWithDriver = async (driver, message) => {
    if (driver.driver === 'ses') {
        const result = await SesDriver.send({ region: driver.region }, message);
        return { ok: result.ok, driver: 'ses', messageId: result.messageId };
    }
    // Drivers resolve via MailDriverRegistry (external packages)
    const external = MailDriverRegistry.get(driver.driver);
    if (external !== undefined) {
        const result = await external(driver, message);
        return {
            ok: Boolean(result?.ok),
            driver: driver.driver,
            messageId: typeof result?.messageId === 'string' ? result.messageId : undefined,
        };
    }
    if (driver.driver === 'sendgrid' || driver.driver === 'mailgun' || driver.driver === 'smtp') {
        throw ErrorFactory.createConfigError(`Mail driver not registered: ${driver.driver} (run \`zin add mail:${driver.driver}\` / \`npm i @zintrust/mail-${driver.driver}\`)`);
    }
    // Config exists for future drivers, but implementations are intentionally CLI/runtime-safe and added incrementally.
    throw ErrorFactory.createConfigError(`Mail driver not registered: ${mailConfig.default}. Available drivers: ses, sendgrid, mailgun, smtp. Run \`zin add mail:${mailConfig.default}\` to install.`);
};
const createMailer = (name) => Object.freeze({
    async send(input) {
        const driver = mailConfig.getDriver(name);
        if (driver.driver === 'disabled') {
            const err = ErrorFactory.createConfigError('Mail driver is disabled (set MAIL_DRIVER)');
            throw err;
        }
        const from = resolveFrom(input.from);
        const storage = createStorageWrapper();
        const attachments = await resolveAttachments(input.attachments, { storage });
        return sendWithDriver(driver, {
            to: input.to,
            from,
            subject: input.subject,
            text: input.text,
            html: input.html,
            attachments,
        });
    },
});
const looksLikeHtml = (value) => /<\s*html\b|<!doctype\b|<\s*body\b/i.test(value);
async function renderTemplateToHtml(input) {
    const { template, variables } = input;
    // Import lazily to avoid circular deps
    const { loadTemplate } = await import('./template-loader.js');
    if (looksLikeHtml(template)) {
        return loadTemplate(template, (variables ?? {}));
    }
    // template-loader expects full filename
    const fileName = template.endsWith('.html') ? template : `${template}.html`;
    return loadTemplate(fileName, (variables ?? {}));
}
export const Mail = Object.freeze({
    /**
     * Render a template with variables and return HTML (preview only).
     */
    async render(input) {
        return renderTemplateToHtml(input);
    },
    /** Alias of render */
    async view(input) {
        return renderTemplateToHtml(input);
    },
    /**
     * Select a named mailer (key from mailConfig.drivers).
     *
     * Example: `Mail.mailer('transactional').send(...)`
     */
    mailer(name) {
        return createMailer(name);
    },
    async send(input) {
        return createMailer().send(input);
    },
});
export default Mail;
