import neo4j from "neo4j-driver";
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
let driver = null;
function readPassword() {
    if (process.env.NEO4J_PASSWORD)
        return process.env.NEO4J_PASSWORD;
    // Task 207 — five segments up from the compiled file
    // (`platform/plugins/memory/mcp/dist/lib/neo4j.js`) reaches `platform/`,
    // where the installer writes `config/.neo4j-password`. The previous
    // four-segment hop landed at `platform/plugins/`, where no password
    // file exists; that miscalculation was masked while the per-spawn
    // `.mcp.json` injected `PLATFORM_ROOT` on every MCP child. Task 207
    // drops that JSON for specialists, so this fallback is the new
    // load-bearing path when `process.env.PLATFORM_ROOT` is unset.
    const passwordFile = resolve(process.env.PLATFORM_ROOT ?? resolve(import.meta.dirname, "../../../../.."), "config/.neo4j-password");
    try {
        return readFileSync(passwordFile, "utf-8").trim();
    }
    catch {
        throw new Error(`Neo4j password not found. Expected at ${passwordFile} or in NEO4J_PASSWORD env var.`);
    }
}
export function getDriver() {
    if (!driver) {
        // NEO4J_URI must be explicit. Silent default to:7687 was the
        // vector for the orphan data in Maxy's DB from a realagent install.
        const uri = process.env.NEO4J_URI;
        if (!uri) {
            throw new Error("[memory] NEO4J_URI unset — refusing to default to bolt://localhost:7687");
        }
        const user = process.env.NEO4J_USER ?? "neo4j";
        const password = readPassword();
        console.error(`[memory] resolved neo4j_uri=${uri}`);
        driver = neo4j.driver(uri, neo4j.auth.basic(user, password));
    }
    return driver;
}
export function getSession() {
    return getDriver().session();
}
export async function closeDriver() {
    if (driver) {
        await driver.close();
        driver = null;
    }
}
/**
 * Task 237 — walk `:HAS_TRANSCRIPT` from a `:Conversation` (trashed or not)
 * by elementId. Required by `memory-empty-trash`'s `:AdminConversation`
 * defense-in-depth: a `:Trashed :Conversation` has its `sessionId`
 * nulled (the unique-key snapshot moves to `_trashedKeys`), so
 * sessionId-keyed lookups can't find it. elementId survives the
 * trash transition unchanged.
 *
 * Account-scoped: `:Conversation.accountId` is NOT in the trash
 * unique-key snapshot, so it stays live and we filter on it for
 * defense-in-depth even though the caller already selected the candidate
 * from its own account-scoped sweep.
 */
export async function getClaudeSessionIdsByConversationElementId(elementId, accountId) {
    const session = getSession();
    try {
        const result = await session.run(`MATCH (c) WHERE elementId(c) = $elementId AND c.accountId = $accountId
       MATCH (c)-[:HAS_TRANSCRIPT]->(t:ClaudeSession)
       RETURN t.claudeSessionId AS claudeSessionId
       ORDER BY t.createdAt ASC`, { elementId, accountId });
        return result.records
            .map((r) => r.get("claudeSessionId"))
            .filter((v) => typeof v === "string" && v.length > 0);
    }
    finally {
        await session.close();
    }
}
//# sourceMappingURL=neo4j.js.map