/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import { getSettingsSchema, } from '../config/settingsSchema.js';
import { ExperimentFlags } from '@google/gemini-cli-core';
function flattenSchema(schema, prefix = '') {
    let result = {};
    for (const key in schema) {
        const newKey = prefix ? `${prefix}.${key}` : key;
        const definition = schema[key];
        result[newKey] = { ...definition, key: newKey };
        if (definition.properties) {
            result = { ...result, ...flattenSchema(definition.properties, newKey) };
        }
    }
    return result;
}
let _FLATTENED_SCHEMA;
/** Returns a flattened schema, the first call is memoized for future requests. */
export function getFlattenedSchema() {
    return (_FLATTENED_SCHEMA ??
        (_FLATTENED_SCHEMA = flattenSchema(getSettingsSchema())));
}
function clearFlattenedSchema() {
    _FLATTENED_SCHEMA = undefined;
}
export function getSettingsByCategory() {
    const categories = {};
    Object.values(getFlattenedSchema()).forEach((definition) => {
        const category = definition.category;
        if (!categories[category]) {
            categories[category] = [];
        }
        categories[category].push(definition);
    });
    return categories;
}
export function getSettingDefinition(key) {
    return getFlattenedSchema()[key];
}
export function requiresRestart(key) {
    return getFlattenedSchema()[key]?.requiresRestart ?? false;
}
export function getDefaultValue(key) {
    return getFlattenedSchema()[key]?.default;
}
/**
 * Get the effective default value for a setting, checking experiment values when available.
 * For settings like Context Compression Threshold, this will return the experiment value if set,
 * otherwise falls back to the schema default.
 */
export function getEffectiveDefaultValue(key, config) {
    if (key === 'model.compressionThreshold' && config) {
        const experiments = config.getExperiments();
        const experimentValue = experiments?.flags[ExperimentFlags.CONTEXT_COMPRESSION_THRESHOLD]
            ?.floatValue;
        if (experimentValue !== undefined && experimentValue !== 0) {
            return experimentValue;
        }
    }
    return getDefaultValue(key);
}
export function getRestartRequiredSettings() {
    return Object.values(getFlattenedSchema())
        .filter((definition) => definition.requiresRestart)
        .map((definition) => definition.key);
}
/**
 * Get restart-required setting keys that are also visible in the dialog.
 * Non-dialog restart keys (e.g. parent container objects like mcpServers, tools)
 * are excluded because users cannot change them through the dialog.
 */
export function getDialogRestartRequiredSettings() {
    return Object.values(getFlattenedSchema())
        .filter((definition) => definition.requiresRestart && definition.showInDialog !== false)
        .map((definition) => definition.key);
}
export function isRecord(value) {
    return typeof value === 'object' && value !== null;
}
function isSettingsValue(value) {
    if (value === undefined)
        return true;
    if (value === null)
        return false;
    const type = typeof value;
    return (type === 'string' ||
        type === 'number' ||
        type === 'boolean' ||
        type === 'object');
}
/**
 * Gets a value from a nested object using a key path array iteratively.
 */
export function getNestedValue(obj, path) {
    let current = obj;
    for (const key of path) {
        if (!isRecord(current) || !(key in current)) {
            return undefined;
        }
        current = current[key];
    }
    return current;
}
/**
 * Get the effective value for a setting falling back to the default value
 */
export function getEffectiveValue(key, settings) {
    const definition = getSettingDefinition(key);
    if (!definition) {
        return undefined;
    }
    const path = key.split('.');
    // Check the current scope's settings first
    const value = getNestedValue(settings, path);
    if (value !== undefined && isSettingsValue(value)) {
        return value;
    }
    // Return default value if no value is set anywhere
    return definition.default;
}
export function getAllSettingKeys() {
    return Object.keys(getFlattenedSchema());
}
export function getSettingsByType(type) {
    return Object.values(getFlattenedSchema()).filter((definition) => definition.type === type);
}
export function getSettingsRequiringRestart() {
    return Object.values(getFlattenedSchema()).filter((definition) => definition.requiresRestart);
}
/**
 * Validate if a setting key exists in the schema
 */
export function isValidSettingKey(key) {
    return key in getFlattenedSchema();
}
export function getSettingCategory(key) {
    return getFlattenedSchema()[key]?.category;
}
export function shouldShowInDialog(key) {
    return getFlattenedSchema()[key]?.showInDialog ?? true; // Default to true for backward compatibility
}
export function getDialogSettingKeys() {
    return Object.values(getFlattenedSchema())
        .filter((definition) => definition.showInDialog !== false)
        .map((definition) => definition.key);
}
/**
 * Get all settings that should be shown in the dialog, grouped by category like "Advanced", "General", etc.
 */
export function getDialogSettingsByCategory() {
    const categories = {};
    Object.values(getFlattenedSchema())
        .filter((definition) => definition.showInDialog !== false)
        .forEach((definition) => {
        const category = definition.category;
        if (!categories[category]) {
            categories[category] = [];
        }
        categories[category].push(definition);
    });
    return categories;
}
export function getDialogSettingsByType(type) {
    return Object.values(getFlattenedSchema()).filter((definition) => definition.type === type && definition.showInDialog !== false);
}
export function isInSettingsScope(key, scopeSettings) {
    const path = key.split('.');
    const value = getNestedValue(scopeSettings, path);
    return value !== undefined;
}
/**
 * Appends a star (*) to settings that exist in the scope
 */
export function getDisplayValue(key, scopeSettings, _mergedSettings) {
    const definition = getSettingDefinition(key);
    const existsInScope = isInSettingsScope(key, scopeSettings);
    let value;
    if (existsInScope) {
        value = getEffectiveValue(key, scopeSettings);
    }
    else {
        value = getDefaultValue(key);
    }
    let valueString = String(value);
    // Handle object types by stringifying them
    if (definition?.type === 'object' &&
        value !== null &&
        typeof value === 'object') {
        valueString = JSON.stringify(value);
    }
    else if (definition?.type === 'enum' && definition.options) {
        const option = definition.options?.find((option) => option.value === value);
        valueString = option?.label ?? `${value}`;
    }
    if (definition?.unit === '%' && typeof value === 'number') {
        valueString = `${value} (${Math.round(value * 100)}%)`;
    }
    else if (definition?.unit) {
        valueString = `${valueString}${definition.unit}`;
    }
    if (existsInScope) {
        return `${valueString}*`;
    }
    return valueString;
}
/**Utilities for parsing Settings that can be inline edited by the user typing out values */
function tryParseJsonStringArray(input) {
    try {
        const parsed = JSON.parse(input);
        if (Array.isArray(parsed) &&
            parsed.every((item) => typeof item === 'string')) {
            return parsed;
        }
        return null;
    }
    catch {
        return null;
    }
}
function tryParseJsonObject(input) {
    try {
        const parsed = JSON.parse(input);
        if (isRecord(parsed) && !Array.isArray(parsed)) {
            return parsed;
        }
        return null;
    }
    catch {
        return null;
    }
}
function parseStringArrayValue(input) {
    const trimmed = input.trim();
    if (trimmed === '')
        return [];
    return (tryParseJsonStringArray(trimmed) ??
        input
            .split(',')
            .map((p) => p.trim())
            .filter((p) => p.length > 0));
}
function parseObjectValue(input) {
    const trimmed = input.trim();
    if (trimmed === '') {
        return null;
    }
    return tryParseJsonObject(trimmed);
}
export function parseEditedValue(type, newValue) {
    if (type === 'number') {
        if (newValue.trim() === '') {
            return null;
        }
        const numParsed = Number(newValue.trim());
        if (Number.isNaN(numParsed)) {
            return null;
        }
        return numParsed;
    }
    if (type === 'array') {
        return parseStringArrayValue(newValue);
    }
    if (type === 'object') {
        return parseObjectValue(newValue);
    }
    return newValue;
}
export function getEditValue(type, rawValue) {
    if (rawValue === undefined) {
        return undefined;
    }
    if (type === 'array' && Array.isArray(rawValue)) {
        return rawValue.join(', ');
    }
    if (type === 'object' && rawValue !== null && typeof rawValue === 'object') {
        return JSON.stringify(rawValue);
    }
    return undefined;
}
export const TEST_ONLY = { clearFlattenedSchema };
//# sourceMappingURL=settingsUtils.js.map