"use strict";
/**
 * credentials-file — Per-user JSON store for Amplitude project API keys.
 *
 * Lives at `~/.amplitude/wizard/credentials.json` (override the cache root
 * via `AMPLITUDE_WIZARD_CACHE_DIR`). Mode `0o600`. Keyed by a stable hash
 * of the project's install directory so multiple projects on one machine
 * stay isolated.
 *
 * ── Why not the OS keychain? ───────────────────────────────────────────
 * The previous implementation used `security` (macOS) and `secret-tool`
 * (Linux). It was written with `-T ''` (empty trusted-application list),
 * intending to suppress prompts. In practice an empty ACL means "no app
 * is on the allow-list," so every read triggered a "wizard wants to use
 * your keychain" dialog. Users are prompted on EVERY launch because
 * `credential-resolution.ts` reads the API key during startup.
 *
 * The Amplitude project API key is not a confidential credential — it
 * is a public ingestion key embedded in every Amplitude-instrumented
 * client SDK bundle (web JS, mobile binaries). The OAuth tokens already
 * sit in `~/.ampli.json` as a plain `0o600` file, and they are strictly
 * more sensitive than this. So a per-user file at `0o600` is the right
 * sensitivity match and aligns with how `gh`, `aws`, `vercel`, `doppler`,
 * `supabase` store their secrets.
 *
 * ── Schema ─────────────────────────────────────────────────────────────
 * {
 *   "version": 1,
 *   "projects": {
 *     "<sha256(installDir).slice(0,12)>": {
 *       "apiKey": "<amplitude api key>",
 *       "updatedAt": "<ISO 8601 timestamp>"
 *     }
 *   }
 * }
 *
 * Forward-compat: unknown top-level keys and unknown keys inside a project
 * entry are preserved on write so a future wizard version can add fields
 * without older versions stomping them.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.readCredential = readCredential;
exports.writeCredential = writeCredential;
exports.clearCredential = clearCredential;
const node_fs_1 = require("node:fs");
const zod_1 = require("zod");
const atomic_write_js_1 = require("./atomic-write.js");
const storage_paths_js_1 = require("./storage-paths.js");
const debug_js_1 = require("./debug.js");
/** Mode for the credentials file. Owner read/write only. */
const CREDENTIALS_FILE_MODE = 0o600;
/** Current schema version. Bump on breaking changes. */
const SCHEMA_VERSION = 1;
/**
 * Permissive schema: validates the shape we care about while letting
 * unrecognized keys pass through (`.passthrough()`). That way a newer
 * wizard can add fields without an older wizard erroring on load.
 */
const ProjectEntrySchema = zod_1.z
    .object({
    apiKey: zod_1.z.string(),
    updatedAt: zod_1.z.string().optional(),
})
    .passthrough();
const CredentialsFileSchema = zod_1.z
    .object({
    version: zod_1.z.number().int().positive().optional(),
    projects: zod_1.z.record(zod_1.z.string(), ProjectEntrySchema).optional(),
})
    .passthrough();
/**
 * Read and parse the credentials file. Returns an empty shell on miss
 * or corruption — never throws, because a corrupt credentials file
 * should never break the wizard's startup path. A corrupt file gets
 * overwritten on the next `writeCredential` call.
 */
function readFile() {
    const path = (0, storage_paths_js_1.getCredentialsFile)();
    if (!(0, node_fs_1.existsSync)(path)) {
        return { version: SCHEMA_VERSION, projects: {} };
    }
    try {
        const raw = (0, node_fs_1.readFileSync)(path, 'utf8');
        const parsed = JSON.parse(raw);
        const result = CredentialsFileSchema.safeParse(parsed);
        if (!result.success) {
            (0, debug_js_1.logToFile)(`[credentials-file] schema mismatch in ${path}, ignoring: ${result.error.message}`);
            return { version: SCHEMA_VERSION, projects: {} };
        }
        return result.data;
    }
    catch (err) {
        // JSON parse errors, EACCES, etc. — treat as "no credentials yet"
        // and let the next write recreate cleanly.
        (0, debug_js_1.logToFile)(`[credentials-file] failed to read ${path}: ${err instanceof Error ? err.message : String(err)}`);
        return { version: SCHEMA_VERSION, projects: {} };
    }
}
/** Read the API key for an install directory. Returns null on miss. */
function readCredential(installDir) {
    const data = readFile();
    const entry = data.projects?.[(0, storage_paths_js_1.projectHash)(installDir)];
    if (!entry)
        return null;
    const trimmed = entry.apiKey.trim();
    return trimmed.length > 0 ? trimmed : null;
}
/**
 * Persist the API key for an install directory. Creates the cache root
 * directory if needed. Throws on write failure — callers should catch
 * and fall back to `.env.local`.
 */
function writeCredential(installDir, apiKey) {
    // Ensure the cache root exists; `ensureDir` is mode 0o700 so the file's
    // 0o600 isn't undermined by a world-readable parent dir.
    (0, storage_paths_js_1.ensureDir)((0, storage_paths_js_1.getCacheRoot)());
    const data = readFile();
    const projects = { ...(data.projects ?? {}) };
    const hash = (0, storage_paths_js_1.projectHash)(installDir);
    const existing = projects[hash] ?? {};
    projects[hash] = {
        ...existing,
        apiKey,
        updatedAt: new Date().toISOString(),
    };
    const next = {
        ...data,
        version: data.version ?? SCHEMA_VERSION,
        projects,
    };
    (0, atomic_write_js_1.atomicWriteJSON)((0, storage_paths_js_1.getCredentialsFile)(), next, { mode: CREDENTIALS_FILE_MODE });
}
/**
 * Remove the entry for an install directory. No-op if the file or entry
 * doesn't exist. Never throws — logout should always succeed even if
 * the credentials file is corrupted or unwritable.
 */
function clearCredential(installDir) {
    const path = (0, storage_paths_js_1.getCredentialsFile)();
    if (!(0, node_fs_1.existsSync)(path))
        return;
    const data = readFile();
    const hash = (0, storage_paths_js_1.projectHash)(installDir);
    if (!data.projects?.[hash])
        return;
    const projects = { ...data.projects };
    delete projects[hash];
    const next = {
        ...data,
        version: data.version ?? SCHEMA_VERSION,
        projects,
    };
    try {
        (0, atomic_write_js_1.atomicWriteJSON)(path, next, { mode: CREDENTIALS_FILE_MODE });
    }
    catch (err) {
        (0, debug_js_1.logToFile)(`[credentials-file] failed to clear ${path}: ${err instanceof Error ? err.message : String(err)}`);
    }
}
