import { existsSync, readFileSync } from 'node:fs';
import { parse as parseYaml } from 'yaml';
import { setNestedConfigValue } from '../env/config-env-overrides.js';
import { getGlobalTracedSchema, getProjectTracedSchema, } from './tracedConfigSchema.js';
import { loadTraceEntriesViaRuntime } from './tracedConfigRuntimeBridge.js';
function isRecord(value) {
    return typeof value === 'object' && value !== null && !Array.isArray(value);
}
function createYamlParser(options) {
    return (content) => {
        let parsed;
        try {
            parsed = parseYaml(content);
        }
        catch (error) {
            const message = error instanceof Error ? error.message : String(error);
            if (options.parseErrorPrefix) {
                throw new Error(`${options.parseErrorPrefix}: ${message}`);
            }
            throw new Error(message);
        }
        const sanitized = options.sanitize ? options.sanitize(parsed) : parsed;
        if (sanitized != null && !isRecord(sanitized)) {
            throw new Error(options.rootObjectError ?? `Configuration error: ${options.configPath} must be a YAML object.`);
        }
        return sanitized ?? {};
    };
}
function getNearestTraceEntry(traceEntries, path) {
    let current = path;
    while (current.length > 0) {
        const traced = traceEntries.get(current);
        if (traced && traced.origin !== 'default') {
            return { traced };
        }
        const lastDot = current.lastIndexOf('.');
        if (lastDot < 0) {
            break;
        }
        current = current.slice(0, lastDot);
    }
    return undefined;
}
function getOriginPrecedence(origin) {
    if (origin === 'cli')
        return 4;
    if (origin === 'env')
        return 3;
    if (origin === 'local')
        return 2;
    if (origin === 'global')
        return 1;
    return 0;
}
function shouldBlockDescendantTraceEntry(ancestor, descendant) {
    if (ancestor.origin !== 'env' && ancestor.origin !== 'cli') {
        return false;
    }
    if (!descendant || descendant.origin === 'default') {
        return true;
    }
    return getOriginPrecedence(ancestor.origin) > getOriginPrecedence(descendant.origin);
}
function getBlockingAncestorTraceEntry(traceEntries, path) {
    const descendant = traceEntries.get(path);
    const segments = path.split('.');
    for (let index = 1; index < segments.length; index += 1) {
        const ancestorKey = segments.slice(0, index).join('.');
        const traced = traceEntries.get(ancestorKey);
        if (traced && shouldBlockDescendantTraceEntry(traced, descendant)) {
            return { traced };
        }
    }
    return undefined;
}
function buildRawConfig(schemaKeys, traceEntries) {
    const rawConfig = {};
    const keys = [...schemaKeys].sort((left, right) => left.split('.').length - right.split('.').length);
    for (const key of keys) {
        const traced = traceEntries.get(key);
        if (!traced || traced.origin === 'default') {
            continue;
        }
        if (getBlockingAncestorTraceEntry(traceEntries, key)) {
            continue;
        }
        setNestedConfigValue(rawConfig, key, traced.value);
    }
    return rawConfig;
}
export function loadConfigTrace(options) {
    const parser = createYamlParser(options);
    const parsedConfig = existsSync(options.configPath)
        ? parser(readFileSync(options.configPath, 'utf-8'))
        : {};
    const traceEntries = loadTraceEntriesViaRuntime(options.schema, options.fileOrigin, parsedConfig);
    const rawConfig = buildRawConfig(Object.keys(options.schema), traceEntries);
    const trace = {
        getOrigin(path) {
            const blockingAncestor = getBlockingAncestorTraceEntry(traceEntries, path);
            if (blockingAncestor) {
                return blockingAncestor.traced.origin;
            }
            return getNearestTraceEntry(traceEntries, path)?.traced.origin ?? 'default';
        },
    };
    return { parsedConfig, rawConfig, trace };
}
export function loadGlobalConfigTrace(configPath, sanitize) {
    return loadConfigTrace({
        configPath,
        fileOrigin: 'global',
        schema: getGlobalTracedSchema(),
        rootObjectError: 'Configuration error: ~/.takt/config.yaml must be a YAML object.',
        sanitize,
    });
}
export function loadProjectConfigTrace(configPath) {
    return loadConfigTrace({
        configPath,
        fileOrigin: 'local',
        schema: getProjectTracedSchema(),
        parseErrorPrefix: `Configuration error: failed to parse ${configPath}`,
        rootObjectError: `Configuration error: ${configPath} must be a YAML object.`,
    });
}
//# sourceMappingURL=tracedConfigLoader.js.map