/**
 * A library for loading config files for use with the
 * {@link https://rushstack.io/pages/heft/overview/ | Heft} build system.
 *
 * @packageDocumentation
 */

import type { IRigConfig } from '@rushstack/rig-package';
import type { ITerminal } from '@rushstack/terminal';

/**
 * @deprecated Use {@link ProjectConfigurationFile} instead.
 * @beta
 */
export declare const ConfigurationFile: typeof ProjectConfigurationFile;

/**
 * @deprecated Use {@link ProjectConfigurationFile} instead.
 * @beta
 */
export declare type ConfigurationFile<TConfigurationFile> = ProjectConfigurationFile<TConfigurationFile>;

/**
 * @beta
 */
export declare abstract class ConfigurationFileBase<TConfigurationFile, TExtraOptions extends {}> {
    private readonly _getSchema;
    private readonly _jsonPathMetadata;
    private readonly _propertyInheritanceTypes;
    private readonly _defaultPropertyInheritance;
    private readonly _customValidationFunction;
    private __schema;
    private get _schema();
    private readonly _configCache;
    private readonly _configPromiseCache;
    constructor(options: IConfigurationFileOptions<TConfigurationFile, TExtraOptions>);
    /**
     * @internal
     */
    static _formatPathForLogging: (path: string) => string;
    /**
     * Get the path to the source file that the referenced property was originally
     * loaded from.
     */
    getObjectSourceFilePath<TObject extends object>(obj: TObject): string | undefined;
    /**
     * Get the value of the specified property on the specified object that was originally
     * loaded from a configuration file.
     */
    getPropertyOriginalValue<TParentProperty extends object, TValue>(options: IOriginalValueOptions<TParentProperty>): TValue | undefined;
    /**
     * Get the original value of the `$schema` property from the original configuration file, it one was present.
     */
    getSchemaPropertyOriginalValue<TObject extends object>(obj: TObject): string | undefined;
    protected _loadConfigurationFileInnerWithCache(terminal: ITerminal, resolvedConfigurationFilePath: string, projectFolderPath: string | undefined, onConfigurationFileNotFound?: IOnConfigurationFileNotFoundCallback): TConfigurationFile;
    protected _loadConfigurationFileInnerWithCacheAsync(terminal: ITerminal, resolvedConfigurationFilePath: string, projectFolderPath: string | undefined, onFileNotFound?: IOnConfigurationFileNotFoundCallback): Promise<TConfigurationFile>;
    private _loadConfigurationFileEntryWithCache;
    private _loadConfigurationFileEntryWithCacheAsync;
    /**
     * Parses the raw JSON-with-comments text of the configuration file.
     * @param fileText - The text of the configuration file
     * @param resolvedConfigurationFilePathForLogging - The path to the configuration file, formatted for logs
     * @returns The parsed configuration file
     */
    private _parseConfigurationFile;
    /**
     * Resolves all path properties and annotates properties with their original values.
     * @param entry - The cache entry for the loaded configuration file
     * @param projectFolderPath - The project folder path, if applicable
     * @returns The configuration file with all path properties resolved
     */
    private _contextualizeConfigurationFile;
    /**
     * Resolves all path properties and merges parent properties.
     * @param entry - The cache entry for the loaded configuration file
     * @param projectFolderPath - The project folder path, if applicable
     * @returns The flattened, unvalidated configuration file, with path properties resolved
     */
    private _contextualizeAndFlattenConfigurationFile;
    /**
     * Resolves all path properties, merges parent properties, and validates the configuration file.
     * @param entry - The cache entry for the loaded configuration file
     * @param projectFolderPath - The project folder path, if applicable
     * @param terminal - The terminal to log validation messages to
     * @returns The finalized configuration file
     */
    private _finalizeConfigurationFile;
    private _loadConfigurationFileEntry;
    private _loadConfigurationFileEntryAsync;
    private _annotateProperties;
    private _resolvePathProperty;
    private _mergeConfigurationFiles;
    private _mergeObjects;
}

/**
 * A function to invoke after schema validation to validate the configuration file.
 * If this function returns any value other than `true`, the configuration file API
 * will throw an error indicating that custom validation failed. If the function wishes
 * to provide its own error message, it may use any combination of the terminal and throwing
 * its own error.
 * @beta
 */
export declare type CustomValidationFunction<TConfigurationFile> = (configurationFile: TConfigurationFile, resolvedConfigurationFilePathForLogging: string, terminal: ITerminal) => boolean;

/**
 * @beta
 */
export declare type IConfigurationFileOptions<TConfigurationFile, TExtraOptions extends object> = IConfigurationFileOptionsWithJsonSchemaFilePath<TConfigurationFile, TExtraOptions> | IConfigurationFileOptionsWithJsonSchemaObject<TConfigurationFile, TExtraOptions>;

/**
 * @beta
 */
export declare interface IConfigurationFileOptionsBase<TConfigurationFile> {
    /**
     * Use this property to specify how JSON nodes are postprocessed.
     */
    jsonPathMetadata?: IJsonPathsMetadata<TConfigurationFile>;
    /**
     * Use this property to control how root-level properties are handled between parent and child
     * configuration files.
     */
    propertyInheritance?: IPropertiesInheritance<TConfigurationFile>;
    /**
     * Use this property to control how specific property types are handled between parent and child
     * configuration files.
     */
    propertyInheritanceDefaults?: IPropertyInheritanceDefaults;
    /**
     * Use this property if you need to validate the configuration file in ways beyond what JSON schema can handle.
     * This function will be invoked after JSON schema validation.
     *
     * If the file is valid, this function should return `true`, otherwise `ConfigurationFile` will throw an error
     * indicating that custom validation failed. To suppress this error, the function may itself choose to throw.
     */
    customValidationFunction?: CustomValidationFunction<TConfigurationFile>;
}

/**
 * @beta
 */
export declare type IConfigurationFileOptionsWithJsonSchemaFilePath<TConfigurationFile, TExtraOptions extends {}> = IConfigurationFileOptionsBase<TConfigurationFile> & TExtraOptions & {
    /**
     * The path to the schema for the configuration file.
     */
    jsonSchemaPath: string;
    jsonSchemaObject?: never;
};

/**
 * @beta
 */
export declare type IConfigurationFileOptionsWithJsonSchemaObject<TConfigurationFile, TExtraOptions extends {}> = IConfigurationFileOptionsBase<TConfigurationFile> & TExtraOptions & {
    /**
     * The schema for the configuration file.
     */
    jsonSchemaObject: object;
    jsonSchemaPath?: never;
};

/**
 * Used to specify how node(s) in a JSON object should be processed after being loaded.
 *
 * @beta
 */
export declare interface ICustomJsonPathMetadata<TConfigurationFile> {
    /**
     * If `ICustomJsonPathMetadata.pathResolutionMethod` is set to `PathResolutionMethod.custom`,
     * this property be used to resolve the path.
     */
    customResolver?: (resolverOptions: IJsonPathMetadataResolverOptions<TConfigurationFile>) => string;
    /**
     * If this property describes a filesystem path, use this property to describe
     * how the path should be resolved.
     */
    pathResolutionMethod?: PathResolutionMethod.custom;
}

/**
 * @beta
 */
export declare interface ICustomPropertyInheritance<TObject> extends IPropertyInheritance<InheritanceType.custom> {
    /**
     * Provides a custom inheritance function. This function takes two arguments: the first is the
     * child file's object, and the second is the parent file's object. The function should return
     * the resulting combined object.
     * This function will not be invoked if the current value is `null`, the property will simply be deleted.
     */
    inheritanceFunction: PropertyInheritanceCustomFunction<TObject>;
}

/**
 * @beta
 */
export declare type IJsonPathMetadata<T> = ICustomJsonPathMetadata<T> | INonCustomJsonPathMetadata;

/**
 * Options provided to the custom resolver specified in {@link ICustomJsonPathMetadata}.
 *
 * @beta
 */
export declare interface IJsonPathMetadataResolverOptions<TConfigurationFile> {
    /**
     * The name of the property being resolved.
     */
    propertyName: string;
    /**
     * The value of the path property being resolved.
     */
    propertyValue: string;
    /**
     * The path to the configuration file the property was obtained from.
     */
    configurationFilePath: string;
    /**
     * The configuration file the property was obtained from.
     */
    configurationFile: Partial<TConfigurationFile>;
    /**
     * If this is a project configuration file, the root folder of the project.
     */
    projectFolderPath?: string;
}

/**
 * Keys in this object are JSONPaths {@link https://jsonpath.com/}, and values are objects
 * that describe how node(s) selected by the JSONPath are processed after loading.
 *
 * @beta
 */
export declare interface IJsonPathsMetadata<TConfigurationFile> {
    [jsonPath: string]: IJsonPathMetadata<TConfigurationFile>;
}

/**
 * @beta
 *
 * The set of possible mechanisms for merging properties from parent configuration files.
 * If a child configuration file sets a property value to `null`, that will always delete the value
 * specified in the parent configuration file, regardless of the inheritance type.
 */
export declare const InheritanceType: {
    /**
     * Append additional elements after elements from the parent file's property. Only applicable
     * for arrays.
     */
    readonly append: "append";
    /**
     * Perform a shallow merge of additional elements after elements from the parent file's property.
     * Only applicable for objects.
     */
    readonly merge: "merge";
    /**
     * Discard elements from the parent file's property
     */
    readonly replace: "replace";
    /**
     * Custom inheritance functionality
     */
    readonly custom: "custom";
};

/**
 * @beta
 */
export declare type InheritanceType = (typeof InheritanceType)[keyof typeof InheritanceType];

/**
 * @beta
 */
export declare namespace InheritanceType {
    /**
     * Append additional elements after elements from the parent file's property. Only applicable
     * for arrays.
     */
    export type append = typeof InheritanceType.append;
    /**
     * Perform a shallow merge of additional elements after elements from the parent file's property.
     * Only applicable for objects.
     */
    export type merge = typeof InheritanceType.merge;
    /**
     * Discard elements from the parent file's property
     */
    export type replace = typeof InheritanceType.replace;
    /**
     * Custom inheritance functionality
     */
    export type custom = typeof InheritanceType.custom;
}

/**
 * Used to specify how node(s) in a JSON object should be processed after being loaded.
 *
 * @beta
 */
export declare interface INonCustomJsonPathMetadata {
    /**
     * If this property describes a filesystem path, use this property to describe
     * how the path should be resolved.
     */
    pathResolutionMethod?: PathResolutionMethod.NodeResolve | PathResolutionMethod.nodeResolve | PathResolutionMethod.resolvePathRelativeToConfigurationFile | PathResolutionMethod.resolvePathRelativeToProjectRoot;
}

/**
 * Callback that returns a fallback configuration file path if the original configuration file was not found.
 * @beta
 */
export declare type IOnConfigurationFileNotFoundCallback = (resolvedConfigurationFilePathForLogging: string) => string | undefined;

/**
 * @beta
 */
export declare interface IOriginalValueOptions<TParentProperty> {
    parentObject: Partial<TParentProperty>;
    propertyName: keyof TParentProperty;
}

/**
 * @beta
 */
export declare interface IProjectConfigurationFileOptions {
    /**
     * A project root-relative path to the configuration file that should be loaded.
     */
    projectRelativeFilePath: string;
}

/**
 * Alias for the constructor type for {@link ProjectConfigurationFile}.
 * @beta
 */
export declare type IProjectConfigurationFileSpecification<TConfigFile> = IConfigurationFileOptions<TConfigFile, IProjectConfigurationFileOptions>;

/**
 * @beta
 */
export declare type IPropertiesInheritance<TConfigurationFile> = {
    [propertyName in keyof TConfigurationFile]?: IPropertyInheritance<InheritanceType.append | InheritanceType.merge | InheritanceType.replace> | ICustomPropertyInheritance<TConfigurationFile[propertyName]>;
};

/**
 * @beta
 */
export declare interface IPropertyInheritance<TInheritanceType extends InheritanceType> {
    inheritanceType: TInheritanceType;
}

/**
 * @beta
 */
export declare interface IPropertyInheritanceDefaults {
    array?: IPropertyInheritance<InheritanceType.append | InheritanceType.replace>;
    object?: IPropertyInheritance<InheritanceType.merge | InheritanceType.replace>;
}

/**
 * @beta
 */
export declare class NonProjectConfigurationFile<TConfigurationFile> extends ConfigurationFileBase<TConfigurationFile, {}> {
    /**
     * Load the configuration file at the specified absolute path, automatically resolving
     * `extends` properties. Will throw an error if the file cannot be found.
     */
    loadConfigurationFile(terminal: ITerminal, filePath: string): TConfigurationFile;
    /**
     * Load the configuration file at the specified absolute path, automatically resolving
     * `extends` properties. Will throw an error if the file cannot be found.
     */
    loadConfigurationFileAsync(terminal: ITerminal, filePath: string): Promise<TConfigurationFile>;
    /**
     * This function is identical to {@link NonProjectConfigurationFile.loadConfigurationFile}, except
     * that it returns `undefined` instead of throwing an error if the configuration file cannot be found.
     */
    tryLoadConfigurationFile(terminal: ITerminal, filePath: string): TConfigurationFile | undefined;
    /**
     * This function is identical to {@link NonProjectConfigurationFile.loadConfigurationFileAsync}, except
     * that it returns `undefined` instead of throwing an error if the configuration file cannot be found.
     */
    tryLoadConfigurationFileAsync(terminal: ITerminal, filePath: string): Promise<TConfigurationFile | undefined>;
}

/**
 * @beta
 *
 * The set of possible resolution methods for fields that refer to paths.
 */
export declare const PathResolutionMethod: {
    /**
     * Resolve a path relative to the configuration file
     */
    readonly resolvePathRelativeToConfigurationFile: "resolvePathRelativeToConfigurationFile";
    /**
     * Resolve a path relative to the root of the project containing the configuration file
     */
    readonly resolvePathRelativeToProjectRoot: "resolvePathRelativeToProjectRoot";
    /**
     * Treat the property as a NodeJS-style require/import reference and resolve using standard
     * NodeJS filesystem resolution
     *
     * @deprecated
     * Use {@link (PathResolutionMethod:variable).nodeResolve} instead
     */
    readonly NodeResolve: "NodeResolve";
    /**
     * Treat the property as a NodeJS-style require/import reference and resolve using standard
     * NodeJS filesystem resolution
     */
    readonly nodeResolve: "nodeResolve";
    /**
     * Resolve the property using a custom resolver.
     */
    readonly custom: "custom";
};

/**
 * @beta
 */
export declare type PathResolutionMethod = (typeof PathResolutionMethod)[keyof typeof PathResolutionMethod];

/**
 * @beta
 */
export declare namespace PathResolutionMethod {
    /**
     * Resolve a path relative to the configuration file
     */
    export type resolvePathRelativeToConfigurationFile = typeof PathResolutionMethod.resolvePathRelativeToConfigurationFile;
    /**
     * Resolve a path relative to the root of the project containing the configuration file
     */
    export type resolvePathRelativeToProjectRoot = typeof PathResolutionMethod.resolvePathRelativeToProjectRoot;
    /**
     * Treat the property as a NodeJS-style require/import reference and resolve using standard
     * NodeJS filesystem resolution
     *
     * @deprecated
     * Use {@link (PathResolutionMethod:namespace).nodeResolve} instead
     */
    export type NodeResolve = typeof PathResolutionMethod.NodeResolve;
    /**
     * Treat the property as a NodeJS-style require/import reference and resolve using standard
     * NodeJS filesystem resolution
     */
    export type nodeResolve = typeof PathResolutionMethod.nodeResolve;
    /**
     * Resolve the property using a custom resolver.
     */
    export type custom = typeof PathResolutionMethod.custom;
}

/**
 * @beta
 */
export declare class ProjectConfigurationFile<TConfigurationFile> extends ConfigurationFileBase<TConfigurationFile, IProjectConfigurationFileOptions> {
    /** {@inheritDoc IProjectConfigurationFileOptions.projectRelativeFilePath} */
    readonly projectRelativeFilePath: string;
    constructor(options: IProjectConfigurationFileSpecification<TConfigurationFile>);
    /**
     * Find and return a configuration file for the specified project, automatically resolving
     * `extends` properties and handling rigged configuration files. Will throw an error if a configuration
     * file cannot be found in the rig or project config folder.
     */
    loadConfigurationFileForProject(terminal: ITerminal, projectPath: string, rigConfig?: IRigConfig): TConfigurationFile;
    /**
     * Find and return a configuration file for the specified project, automatically resolving
     * `extends` properties and handling rigged configuration files. Will throw an error if a configuration
     * file cannot be found in the rig or project config folder.
     */
    loadConfigurationFileForProjectAsync(terminal: ITerminal, projectPath: string, rigConfig?: IRigConfig): Promise<TConfigurationFile>;
    /**
     * This function is identical to {@link ProjectConfigurationFile.loadConfigurationFileForProject}, except
     * that it returns `undefined` instead of throwing an error if the configuration file cannot be found.
     */
    tryLoadConfigurationFileForProject(terminal: ITerminal, projectPath: string, rigConfig?: IRigConfig): TConfigurationFile | undefined;
    /**
     * This function is identical to {@link ProjectConfigurationFile.loadConfigurationFileForProjectAsync}, except
     * that it returns `undefined` instead of throwing an error if the configuration file cannot be found.
     */
    tryLoadConfigurationFileForProjectAsync(terminal: ITerminal, projectPath: string, rigConfig?: IRigConfig): Promise<TConfigurationFile | undefined>;
    private _getConfigurationFilePathForProject;
    private _getRigConfigFallback;
}

/**
 * @beta
 */
export declare type PropertyInheritanceCustomFunction<TObject> = (currentObject: TObject, parentObject: TObject) => TObject;

/**
 * Returns an object with investigative annotations stripped, useful for snapshot testing.
 *
 * @beta
 */
declare function stripAnnotations<TObject>(obj: TObject): TObject;

export declare namespace TestUtilities {
    export {
        stripAnnotations
    }
}

export { }
