import fs from "node:fs";
import { google } from "googleapis";
import { loadGoogleTokens, saveGoogleTokens, } from "../config/index.js";
import { resolveGoogleOAuthCredentials } from "../google/oauth-credentials.js";
/**
 * Create a pre-configured Gmail API client with auto-refreshing OAuth2 tokens.
 */
export function createGmailClient(config) {
    const tokens = loadGoogleTokens();
    const { clientId, clientSecret } = resolveGoogleOAuthCredentials(config);
    const oauth2Client = new google.auth.OAuth2(clientId, clientSecret);
    oauth2Client.setCredentials({
        access_token: tokens.access_token,
        refresh_token: tokens.refresh_token,
        token_type: tokens.token_type,
        expiry_date: tokens.expiry_date,
    });
    oauth2Client.on("tokens", (newTokens) => {
        const existing = loadGoogleTokens();
        saveGoogleTokens({
            ...existing,
            access_token: newTokens.access_token ?? existing.access_token,
            refresh_token: newTokens.refresh_token ?? existing.refresh_token,
            expiry_date: newTokens.expiry_date ?? existing.expiry_date,
        });
    });
    return google.gmail({ version: "v1", auth: oauth2Client });
}
/**
 * Find a header value from a Gmail message's header list (case-insensitive).
 */
export function getHeader(headers, name) {
    const h = (headers ?? []).find((x) => (x.name ?? "").toLowerCase() === name.toLowerCase());
    return h?.value ?? undefined;
}
/** RFC 2047 encode a header value if it contains non-ASCII characters. */
export function mimeEncodeHeader(value) {
    // eslint-disable-next-line no-control-regex
    if (/^[\x00-\x7F]*$/.test(value))
        return value;
    const encoded = Buffer.from(value, "utf-8").toString("base64");
    return `=?UTF-8?B?${encoded}?=`;
}
function buildCommonHeaders(options) {
    const headers = [
        `From: ${options.from}`,
        `To: ${options.to}`,
    ];
    if (options.cc)
        headers.push(`Cc: ${options.cc}`);
    if (options.bcc)
        headers.push(`Bcc: ${options.bcc}`);
    headers.push(`Subject: ${mimeEncodeHeader(options.subject)}`);
    if (options.inReplyTo)
        headers.push(`In-Reply-To: ${options.inReplyTo}`);
    if (options.references)
        headers.push(`References: ${options.references}`);
    headers.push("MIME-Version: 1.0");
    return headers;
}
function buildTextPart(text) {
    return [
        "Content-Type: text/plain; charset=utf-8",
        "Content-Transfer-Encoding: base64",
        "",
        Buffer.from(text, "utf-8").toString("base64"),
    ];
}
function buildHtmlPart(html) {
    return [
        "Content-Type: text/html; charset=utf-8",
        "Content-Transfer-Encoding: base64",
        "",
        Buffer.from(html, "utf-8").toString("base64"),
    ];
}
function buildAlternativePart(text, html, boundary) {
    const lines = [];
    lines.push(`Content-Type: multipart/alternative; boundary="${boundary}"`);
    lines.push("");
    lines.push(`--${boundary}`);
    lines.push(...buildTextPart(text));
    lines.push(`--${boundary}`);
    lines.push(...buildHtmlPart(html));
    lines.push(`--${boundary}--`);
    return lines;
}
function buildAttachmentParts(attachments, boundary) {
    const parts = [];
    for (const att of attachments) {
        let b64Data = null;
        const filename = att.filename ?? "attachment";
        const mimeType = att.mimeType ?? "application/octet-stream";
        if (att.localPath && fs.existsSync(att.localPath)) {
            b64Data = fs.readFileSync(att.localPath).toString("base64");
        }
        else if (att.base64) {
            b64Data = att.base64;
        }
        else if (att.url) {
            parts.push(`--${boundary}`);
            parts.push("Content-Type: text/plain; charset=utf-8");
            parts.push("");
            parts.push(`[Attachment: ${filename}] ${att.url}`);
            continue;
        }
        if (b64Data) {
            parts.push(`--${boundary}`);
            parts.push(`Content-Type: ${mimeType}`);
            parts.push("Content-Transfer-Encoding: base64");
            parts.push(`Content-Disposition: attachment; filename="${filename}"`);
            parts.push("");
            parts.push(b64Data);
        }
    }
    return parts;
}
/**
 * Create a base64url-encoded raw email for the Gmail API.
 * Supports cc/bcc, RFC 2047 subject encoding, HTML body (multipart/alternative),
 * and optional file/base64 attachments via MIME multipart.
 */
export function createRawEmail(options) {
    const attachments = options.attachments && options.attachments.length > 0
        ? options.attachments
        : undefined;
    const htmlBody = options.htmlBody;
    const headers = buildCommonHeaders(options);
    // Simple text-only email (no HTML, no attachments)
    if (!htmlBody && !attachments) {
        headers.push("Content-Type: text/plain; charset=utf-8");
        headers.push("Content-Transfer-Encoding: base64");
        const bodyEncoded = Buffer.from(options.body, "utf-8").toString("base64");
        const raw = `${headers.join("\r\n")}\r\n\r\n${bodyEncoded}`;
        return Buffer.from(raw, "utf-8").toString("base64url");
    }
    // HTML without attachments: multipart/alternative
    if (htmlBody && !attachments) {
        const altBoundary = `----VisionClawAlt${Date.now()}`;
        headers.push(`Content-Type: multipart/alternative; boundary="${altBoundary}"`);
        const parts = [...headers, ""];
        parts.push(`--${altBoundary}`);
        parts.push(...buildTextPart(options.body));
        parts.push(`--${altBoundary}`);
        parts.push(...buildHtmlPart(htmlBody));
        parts.push(`--${altBoundary}--`);
        return Buffer.from(parts.join("\r\n")).toString("base64url");
    }
    // With attachments: multipart/mixed
    const mixedBoundary = `----VisionClaw${Date.now()}`;
    headers.push(`Content-Type: multipart/mixed; boundary="${mixedBoundary}"`);
    const parts = [...headers, ""];
    if (htmlBody) {
        // HTML + attachments: nested multipart/alternative inside multipart/mixed
        const altBoundary = `----VisionClawAlt${Date.now()}`;
        parts.push(`--${mixedBoundary}`);
        parts.push(...buildAlternativePart(options.body, htmlBody, altBoundary));
    }
    else {
        // Plain text + attachments
        parts.push(`--${mixedBoundary}`);
        parts.push(...buildTextPart(options.body));
    }
    if (attachments) {
        parts.push(...buildAttachmentParts(attachments, mixedBoundary));
    }
    parts.push(`--${mixedBoundary}--`);
    return Buffer.from(parts.join("\r\n")).toString("base64url");
}
/**
 * Fetch reply threading headers (Message-Id, References) from an existing Gmail message.
 * Returns { inReplyTo, references } ready for use in createRawEmail.
 */
export async function fetchReplyHeaders(gmail, messageId) {
    const original = await gmail.users.messages.get({
        userId: "me",
        id: messageId,
        format: "metadata",
        metadataHeaders: ["Message-Id", "References"],
    });
    const headers = original.data.payload?.headers ?? [];
    const messageIdHeader = getHeader(headers, "Message-Id");
    const referencesHeader = getHeader(headers, "References");
    return {
        inReplyTo: messageIdHeader ?? undefined,
        references: referencesHeader
            ? `${referencesHeader} ${messageIdHeader ?? ""}`.trim()
            : messageIdHeader ?? undefined,
    };
}
/**
 * Build a reply subject from an original subject.
 * Prepends "Re: " if not already present.
 */
export function buildReplySubject(originalSubject) {
    return originalSubject.toLowerCase().startsWith("re:")
        ? originalSubject
        : `Re: ${originalSubject}`;
}
//# sourceMappingURL=gmail-utils.js.map