"use strict";
/*
 * Copyright The OpenTelemetry Authors
 * SPDX-License-Identifier: Apache-2.0
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseConfigFile = exports.FileConfigFactory = void 0;
const core_1 = require("@opentelemetry/core");
const fs = require("fs");
const yaml = require("yaml");
const utils_1 = require("./utils");
// Pre-compiled ajv validator — eliminates runtime schema compilation on cold start.
// Generated by `npm run generate:config`; do not import ajv directly here.
const validateConfig = require("./generated/validator");
class FileConfigFactory {
    _config;
    constructor() {
        this._config = parseConfigFile();
    }
    getConfigModel() {
        return this._config;
    }
}
exports.FileConfigFactory = FileConfigFactory;
function parseConfigFile() {
    const supportedFileVersionPattern = /^1\.0$/;
    const configFile = (0, core_1.getStringFromEnv)('OTEL_CONFIG_FILE') || '';
    const file = fs.readFileSync(configFile, 'utf8');
    const doc = yaml.parseDocument(file, { version: '1.2' });
    (0, utils_1.substituteEnvVars)(doc);
    const processed = doc.toJS();
    const fileFormat = processed?.file_format;
    if (!fileFormat || !supportedFileVersionPattern.test(String(fileFormat))) {
        throw new Error(`${configFile}: Unsupported file_format: "${fileFormat}". Must match ${supportedFileVersionPattern}.`);
    }
    const valid = validateConfig(processed);
    if (!valid) {
        let detail;
        if (!validateConfig.errors) {
            detail = 'unknown error';
        }
        else if (validateConfig.errors.length === 1) {
            const err = validateConfig.errors[0];
            detail = `${err.instancePath} ${err.message}`;
        }
        else {
            const sep = '\n  ';
            detail =
                sep +
                    validateConfig.errors
                        .map(e => `${e.instancePath} ${e.message}`)
                        .join(sep);
        }
        throw new Error(`Invalid OpenTelemetry config file: ${configFile}: ${detail}`);
    }
    const data = processed;
    // Strip file_format from output — it's a meta-field, not a config value
    delete data['file_format'];
    applyConfigDefaults(data);
    mergeAttributesList(data);
    mergeCompositeList(data);
    applyBatchProcessorDefaults(data);
    applyPeriodicReaderDefaults(data);
    applyOtlpHttpEncodingDefaults(data);
    return data;
}
exports.parseConfigFile = parseConfigFile;
/**
 * Apply default encoding ('protobuf') to all otlp_http exporters in the config
 * where encoding was not explicitly specified. The spec defines protobuf as the
 * default encoding for OTLP HTTP exporters.
 */
function applyOtlpHttpEncodingDefaults(data) {
    const applyEncoding = (exporter) => {
        if (exporter && exporter.encoding == null) {
            exporter.encoding = 'protobuf';
        }
    };
    for (const processor of data.tracer_provider?.processors ?? []) {
        applyEncoding(processor.batch?.exporter?.otlp_http);
        applyEncoding(processor.simple?.exporter?.otlp_http);
    }
    for (const reader of data.meter_provider?.readers ?? []) {
        applyEncoding(reader.periodic?.exporter?.otlp_http);
    }
    for (const processor of data.logger_provider?.processors ?? []) {
        applyEncoding(processor.batch?.exporter?.otlp_http);
        applyEncoding(processor.simple?.exporter?.otlp_http);
    }
}
/**
 * Merge resource.attributes_list (comma-separated key=value pairs) into
 * resource.attributes, with entries already in attributes taking precedence.
 */
function mergeAttributesList(data) {
    const resource = data.resource;
    const list = resource?.attributes_list;
    if (typeof list !== 'string' || !list.trim())
        return;
    if (resource.attributes == null) {
        resource.attributes = [];
    }
    const existingKeys = new Set(resource.attributes.map((a) => a.name));
    for (const pair of list.split(',')) {
        const eqIdx = pair.indexOf('=');
        if (eqIdx > 0) {
            const key = pair.slice(0, eqIdx).trim();
            const value = pair.slice(eqIdx + 1).trim();
            if (key && !existingKeys.has(key)) {
                resource.attributes.push({ name: key, value, type: 'string' });
            }
        }
    }
}
/**
 * Merge propagator.composite_list (comma-separated propagator names) into
 * propagator.composite, with entries already in composite taking precedence.
 */
function mergeCompositeList(data) {
    const propagator = data.propagator;
    const list = propagator?.composite_list;
    if (typeof list !== 'string' || !list.trim())
        return;
    if (propagator.composite == null) {
        propagator.composite = [];
    }
    const existingNames = new Set(propagator.composite.map(entry => Object.keys(entry)[0]));
    for (const name of list.split(',')) {
        const trimmed = name.trim();
        if (trimmed && !existingNames.has(trimmed)) {
            propagator.composite.push({
                [trimmed]: {},
            });
        }
    }
}
/**
 * Apply spec-defined defaults for batch span/log-record processor fields that
 * are not encoded in the JSON schema.
 */
function applyBatchProcessorDefaults(data) {
    const applyDefaults = (batch) => {
        if (batch.schedule_delay == null)
            batch.schedule_delay = 5000;
        if (batch.export_timeout == null)
            batch.export_timeout = 30000;
        if (batch.max_queue_size == null)
            batch.max_queue_size = 2048;
        if (batch.max_export_batch_size == null)
            batch.max_export_batch_size = 512;
    };
    for (const processor of data.tracer_provider?.processors ?? []) {
        if (processor.batch)
            applyDefaults(processor.batch);
    }
    for (const processor of data.logger_provider?.processors ?? []) {
        if (processor.batch)
            applyDefaults(processor.batch);
    }
}
/**
 * Apply spec-defined defaults for periodic metric reader fields.
 */
function applyPeriodicReaderDefaults(data) {
    for (const reader of data.meter_provider?.readers ?? []) {
        const periodic = reader.periodic;
        if (!periodic)
            continue;
        if (periodic.interval == null)
            periodic.interval = 60000;
        if (periodic.timeout == null)
            periodic.timeout = 30000;
        if (periodic.cardinality_limits == null) {
            periodic.cardinality_limits = { default: 2000 };
        }
    }
}
/**
 * Apply spec-defined defaults that are not encoded in the JSON schema.
 * Both FileConfigFactory and EnvironmentConfigFactory apply the same defaults
 * so consumers see consistent behaviour regardless of config source.
 */
function applyConfigDefaults(data) {
    if (data.disabled == null) {
        data.disabled = false;
    }
    if (data.log_level == null) {
        data.log_level = 'info';
    }
    if (data.attribute_limits == null) {
        data.attribute_limits = { attribute_count_limit: 128 };
    }
    else if (data.attribute_limits.attribute_count_limit == null) {
        data.attribute_limits.attribute_count_limit = 128;
    }
}
//# sourceMappingURL=FileConfigFactory.js.map