/**
 * SimpleLogin REST client.
 *
 * SimpleLogin is the alias-management service owned by Proton (since 2022).
 * It exposes a public HTTP API documented at:
 *   https://github.com/simple-login/app/blob/master/docs/api.md
 *
 * Authentication uses a single `Authentication:` header (note the non-standard
 * spelling — that's how SimpleLogin designed it). API keys are scoped per
 * account and generated from the SimpleLogin dashboard.
 *
 * All requests are fire-and-forget from mailpouch's perspective; we do
 * not persist alias state locally — the agent always reads fresh from the API.
 */
import { logger } from "../utils/logger.js";
const DEFAULT_BASE_URL = "https://app.simplelogin.io";
const REQUEST_TIMEOUT_MS = 15_000;
export class SimpleLoginService {
    baseUrl;
    apiKey;
    constructor(apiKey, baseUrl = DEFAULT_BASE_URL) {
        this.apiKey = apiKey;
        this.baseUrl = baseUrl.replace(/\/+$/, "");
    }
    /** True when the service has been configured with a non-empty API key. */
    isConfigured() {
        return !!this.apiKey;
    }
    /**
     * CRED-012: strip secret-shaped substrings from a server-controlled error
     * string. Replaces (a) the exact configured API key and (b) any long
     * opaque token (SimpleLogin keys are ~40+ char base62/hex blobs) with a
     * `[redacted]` placeholder so they never reach the log or the MCP response.
     */
    redactSecrets(message) {
        let out = message;
        if (this.apiKey) {
            out = out.split(this.apiKey).join("[redacted]");
        }
        // Generic catch-all for opaque tokens: 24+ chars of base62/_- with no
        // whitespace. Errs toward over-redaction; human-readable messages rarely
        // contain unbroken 24-char alphanumeric runs.
        out = out.replace(/[A-Za-z0-9_-]{24,}/g, "[redacted]");
        return out;
    }
    async request(path, init = {}) {
        if (!this.apiKey) {
            throw new Error("SimpleLogin: no API key configured. Set simpleloginApiKey in Settings → Aliases, " +
                "or obtain one at https://app.simplelogin.io/dashboard/api_key");
        }
        const controller = new AbortController();
        const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
        try {
            const res = await fetch(`${this.baseUrl}${path}`, {
                ...init,
                headers: {
                    "Authentication": this.apiKey,
                    "Content-Type": "application/json",
                    ...(init.headers ?? {}),
                },
                signal: controller.signal,
            });
            const text = await res.text();
            if (!res.ok) {
                // SimpleLogin returns structured errors: { error: "message" }
                let message = `${res.status} ${res.statusText}`;
                try {
                    const parsed = JSON.parse(text);
                    if (parsed.error)
                        message = parsed.error;
                }
                catch { /* non-JSON body — use status text */ }
                // CRED-012: the upstream `error` body is server-controlled and reaches
                // the MCP response + the JSONL log, whose redaction is key-based (not
                // value-pattern). Defensively scrub our own API key and any
                // token-shaped substrings before raising so a hostile error string
                // can't smuggle a secret into ~/.mailpouch.log.
                message = this.redactSecrets(message);
                throw new Error(`SimpleLogin ${init.method ?? "GET"} ${path} → ${message}`);
            }
            if (!text)
                return undefined;
            return JSON.parse(text);
        }
        finally {
            clearTimeout(timer);
        }
    }
    /**
     * List all aliases on the account. Paginates transparently.
     * `pageSize` is a caller-enforced cap on total results (not a SimpleLogin parameter).
     */
    async listAliases(pageSize = 200) {
        logger.debug("SimpleLogin: listing aliases", "SimpleLoginService", { pageSize });
        const collected = [];
        let page = 0;
        // SimpleLogin returns { aliases: SimpleLoginAlias[] }. An empty array signals end.
        while (collected.length < pageSize) {
            const body = await this.request(`/api/v2/aliases?page_id=${page}`);
            const batch = body.aliases ?? [];
            if (batch.length === 0)
                break;
            collected.push(...batch);
            if (collected.length >= pageSize)
                break;
            page += 1;
            // Safety: never fetch more than 50 pages — 20/page × 50 = 1000 aliases
            if (page >= 50)
                break;
        }
        return collected.slice(0, pageSize);
    }
    /** Create a random alias. Default mode is "uuid" (long, least guessable). */
    async createRandomAlias(opts = {}) {
        const params = new URLSearchParams();
        if (opts.hostname)
            params.set("hostname", opts.hostname);
        const qs = params.toString() ? `?${params.toString()}` : "";
        return this.request(`/api/alias/random/new${qs}`, {
            method: "POST",
            body: JSON.stringify({
                mode: opts.mode ?? "uuid",
                note: opts.note ?? null,
            }),
        });
    }
    /**
     * Create a custom alias. Requires a `signedSuffix` obtained from
     * /api/v5/alias/options — we expose that via {@link getAliasOptions} for the
     * caller to populate the UI.
     */
    async createCustomAlias(opts) {
        const params = new URLSearchParams();
        if (opts.hostname)
            params.set("hostname", opts.hostname);
        const qs = params.toString() ? `?${params.toString()}` : "";
        return this.request(`/api/v3/alias/custom/new${qs}`, {
            method: "POST",
            body: JSON.stringify({
                alias_prefix: opts.aliasPrefix,
                signed_suffix: opts.signedSuffix,
                mailbox_ids: opts.mailboxIds,
                note: opts.note,
                name: opts.name,
            }),
        });
    }
    /** Fetch the prefixes / suffixes available for custom-alias creation. */
    async getAliasOptions(hostname) {
        const params = new URLSearchParams();
        if (hostname)
            params.set("hostname", hostname);
        const qs = params.toString() ? `?${params.toString()}` : "";
        return this.request(`/api/v5/alias/options${qs}`);
    }
    async toggleAlias(aliasId) {
        return this.request(`/api/aliases/${aliasId}/toggle`, { method: "POST" });
    }
    async deleteAlias(aliasId) {
        await this.request(`/api/aliases/${aliasId}`, { method: "DELETE" });
    }
    async getAliasActivities(aliasId, pageSize = 50) {
        const collected = [];
        let page = 0;
        while (collected.length < pageSize) {
            const body = await this.request(`/api/aliases/${aliasId}/activities?page_id=${page}`);
            const batch = body.activities ?? [];
            if (batch.length === 0)
                break;
            collected.push(...batch);
            if (collected.length >= pageSize)
                break;
            page += 1;
            if (page >= 20)
                break;
        }
        return collected.slice(0, pageSize);
    }
    async updateAlias(aliasId, patch) {
        await this.request(`/api/aliases/${aliasId}`, {
            method: "PUT",
            body: JSON.stringify(patch),
        });
    }
}
//# sourceMappingURL=simplelogin-service.js.map