import { execSync } from "node:child_process";
import { pathExists } from "@poe-code/config-mutations";
import { collectEnvOverrides, initProjectConfig, readDocument, readDocumentReadonly, resolveEditTarget } from "@poe-code/poe-code-config";
import { text } from "toolcraft-design";
import { knownConfigScopes } from "../../services/config.js";
import { createExecutionResources, resolveCommandFlags, resolveMergedDocument, shlexQuote } from "./shared.js";
const REDACTED_CONFIG_VALUE = "<redacted>";
const SENSITIVE_CONFIG_KEY_NAMES = new Set([
    "apikey",
    "authorization",
    "proxyauthorization",
    "secret",
    "token",
    "password"
]);
const SENSITIVE_CONFIG_KEY_SUFFIXES = [
    "apikey",
    "apitoken",
    "authtoken",
    "accesstoken",
    "secret",
    "password"
];
const SENSITIVE_ENV_VARS = new Set(["POE_API_KEY"]);
export function registerConfigCommand(program, container) {
    const config = program
        .command("config")
        .description("Inspect and manage poe-code config files.")
        .action(async () => {
        await executeConfigInfo(program, container);
    });
    config
        .command("show")
        .description("Show config inputs and the merged result.")
        .action(async () => {
        await executeConfigShow(program, container);
    });
    config
        .command("init")
        .description("Create an empty project config file.")
        .action(async () => {
        await executeConfigInit(program, container);
    });
    config
        .command("edit")
        .description("Open a config file in $EDITOR.")
        .option("--global", "Open the global config file.")
        .option("--project", "Open the project config file.")
        .action(async (options) => {
        await executeConfigEdit(program, container, options);
    });
}
async function executeConfigInfo(program, container) {
    const flags = resolveCommandFlags(program);
    const resources = createExecutionResources(container, flags, "config");
    const globalExists = await pathExists(container.fs, container.env.configPath);
    const projectExists = await pathExists(container.fs, container.env.projectConfigPath);
    resources.logger.intro("config");
    resources.logger.resolved("Global config", `${container.env.configPath} (${globalExists ? "exists" : "missing"})`);
    resources.logger.resolved("Project config", `${container.env.projectConfigPath} (${projectExists ? "exists" : "missing"})`);
    resources.logger.nextSteps([
        'Run "poe-code utils config show" to see resolved configuration.'
    ]);
}
async function executeConfigShow(program, container) {
    const flags = resolveCommandFlags(program);
    const resources = createExecutionResources(container, flags, "config:show");
    const readConfigDocument = flags.dryRun ? readDocumentReadonly : readDocument;
    const globalDocument = await readConfigDocument(container.fs, container.env.configPath);
    const projectDocument = await readConfigDocument(container.fs, container.env.projectConfigPath);
    const envOverrides = collectEnvOverrides(knownConfigScopes, container.env.variables);
    const resolvedDocument = await resolveMergedDocument(container, { readOnly: flags.dryRun });
    resources.logger.intro("config show");
    resources.logger.info([
        formatDocumentSection("Global config", container.env.configPath, globalDocument),
        formatDocumentSection("Project config", container.env.projectConfigPath, projectDocument),
        formatEnvSection(envOverrides.entries),
        formatDocumentSection("Resolved (merged)", undefined, resolvedDocument)
    ].join("\n\n"));
}
async function executeConfigInit(program, container) {
    const flags = resolveCommandFlags(program);
    const resources = createExecutionResources(container, flags, "config:init");
    const targetPath = container.env.projectConfigPath;
    resources.logger.intro("config init");
    if (await pathExists(container.fs, targetPath)) {
        resources.logger.info(`Project config already exists at ${targetPath}`);
        return;
    }
    if (flags.dryRun) {
        resources.logger.dryRun(`Dry run: would create project config at ${targetPath}`);
        return;
    }
    await initProjectConfig(container.fs, targetPath);
    resources.logger.success(`Created project config at ${targetPath}`);
}
async function executeConfigEdit(program, container, options) {
    const flags = resolveCommandFlags(program);
    const resources = createExecutionResources(container, flags, "config:edit");
    const editor = resolveEditor(container);
    resources.logger.intro("config edit");
    const targetPath = await resolveEditTarget(container.fs, container.env.configPath, container.env.projectConfigPath, options);
    if (flags.dryRun) {
        resources.logger.dryRun(`Dry run: would open ${targetPath} in ${editor}`);
        return;
    }
    if (!(await pathExists(container.fs, targetPath))) {
        await initProjectConfig(container.fs, targetPath);
    }
    execSync(`${editor} ${shlexQuote(targetPath)}`, {
        stdio: "inherit"
    });
}
function formatDocumentSection(title, filePath, document) {
    const headingText = filePath ? `${title} (${filePath})` : title;
    const displayDocument = redactConfigDocument(document);
    const body = Object.keys(document).length === 0
        ? text.muted("(empty)")
        : JSON.stringify(displayDocument, null, 2);
    return `${text.heading(`── ${headingText} ──`)}\n${body}`;
}
function formatEnvSection(entries) {
    const body = entries.length > 0 ? entries.map(redactEnvEntry).join("\n") : text.muted("(empty)");
    return `${text.heading("── Environment variable overrides ──")}\n${body}`;
}
function redactConfigDocument(document) {
    return redactConfigValue(document);
}
function redactConfigValue(value, key) {
    if (key !== undefined && isSensitiveConfigKey(key)) {
        return REDACTED_CONFIG_VALUE;
    }
    if (Array.isArray(value)) {
        return value.map((entry) => redactConfigValue(entry));
    }
    if (value !== null && typeof value === "object") {
        return Object.fromEntries(Object.entries(value).map(([entryKey, entryValue]) => [
            entryKey,
            redactConfigValue(entryValue, entryKey)
        ]));
    }
    return value;
}
function isSensitiveConfigKey(key) {
    const normalized = key.toLowerCase().replace(/[-_\s]/g, "");
    return SENSITIVE_CONFIG_KEY_NAMES.has(normalized)
        || SENSITIVE_CONFIG_KEY_SUFFIXES.some(suffix => normalized.endsWith(suffix));
}
function redactEnvEntry(entry) {
    const match = entry.match(/^(\s*([A-Z0-9_]+)\s*=\s*).*/);
    if (!match) {
        return entry;
    }
    const [, prefix, name] = match;
    return SENSITIVE_ENV_VARS.has(name) ? `${prefix}${REDACTED_CONFIG_VALUE}` : entry;
}
function resolveEditor(container) {
    const editor = container.env.getVariable("EDITOR") ?? container.env.getVariable("VISUAL");
    const resolved = typeof editor === "string" ? editor.trim() : "";
    if (resolved.length === 0) {
        throw new Error("Set $EDITOR to use this command");
    }
    return resolved;
}
//# sourceMappingURL=config.js.map