/**
 * Spectral configuration: persisted Aexol MCP credentials.
 *
 * Stored at ~/.spectral/config.json with mode 0600 (user read/write only).
 * The directory is created with mode 0700.
 *
 * Keep this module zero-side-effect so it can be imported from both the
 * CLI entry (pre-flight check) and the spectral extension.
 *
 * `getConfigDir()` honors `SPECTRAL_CONFIG_DIR` so tests can redirect to a
 * temporary directory without touching the real `~/.spectral`.
 */
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
import { homedir } from "node:os";
import { dirname, join } from "node:path";
export const DEFAULT_API_URL = "https://api.aexol.ai/mcp";
export const TEAM_API_KEY_PREFIX = "sk-aexol-team-";
/**
 * Resolve the directory used to store Spectral configuration.
 *
 * Honors `SPECTRAL_CONFIG_DIR` first (used by tests and CI), then falls back
 * to `<HOMEDIR>/.spectral`. Computed lazily on every call so test-time env
 * mutation works without re-importing the module.
 */
export function getConfigDir() {
    return process.env.SPECTRAL_CONFIG_DIR ?? join(homedir(), ".spectral");
}
/** Path to the config JSON file. Computed lazily — see `getConfigDir`. */
export function getConfigFile() {
    return join(getConfigDir(), "config.json");
}
/**
 * Backwards-compatible aliases for code that imported the old top-level
 * constants. They eagerly resolve at import time, so they reflect the env
 * state at startup. New code should call `getConfigDir()` / `getConfigFile()`
 * directly so test-time env overrides take effect.
 */
export const CONFIG_DIR = getConfigDir();
export const CONFIG_FILE = getConfigFile();
/** Resolve the effective API URL: env wins, then stored value, then default. */
export function getApiUrl(stored) {
    return process.env.SPECTRAL_MCP_URL ?? stored ?? DEFAULT_API_URL;
}
/** Validate the team API key prefix. The full token check happens server-side. */
export function validateTeamApiKey(key) {
    return typeof key === "string" && key.startsWith(TEAM_API_KEY_PREFIX);
}
/** Read config; returns null if missing or malformed. Never throws. */
export async function readConfig() {
    let raw;
    try {
        raw = await readFile(getConfigFile(), "utf8");
    }
    catch {
        return null;
    }
    try {
        const parsed = JSON.parse(raw);
        if (typeof parsed.apiUrl !== "string")
            return null;
        const teamApiKey = typeof parsed.teamApiKey === "string" && parsed.teamApiKey.length > 0
            ? parsed.teamApiKey
            : "";
        const userJwt = typeof parsed.userJwt === "string" && parsed.userJwt.length > 0
            ? parsed.userJwt
            : undefined;
        if (!teamApiKey && !userJwt)
            return null;
        return {
            apiUrl: parsed.apiUrl,
            teamApiKey,
            userJwt,
        };
    }
    catch {
        return null;
    }
}
/** Persist config with restrictive permissions (dir 0700, file 0600). */
export async function writeConfig(cfg) {
    const file = getConfigFile();
    await mkdir(dirname(file), { recursive: true, mode: 0o700 });
    // Write atomically-ish: tsc target is Node 20 which has fs.writeFile mode option.
    await writeFile(file, JSON.stringify(cfg, null, 2) + "\n", {
        mode: 0o600,
        encoding: "utf8",
    });
}
/** Delete config; returns true if a file was removed, false if there was nothing to remove. */
export async function deleteConfig() {
    try {
        await rm(getConfigFile(), { force: false });
        return true;
    }
    catch (err) {
        const code = err.code;
        if (code === "ENOENT")
            return false;
        throw err;
    }
}
