import { mkdir, readFile, writeFile } from "node:fs/promises";
import { dirname, join, resolve } from "node:path";
import { homedir } from "node:os";
const emptyConfig = () => ({ instances: {} });
export function resolveConfigPath(env) {
    if (env.EUDIPLO_CLI_CONFIG) {
        return resolve(env.EUDIPLO_CLI_CONFIG);
    }
    const baseDir = env.EUDIPLO_CLI_HOME
        ? resolve(env.EUDIPLO_CLI_HOME)
        : join(homedir(), ".eudiplo");
    return join(baseDir, "config.json");
}
export async function loadConfig(path) {
    try {
        const contents = await readFile(path, "utf8");
        return validateConfig(JSON.parse(contents));
    }
    catch (error) {
        if (isNodeError(error) && error.code === "ENOENT") {
            return emptyConfig();
        }
        throw error;
    }
}
export async function saveConfig(path, config) {
    await mkdir(dirname(path), { recursive: true, mode: 0o700 });
    await writeFile(path, `${JSON.stringify(config, null, 4)}\n`, {
        encoding: "utf8",
        mode: 0o600,
    });
}
function validateConfig(value) {
    if (!isRecord(value)) {
        throw new Error("Config must be a JSON object.");
    }
    if (!isRecord(value.instances)) {
        throw new Error("Config must define an instances object.");
    }
    const instances = {};
    for (const [name, instance] of Object.entries(value.instances)) {
        instances[name] = validateInstanceConfig(name, instance);
    }
    return {
        defaultInstance: validateDefaultInstance(value.defaultInstance, instances),
        instances,
    };
}
export function upsertInstance(config, name, instance) {
    return {
        defaultInstance: config.defaultInstance ?? name,
        instances: {
            ...config.instances,
            [name]: instance,
        },
    };
}
function isRecord(value) {
    return typeof value === "object" && value !== null && !Array.isArray(value);
}
function isNodeError(error) {
    return error instanceof Error && "code" in error;
}
function validateDefaultInstance(value, instances) {
    if (typeof value !== "string") {
        return undefined;
    }
    if (!Object.hasOwn(instances, value)) {
        throw new Error(`Default instance ${value} is not defined in instances.`);
    }
    return value;
}
function validateInstanceConfig(name, value) {
    if (!isRecord(value)) {
        throw new Error(`Instance ${name} must be an object.`);
    }
    if (value.target !== "compose" && value.target !== "external") {
        throw new Error(`Instance ${name} has unsupported target ${String(value.target)}.`);
    }
    if (typeof value.url !== "string" || value.url.length === 0) {
        throw new Error(`Instance ${name} must define a url.`);
    }
    validateHttpUrl(value.url, `Instance ${name} url`);
    validateOptionalHttpUrl(value.clientUrl, `Instance ${name} clientUrl`);
    return {
        target: value.target,
        url: value.url,
        clientUrl: optionalString(value.clientUrl),
        composeFile: optionalString(value.composeFile),
        composeFiles: optionalStringArray(value.composeFiles),
        envFile: optionalString(value.envFile),
        projectName: optionalString(value.projectName),
    };
}
function optionalString(value) {
    return typeof value === "string" ? value : undefined;
}
function optionalStringArray(value) {
    if (!Array.isArray(value)) {
        return undefined;
    }
    if (value.some((item) => typeof item !== "string")) {
        throw new Error("composeFiles must contain only strings.");
    }
    return value;
}
function validateOptionalHttpUrl(value, label) {
    if (typeof value === "string") {
        validateHttpUrl(value, label);
    }
}
function validateHttpUrl(value, label) {
    try {
        const url = new URL(value);
        if (url.protocol !== "http:" && url.protocol !== "https:") {
            throw new Error("Unsupported URL protocol.");
        }
    }
    catch {
        throw new Error(`${label} must be an absolute HTTP(S) URL.`);
    }
}
//# sourceMappingURL=config.js.map