import { readFileSync } from "fs";
import { randomUUID, timingSafeEqual } from "node:crypto";
import { config } from "../config.js";
import { deriveMcpAuthToken, hexToKey, mnemonicToSeed } from "../crypto/key_derivation.js";
const DEFAULT_SESSION_TTL_MS = 15 * 60 * 1000;
export class OAuthKeySessionStore {
    sessions = new Map();
    ttlMs;
    constructor(ttlMs = DEFAULT_SESSION_TTL_MS) {
        this.ttlMs = ttlMs;
    }
    create(nowMs = Date.now()) {
        this.cleanup(nowMs);
        const token = randomUUID();
        this.sessions.set(token, nowMs + this.ttlMs);
        return token;
    }
    isValid(token, nowMs = Date.now()) {
        if (!token)
            return false;
        const expiresAt = this.sessions.get(token);
        if (expiresAt == null)
            return false;
        if (expiresAt <= nowMs) {
            this.sessions.delete(token);
            return false;
        }
        return true;
    }
    cleanup(nowMs = Date.now()) {
        for (const [token, expiresAt] of this.sessions.entries()) {
            if (expiresAt <= nowMs)
                this.sessions.delete(token);
        }
    }
}
export function getConfiguredOAuthKeyToken() {
    const keyFilePath = config.encryption.keyFilePath || "";
    const mnemonic = config.encryption.mnemonic || "";
    const mnemonicPassphrase = config.encryption.mnemonicPassphrase || "";
    if (keyFilePath) {
        const raw = readFileSync(keyFilePath, "utf8").trim();
        return deriveMcpAuthToken(hexToKey(raw));
    }
    if (mnemonic) {
        const seed = mnemonicToSeed(mnemonic, mnemonicPassphrase);
        return deriveMcpAuthToken(seed);
    }
    return null;
}
export function deriveOAuthKeyTokenFromCredentials(credentials) {
    const privateKeyHex = credentials.privateKeyHex?.trim() || "";
    const mnemonic = credentials.mnemonic?.trim() || "";
    const mnemonicPassphrase = credentials.mnemonicPassphrase || "";
    if (privateKeyHex) {
        return deriveMcpAuthToken(hexToKey(privateKeyHex));
    }
    if (mnemonic) {
        const seed = mnemonicToSeed(mnemonic, mnemonicPassphrase);
        return deriveMcpAuthToken(seed);
    }
    throw new Error("Provide private_key_hex or mnemonic");
}
export function isOauthKeyCredentialValid(credentials) {
    const bearerToken = credentials.bearerToken?.trim() || "";
    const configuredBearer = (process.env.NEOTOMA_BEARER_TOKEN || "").trim();
    if (bearerToken) {
        if (!configuredBearer) {
            return {
                ok: false,
                reason: "Bearer token authentication is not configured on this server. Set NEOTOMA_BEARER_TOKEN or use key credentials.",
            };
        }
        const expected = Buffer.from(configuredBearer, "utf8");
        const provided = Buffer.from(bearerToken, "utf8");
        if (expected.length !== provided.length || !timingSafeEqual(expected, provided)) {
            return { ok: false, reason: "Provided bearer token does not match configured token" };
        }
        return { ok: true };
    }
    const expectedToken = getConfiguredOAuthKeyToken();
    if (!expectedToken) {
        return {
            ok: false,
            reason: "OAuth key-auth is required but no key source is configured. Set NEOTOMA_KEY_FILE_PATH or NEOTOMA_MNEMONIC, or use NEOTOMA_BEARER_TOKEN without OAuth.",
        };
    }
    let providedToken;
    try {
        providedToken = deriveOAuthKeyTokenFromCredentials(credentials);
    }
    catch (error) {
        return { ok: false, reason: error?.message || "Invalid key credentials" };
    }
    const expected = Buffer.from(expectedToken, "utf8");
    const provided = Buffer.from(providedToken, "utf8");
    if (expected.length !== provided.length) {
        return { ok: false, reason: "Provided key does not match configured key" };
    }
    if (!timingSafeEqual(expected, provided)) {
        return { ok: false, reason: "Provided key does not match configured key" };
    }
    return { ok: true };
}
export function normalizeOauthNextPath(nextRaw) {
    if (!nextRaw)
        return "/mcp/oauth/authorize";
    const trimmed = nextRaw.trim();
    if (!trimmed)
        return "/mcp/oauth/authorize";
    if (!trimmed.startsWith("/"))
        return "/mcp/oauth/authorize";
    if (!trimmed.startsWith("/mcp/oauth/"))
        return "/mcp/oauth/authorize";
    return trimmed;
}
//# sourceMappingURL=oauth_key_gate.js.map