/**
 * Error classification for client-facing tool error messages.
 *
 * Cluster 6 (2026-05-31 consolidated report): many tools collapsed every
 * failure into the opaque string "An error occurred", giving agents/users
 * nothing actionable. This module maps a thrown error — including the
 * imapflow-style server rejections that expose `responseText` /
 * `responseStatus` / `code` — into a small set of stable, distinguishable
 * categories plus a concise, actionable message.
 *
 * Internal stack detail is intentionally NOT included in the returned message;
 * callers should log the raw error separately (the dispatcher already does).
 */
/**
 * A connection/credential problem the OPERATOR must fix (e.g. wrong Bridge
 * password, Bridge not running). Its message is written for a human and is
 * surfaced VERBATIM to the MCP client (safeErrorMessage passes it through), so
 * the agent — and through it the user — gets actionable guidance instead of an
 * opaque "IMAP operation failed". Thrown from the connection chokepoints; not
 * reclassified.
 */
export class ConnectionStateError extends Error {
    constructor(message) {
        super(message);
        this.name = "ConnectionStateError";
    }
}
function readImapShape(error) {
    if (typeof error !== "object" || error === null)
        return {};
    const e = error;
    return {
        responseText: e.responseText,
        responseStatus: e.responseStatus,
        code: e.code,
        authenticationFailed: e.authenticationFailed,
    };
}
function asString(v) {
    return typeof v === "string" ? v : "";
}
/**
 * Classify an arbitrary thrown value into a category + actionable message.
 *
 * @param error The thrown value.
 * @param context Optional hint about the resource being acted on (e.g. the
 *   folder or label name) so a not-found message can name it precisely.
 */
export function classifyError(error, context) {
    const shape = readImapShape(error);
    const rawMsg = error instanceof Error ? error.message : asString(error);
    const responseText = asString(shape.responseText);
    const code = asString(shape.code);
    // Single lowercase haystack covering message + server response text + code.
    const hay = `${rawMsg} ${responseText} ${code}`.toLowerCase();
    const folderLabel = context?.folder;
    // ── not found ────────────────────────────────────────────────────────────
    // imapflow throws on SELECT of a missing mailbox with responseText
    // "NONEXISTENT" or a textual "Mailbox doesn't exist".
    if (hay.includes("nonexistent") ||
        hay.includes("mailbox doesn't exist") ||
        hay.includes("mailbox does not exist") ||
        hay.includes("does not exist") ||
        hay.includes("no such mailbox") ||
        hay.includes("not found")) {
        return {
            category: "not_found",
            message: folderLabel
                ? `Folder/label '${folderLabel}' not found.`
                : "The requested folder or label was not found.",
        };
    }
    // ── name conflict (folder/label already exists) ──────────────────────────
    // Proton shares ONE namespace across folders and labels, so creating a name
    // that exists in either returns the Proton 409 (Code=2500) / IMAP
    // ALREADYEXISTS. Surface it as actionable instead of "internal".
    if (hay.includes("alreadyexists") ||
        hay.includes("already exists") ||
        hay.includes("code=2500")) {
        return {
            category: "conflict",
            message: folderLabel
                ? `A folder or label named '${folderLabel}' already exists. Proton shares one namespace across folders and labels, so the name can't be reused.`
                : "A folder or label with that name already exists (Proton shares one namespace across folders and labels).",
        };
    }
    // ── auth ─────────────────────────────────────────────────────────────────
    if (shape.authenticationFailed === true ||
        code === "AUTHENTICATIONFAILED" ||
        code === "EAUTH" ||
        hay.includes("authenticationfailed") ||
        hay.includes("authentication failed") ||
        hay.includes("invalid credentials") ||
        hay.includes("login failed") ||
        // Proton Bridge's exact local-login rejections.
        hay.includes("incorrect login credentials") ||
        hay.includes("no such user") ||
        hay.includes("invalid username or password")) {
        return {
            category: "auth",
            message: "Mail server authentication failed. Check the mailbox credentials in Settings.",
        };
    }
    // ── timeout ──────────────────────────────────────────────────────────────
    // Checked before connection: a timeout often also reads as a conn issue.
    if (code === "ETIMEDOUT" ||
        code === "ESOCKETTIMEDOUT" ||
        hay.includes("timed out") ||
        hay.includes("timeout")) {
        return {
            category: "timeout",
            message: "The mail server operation timed out. Please retry.",
        };
    }
    // ── connection lost / unavailable ─────────────────────────────────────────
    if ((error instanceof Error && error.name === "IMAPNotConnectedError") ||
        code === "ECONNRESET" ||
        code === "ECONNREFUSED" ||
        code === "EPIPE" ||
        code === "ENOTFOUND" ||
        code === "EHOSTUNREACH" ||
        hay.includes("connection unavailable") ||
        hay.includes("not connected") ||
        hay.includes("connection closed") ||
        hay.includes("connection lost") ||
        hay.includes("socket")) {
        return {
            category: "connection",
            message: "Lost connection to the mail server. The connection will be retried — please try again.",
        };
    }
    // ── fallthrough ────────────────────────────────────────────────────────────
    return {
        category: "internal",
        message: "An internal error occurred. See server logs for details.",
    };
}
/**
 * Returns true when the error looks like a "mailbox/folder does not exist"
 * rejection from the IMAP server. Used by read tools to convert an opaque
 * server rejection into a precise, named not-found error up front.
 */
export function isFolderNotFoundError(error) {
    return classifyError(error).category === "not_found";
}
//# sourceMappingURL=error-classify.js.map