#!/usr/bin/env node
/**
 * lean-accounts.js — strip default-valued fields from an accounts.jsonc.
 *
 * Reads any accounts.jsonc (the one mailx writes when it bloats it with
 * port: 993, tls: true, auth: "password", enabled: true, sig.html: false,
 * etc.) and emits a compact form. Same logic as the Lean button in the
 * config editor, but standalone so it works without mailx running.
 *
 * Usage:
 *   node lean-accounts.js <path>           prints lean output to stdout
 *   node lean-accounts.js <path> --inplace overwrites the file
 *
 * Drops:
 *   - imap/smtp host/port/tls/auth that match the email's provider defaults
 *   - imap/smtp user when it equals the email
 *   - enabled: true (default)
 *   - sig.html: false (default)
 *   - per-account name when it matches the file-level "name"
 *
 * Promotes a shared "name" to file-level when every account has the same.
 *
 * Requires: jsonc-parser (any version that exports `parse` with comment+
 * trailing-comma tolerance). The mailx repo's node_modules has it.
 */

import fs from "node:fs";
import path from "node:path";
import { pathToFileURL } from "node:url";

const __dirname = import.meta.dirname;

// ── Provider defaults: same set mailx-settings uses ────────────────────────
const PROVIDERS = {
    "gmail.com": {
        label: "Gmail",
        imap: { host: "imap.gmail.com", port: 993, tls: true, auth: "oauth2" },
        smtp: { host: "smtp.gmail.com", port: 587, tls: true, auth: "oauth2" },
    },
    "googlemail.com": {
        label: "Gmail",
        imap: { host: "imap.gmail.com", port: 993, tls: true, auth: "oauth2" },
        smtp: { host: "smtp.gmail.com", port: 587, tls: true, auth: "oauth2" },
    },
    "outlook.com": {
        label: "Outlook",
        imap: { host: "outlook.office365.com", port: 993, tls: true, auth: "oauth2" },
        smtp: { host: "smtp.office365.com", port: 587, tls: true, auth: "oauth2" },
    },
    "hotmail.com": {
        label: "Outlook",
        imap: { host: "outlook.office365.com", port: 993, tls: true, auth: "oauth2" },
        smtp: { host: "smtp.office365.com", port: 587, tls: true, auth: "oauth2" },
    },
    "yahoo.com": {
        label: "Yahoo",
        imap: { host: "imap.mail.yahoo.com", port: 993, tls: true, auth: "oauth2" },
        smtp: { host: "smtp.mail.yahoo.com", port: 587, tls: true, auth: "oauth2" },
    },
    "icloud.com": {
        label: "iCloud",
        imap: { host: "imap.mail.me.com", port: 993, tls: true, auth: "oauth2" },
        smtp: { host: "smtp.mail.me.com", port: 587, tls: true, auth: "oauth2" },
    },
};

function denormalizeAccount(acct, globalName) {
    const domain = (acct.email || "").split("@")[1]?.toLowerCase() || "";
    const provider = PROVIDERS[domain];
    const out = {};
    out.id = acct.id;
    if (acct.label && acct.label !== provider?.label && acct.label !== acct.id) out.label = acct.label;
    out.email = acct.email;
    if (acct.name && acct.name !== globalName) out.name = acct.name;
    if (acct.primary) out.primary = true;
    if (acct.primaryCalendar !== undefined) out.primaryCalendar = acct.primaryCalendar;
    if (acct.primaryTasks !== undefined) out.primaryTasks = acct.primaryTasks;
    if (acct.primaryContacts !== undefined) out.primaryContacts = acct.primaryContacts;

    const imapOut = {};
    if (acct.imap?.host && acct.imap.host !== provider?.imap.host) imapOut.host = acct.imap.host;
    if (acct.imap?.user && acct.imap.user !== acct.email) imapOut.user = acct.imap.user;
    if (acct.imap?.password) imapOut.password = acct.imap.password;
    if (acct.imap?.port && acct.imap.port !== (provider?.imap.port ?? 993)) imapOut.port = acct.imap.port;
    if (acct.imap?.tls !== undefined && acct.imap.tls !== (provider?.imap.tls ?? true)) imapOut.tls = acct.imap.tls;
    if (acct.imap?.auth && acct.imap.auth !== (provider?.imap.auth ?? "password")) imapOut.auth = acct.imap.auth;
    if (Object.keys(imapOut).length > 0) out.imap = imapOut;

    const smtpOut = {};
    if (acct.smtp?.host && acct.smtp.host !== provider?.smtp.host) smtpOut.host = acct.smtp.host;
    if (acct.smtp?.user && acct.smtp.user !== acct.email && acct.smtp.user !== acct.imap?.user) smtpOut.user = acct.smtp.user;
    if (acct.smtp?.password) smtpOut.password = acct.smtp.password;
    if (acct.smtp?.port && acct.smtp.port !== (provider?.smtp.port ?? 587)) smtpOut.port = acct.smtp.port;
    if (acct.smtp?.tls !== undefined && acct.smtp.tls !== (provider?.smtp.tls ?? true)) smtpOut.tls = acct.smtp.tls;
    if (acct.smtp?.auth && acct.smtp.auth !== (provider?.smtp.auth ?? "password")) smtpOut.auth = acct.smtp.auth;
    if (Object.keys(smtpOut).length > 0) out.smtp = smtpOut;

    if (acct.defaultSend) out.defaultSend = true;
    if (acct.enabled === false) out.enabled = false;
    if (acct.relayDomains?.length > 0) out.relayDomains = acct.relayDomains;
    if (acct.identityDomains?.length > 0) out.identityDomains = acct.identityDomains;

    const syncContactsDefault = provider?.imap.auth === "oauth2";
    if (acct.syncContacts !== undefined && acct.syncContacts !== syncContactsDefault) {
        out.syncContacts = acct.syncContacts;
    }
    if (acct.signature) out.signature = acct.signature;
    if (acct.sig?.text) {
        out.sig = acct.sig.html ? { text: acct.sig.text, html: true } : { text: acct.sig.text };
    }
    return out;
}

async function main() {
    const args = process.argv.slice(2);
    const inputPath = args.find(a => !a.startsWith("--"));
    const inplace = args.includes("--inplace");
    if (!inputPath) {
        console.error("Usage: node lean-accounts.js <path-to-accounts.jsonc> [--inplace]");
        process.exit(2);
    }
    if (!fs.existsSync(inputPath)) {
        console.error(`File not found: ${inputPath}`);
        process.exit(1);
    }

    // Resolve jsonc-parser from the local mailx repo's node_modules so the
    // script doesn't require a global install. The repo root is two levels
    // up from bin/ — and we walk up further if invoked from elsewhere.
    let parseJsonc;
    for (const dir of [
        path.join(__dirname, "..", "node_modules", "jsonc-parser"),
        path.join(__dirname, "..", "..", "node_modules", "jsonc-parser"),
        path.join(process.cwd(), "node_modules", "jsonc-parser"),
    ]) {
        if (fs.existsSync(path.join(dir, "package.json"))) {
            const mod = await import(pathToFileURL(path.join(dir, "lib", "esm", "main.js")).href).catch(() => null) ||
                        await import("jsonc-parser").catch(() => null);
            if (mod) { parseJsonc = mod.parse; break; }
        }
    }
    if (!parseJsonc) {
        try { parseJsonc = (await import("jsonc-parser")).parse; }
        catch { console.error("jsonc-parser not found. Install or run from inside the mailx repo."); process.exit(1); }
    }

    const raw = fs.readFileSync(inputPath, "utf-8");
    const errors = [];
    const cfg = parseJsonc(raw, errors, { allowTrailingComma: true });
    if (errors.length) {
        console.error(`JSONC parse error: ${errors.map(e => JSON.stringify(e)).join(", ")}`);
        process.exit(1);
    }

    const accounts = cfg?.accounts || (Array.isArray(cfg) ? cfg : []);
    // Promote shared name to file-level when every entry has the same.
    const names = new Set(accounts.map(a => a.name).filter(Boolean));
    const sharedName = names.size === 1 ? [...names][0] : cfg?.name;
    const lean = accounts.map(a => denormalizeAccount(a, sharedName));
    const payload = sharedName ? { name: sharedName, accounts: lean } : { accounts: lean };
    const output = JSON.stringify(payload, null, 2) + "\n";

    if (inplace) {
        fs.writeFileSync(inputPath, output);
        console.error(`Wrote lean ${inputPath}`);
    } else {
        process.stdout.write(output);
    }
}

main().catch(e => { console.error(e); process.exit(1); });
