/**
 * Attaches Cloudinary authentication to every outgoing request.
 *
 * Priority:
 *   1. OAuth2 Bearer token (if provided via security source)
 *   2. Basic auth (apiKey:apiSecret) — works for all endpoints including upload
 *
 * Also preprocesses JSON request bodies to convert local `file://` URIs
 * into base64 data URIs so uploads work with local file paths.
 */
export class CloudinaryAuthHook {
    config;
    constructor(config) {
        this.config = config;
    }
    async beforeRequest(hookCtx, request) {
        const security = await this.resolveSecurity(hookCtx.securitySource);
        // 1. OAuth2 Bearer token takes priority
        if (this.isOAuth2Token(security)) {
            request.headers.set("Authorization", `Bearer ${security.oauth2}`);
            return request;
        }
        // 2. Resolve API key / secret (env → security overrides)
        const { apiKey, apiSecret } = this.resolveCredentials(security);
        if (!apiKey || !apiSecret) {
            return request;
        }
        // Set Basic auth header
        const encoded = Buffer.from(`${apiKey}:${apiSecret}`).toString("base64");
        request.headers.set("Authorization", `Basic ${encoded}`);
        // Preprocess JSON bodies (e.g. convert file:// URIs to base64)
        request = await this.preprocessBody(request);
        return request;
    }
    // --- Security resolution ---
    async resolveSecurity(source) {
        return typeof source === "function" ? source() : source;
    }
    isOAuth2Token(security) {
        return (typeof security === "object"
            && security !== null
            && "oauth2" in security
            && typeof security.oauth2 === "string"
            && security.oauth2.length > 0);
    }
    resolveCredentials(security) {
        let { apiKey, apiSecret } = this.config;
        if (!security || typeof security !== "object") {
            return { apiKey, apiSecret };
        }
        const sec = security;
        // cloudinaryAuth format (camelCase and snake_case)
        if (sec["cloudinaryAuth"]) {
            const auth = sec["cloudinaryAuth"];
            apiKey = auth.apiKey || auth.api_key || apiKey;
            apiSecret = auth.apiSecret || auth.api_secret || apiSecret;
        }
        // Standard SDK security format (used by MCP server)
        else if ("apiKey" in sec || "apiSecret" in sec) {
            apiKey = sec["apiKey"] || apiKey;
            apiSecret = sec["apiSecret"] || apiSecret;
        }
        return { apiKey, apiSecret };
    }
    // --- Body preprocessing ---
    /**
     * For JSON requests, converts local `file://` URIs to inline base64
     * data URIs so Cloudinary can accept the upload payload.
     */
    async preprocessBody(request) {
        if (request.headers.get("Content-Type") !== "application/json") {
            return request;
        }
        const body = await request.json();
        if (typeof body?.file === "string"
            && body.file.startsWith("file://")) {
            body.file = await this.readLocalFileAsDataUri(body.file);
            return new Request(request, { body: JSON.stringify(body) });
        }
        return new Request(request, { body: JSON.stringify(body) });
    }
    /**
     * Reads a local file and returns it as a base64 data URI.
     */
    async readLocalFileAsDataUri(fileUrl) {
        const filePath = fileUrl.replace(/^file:\/\//, "");
        try {
            const { readFile } = await import("node:fs/promises");
            const buffer = await readFile(filePath);
            const base64 = buffer.toString("base64");
            return `data:application/octet-stream;base64,${base64}`;
        }
        catch (error) {
            throw new Error(`Failed to read file: ${filePath}. ${error instanceof Error ? error.message : String(error)}`);
        }
    }
}
//# sourceMappingURL=cloudinaryAuthHook.js.map