/**
 * Pure REST handler for `GET /api/settings` and `PUT /api/settings`.
 *
 * Settings are stored in the machine's global `settings.json` under the
 * `"observational-memory"` namespace (see `src/memory/config.ts`).
 *
 * GET  /api/settings                        → returns full observational-memory config
 * PUT  /api/settings                        → updates a single key via `{ key, value }` body
 *
 * All paths are machine-level — no session context needed.
 */
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { dirname, join } from "node:path";
import lockfile from "proper-lockfile";
import { getAgentDir } from "../../sdk/coding-agent/index.js";
import { loadConfig } from "../../memory/config.js";
import { BadRequestError } from "./errors.js";
/** Recognised setting keys (dot-separated path within the config object). */
const ALLOWED_KEYS = new Set([
    "observationThresholdTokens",
    "compactionThresholdTokens",
    "reflectionThresholdTokens",
]);
/** Sane bounds for token thresholds. */
const MIN_TOKENS = 1_000;
const MAX_TOKENS = 1_000_000;
// ---------------------------------------------------------------------------
// File helpers (inlined to avoid pulling in the full SettingsManager)
// ---------------------------------------------------------------------------
function acquireLockSyncWithRetry(path) {
    const maxAttempts = 10;
    const delayMs = 20;
    let lastError;
    for (let attempt = 1; attempt <= maxAttempts; attempt++) {
        try {
            return lockfile.lockSync(path, { realpath: false });
        }
        catch (error) {
            const code = typeof error === "object" && error !== null && "code" in error
                ? String(error.code)
                : undefined;
            if (code !== "ELOCKED" || attempt === maxAttempts) {
                throw error;
            }
            lastError = error;
            const start = Date.now();
            while (Date.now() - start < delayMs) {
                // busy-wait; synchronous by design
            }
        }
    }
    throw lastError ?? new Error("Failed to acquire settings lock");
}
/**
 * Atomically read-modify-write the global `settings.json` under the
 * `"observational-memory"` namespace.
 */
function withObservationalMemoryLock(fn) {
    const path = join(getAgentDir(), "settings.json");
    const dir = dirname(path);
    let release;
    try {
        const fileExists = existsSync(path);
        if (fileExists) {
            release = acquireLockSyncWithRetry(path);
        }
        const raw = fileExists ? readFileSync(path, "utf-8") : "{}";
        const parsed = JSON.parse(raw);
        const ns = (parsed["observational-memory"] ?? {});
        const next = fn(ns);
        if (next !== undefined) {
            parsed["observational-memory"] = { ...ns, ...next };
            if (!existsSync(dir)) {
                mkdirSync(dir, { recursive: true });
            }
            if (!release) {
                release = acquireLockSyncWithRetry(path);
            }
            writeFileSync(path, JSON.stringify(parsed, null, 2), "utf-8");
        }
    }
    finally {
        if (release) {
            release();
        }
    }
}
// ---------------------------------------------------------------------------
// GET handler
// ---------------------------------------------------------------------------
export function handleGetSettings(cwd) {
    const config = loadConfig(cwd);
    return {
        observationThresholdTokens: config.observationThresholdTokens,
        compactionThresholdTokens: config.compactionThresholdTokens,
        reflectionThresholdTokens: config.reflectionThresholdTokens,
        passive: config.passive,
        debugLog: config.debugLog,
    };
}
// ---------------------------------------------------------------------------
// PUT handler
// ---------------------------------------------------------------------------
export function handlePutSettings(cwd, body) {
    // Validate key
    if (typeof body.key !== "string" || !ALLOWED_KEYS.has(body.key)) {
        throw new BadRequestError(`settings key must be one of: ${[...ALLOWED_KEYS].join(", ")}`);
    }
    const key = body.key;
    // Validate value
    if (typeof body.value !== "number" || !Number.isInteger(body.value)) {
        throw new BadRequestError("settings value must be an integer");
    }
    if (body.value < MIN_TOKENS || body.value > MAX_TOKENS) {
        throw new BadRequestError(`token threshold must be between ${MIN_TOKENS.toLocaleString()} and ${MAX_TOKENS.toLocaleString()}`);
    }
    const value = body.value;
    // Persist
    withObservationalMemoryLock((current) => {
        return { ...current, [key]: value };
    });
    return { ok: true, key, value };
}
