import { WorkerLogLevel, WorkerOutput } from "@webda/workout";
import Ajv, { ErrorObject } from "ajv";
import * as events from "events";
import { OpenAPIV3 } from "openapi-types";
import { Counter, CounterConfiguration, Gauge, GaugeConfiguration, Histogram, HistogramConfiguration } from "prom-client";
import { Writable } from "stream";
import { Application, Configuration } from "./application.js";
import { BinaryService, ContextProvider, ContextProviderInfo, GlobalContext, HttpContext, Logger, OperationContext, RegExpValidator, Service, Store, WebContext, WebdaQL } from "./index.js";
import { Constructor, CoreModel, CoreModelDefinition } from "./models/coremodel.js";
import { RouteInfo, Router } from "./router.js";
import CryptoService from "./services/cryptoservice.js";
export declare class EventEmitterUtils {
    static emit(eventEmitter: events.EventEmitter, event: string | number | symbol, data: any): boolean;
    static emitSync(eventEmitter: events.EventEmitter, event: string | number | symbol, data: any): Promise<any[]>;
    /**
     * Display a message if the listener takes too long
     * @param start
     */
    static elapse(start: number): void;
}
/**
 * Copy from https://github.com/ajv-validator/ajv/blob/master/lib/runtime/validation_error.ts
 * It is not exported by ajv
 */
export declare class ValidationError extends Error {
    readonly errors: Partial<ErrorObject>[];
    readonly ajv: true;
    readonly validation: true;
    constructor(errors: Partial<ErrorObject>[]);
}
/**
 * Operation
 */
export declare class OperationError extends Error {
    operation: string;
    type: "Unknown" | "PermissionDenied" | "InvalidInput";
    constructor(operation: string, type: "Unknown" | "PermissionDenied" | "InvalidInput");
}
/**
 * Define an operation within webda app
 */
export interface OperationDefinition {
    /**
     * Id of the operation
     */
    id: string;
    /**
     * Name of the schema that defines operation input
     */
    input?: string;
    /**
     * Name of the schema that defines operation output
     */
    output?: string;
    /**
     * WebdaQL to execute on session to know if
     * operation is available to user
     */
    permission?: string;
    /**
     * Service implementing the operation
     */
    service: string;
    /**
     * Method implementing the operation
     */
    method: string;
}
/**
 * Define an operation within webda app
 */
export interface OperationDefinitionInfo extends OperationDefinition {
    /**
     * Contains the parse permission query
     */
    permissionQuery?: WebdaQL.QueryValidator;
}
/**
 *
 */
export type RegistryEntry<T = any> = CoreModel & T;
/**
 * Ensure all events store the context in the same place
 */
export interface EventWithContext<T extends OperationContext = OperationContext> {
    context: T;
}
/**
 * RequestFilter allow a service which implement it to control incoming request
 *
 * If one of the filter replies with "true" then the request will go through
 */
export interface RequestFilter<T extends WebContext = WebContext> {
    /**
     * Return true if the request should be allowed
     *
     * @param context to check for
     */
    checkRequest(context: T, type: "CORS" | "AUTH"): Promise<boolean>;
}
/**
 * Filter request based on their origin
 *
 * @category CoreFeatures
 */
export declare class OriginFilter implements RequestFilter<WebContext> {
    regexs: RegExpValidator;
    constructor(origins: string[]);
    /**
     *
     * @param context
     * @returns
     */
    checkRequest(context: WebContext): Promise<boolean>;
}
/**
 * Authorize requests based on the website
 */
export declare class WebsiteOriginFilter implements RequestFilter<WebContext> {
    websites: string[];
    constructor(website: any);
    checkRequest(context: WebContext): Promise<boolean>;
}
export declare function Bean(constructor: Function): void;
export type CoreEvents = {
    /**
     * Emitted when new result is sent
     */
    "Webda.Result": EventWithContext<WebContext>;
    /**
     * Emitted when new request comes in
     */
    "Webda.Request": EventWithContext<WebContext>;
    /**
     * Emitted when a request does not match any route
     */
    "Webda.404": EventWithContext<WebContext>;
    /**
     * Emitted when Services have been initialized
     */
    "Webda.Init.Services": {
        [key: string]: Service;
    };
    /**
     * Emitted when Services have been created
     */
    "Webda.Create.Services": {
        [key: string]: Service;
    };
    /**
     * Emitted when Core is initialized
     */
    "Webda.Init": Configuration;
    /**
     * Emitted whenever a new Context is created
     */
    "Webda.NewContext": {
        context: OperationContext;
        info: ContextProviderInfo;
    };
    /**
     * Sent when route is added to context
     */
    "Webda.UpdateContextRoute": {
        context: WebContext;
    };
    [key: string]: unknown;
};
type NoSchemaResult = null;
type SchemaValidResult = true;
/**
 * This is the main class of the framework, it handles the routing, the services initialization and resolution
 *
 * @class Core
 * @category CoreFeatures
 */
export declare class Core<E extends CoreEvents = CoreEvents> extends events.EventEmitter {
    /**
     * Webda Services
     * @hidden
     */
    protected services: {
        [key: string]: Service;
    };
    /**
     * Application that generates this Core
     */
    protected application: Application;
    /**
     * Router that will route http request in
     */
    protected router: Router;
    /**
     * If Core is already initiated
     */
    protected _initiated: boolean;
    /**
     * Services who failed to create or initialize
     */
    protected failedServices: {
        [key: string]: any;
    };
    /**
     * Init promise to ensure, webda is initiated
     * Used for init() method
     */
    protected _init: Promise<void>;
    /**
     * Configuration loaded from webda.config.json
     * @hidden
     */
    protected configuration: Configuration;
    /**
     * JSON Schema validator instance
     */
    protected _ajv: Ajv;
    /**
     * JSON Schema registry
     * Save if a schema was added to Ajv already
     */
    protected _ajvSchemas: {
        [key: string]: true;
    };
    /**
     * Current executor
     */
    protected _currentExecutor: any;
    protected _configFile: string;
    /**
     * Contains the current initialization process
     */
    protected _initPromise: Promise<void>;
    /**
     * When the Core was initialized
     */
    protected _initTime: number;
    /**
     * Console logger
     * @hidden
     */
    protected logger: Logger;
    /**
     * Request Filter registry
     *
     * Added via [[Webda.registerRequestFilter]]
     * See [[CorsFilter]]
     */
    protected _requestFilters: RequestFilter<WebContext>[];
    /**
     * CORS Filter registry
     *
     * Added via [[Webda.registerCORSRequestFilter]]
     * See [[CorsFilter]]
     */
    protected _requestCORSFilters: RequestFilter<WebContext>[];
    /**
     * Worker output
     *
     * @see @webda/workout
     */
    private workerOutput;
    /**
     * Store the instance id
     */
    private instanceId;
    /**
     * Application registry
     */
    protected registry: Store<RegistryEntry>;
    /**
     * Manage encryption within the application
     */
    protected cryptoService: CryptoService;
    /**
     * Contains all operations defined by services
     */
    protected operations: {
        [key: string]: OperationDefinitionInfo;
    };
    /**
     * Cache for model to store resolution
     */
    private _modelStoresCache;
    /**
     * Cache for model to store resolution
     */
    private _modelBinariesCache;
    /**
     * Store the Core singleton
     *
     * The main storage is on the process itself
     * This second storage allow to identify dual import
     */
    private static singleton;
    /**
     * True if the dual import warning has been sent
     */
    private _dualImportWarn;
    /**
     * Registered context providers
     */
    private _contextProviders;
    /**
     * System context
     */
    protected globalContext: GlobalContext;
    /**
     *
     */
    interuptables: {
        cancel: () => Promise<void>;
    }[];
    /**
     * @params {Object} config - The configuration Object, if undefined will load the configuration file
     */
    constructor(application?: Application);
    /**
     * Register a cancelable process
     * @param interuptable
     */
    static registerInteruptableProcess(interuptable: {
        cancel: () => Promise<void>;
    }): void;
    /**
     * Unregister a cancelable process
     * @param interuptable
     */
    static unregisterInteruptableProcess(interuptable: {
        cancel: () => Promise<void>;
    }): void;
    /**
     * Get the current script location
     * @returns
     */
    private getScriptUrl;
    /**
     * Return information on the singleton import and version
     * @param singleton
     * @returns
     */
    private static getSingletonInfo;
    /**
     * Get the singleton of Webda Core
     * @returns
     */
    static get(): Core;
    /**
     * Enforce a specific store for a model
     *
     * Useful in some specific case when you want to update store dynamically
     *
     * @param model
     * @param store
     */
    setModelStore(model: Constructor<CoreModel>, store: Store): void;
    /**
     * Get the store assigned to this model
     * @param model
     * @returns
     */
    getModelStore<T extends CoreModel>(modelOrConstructor: Constructor<T> | T): Store<T>;
    /**
     * Get the service that manage a model
     * @param modelOrConstructor
     * @param attribute
     * @returns
     */
    getBinaryStore<T extends CoreModel>(modelOrConstructor: Constructor<T> | T, attribute: string): BinaryService;
    /**
     * Return Core instance id
     *
     * It is a random generated string
     */
    getInstanceId(): string;
    /**
     * Get absolute url with subpath
     * @param subpath
     */
    getApiUrl(subpath?: string): string;
    /**
     * Return application path with subpath
     *
     * Helper that redirect to this.application.getAppPath
     *
     * @param subpath
     * @returns
     */
    getAppPath(subpath?: string): string;
    /**
     * Retrieve all detected modules definition
     */
    getModules(): import("./application").CachedModule;
    /**
     * Return application definition
     */
    getApplication(): Application;
    /**
     * Get WorkerOutput
     */
    getWorkerOutput(): WorkerOutput;
    /**
     * Init one service
     * @param service
     */
    protected initService(service: string): Promise<void>;
    /**
     * Get an object from the application based on its full uuid
     * @param fullUuid
     * @param partials
     */
    getModelObject<T extends CoreModel = CoreModel>(fullUuid: string, partials?: any): Promise<T>;
    /**
     * Init Webda
     *
     * It will resolve Services init method and autolink
     */
    init(): Promise<void>;
    /**
     * Pause for time ms
     *
     * @param time ms
     */
    static sleep(time: any): Promise<void>;
    /**
     * Check if an operation can be executed with the current context
     * Not checking the input use `checkOperation` instead to check everything
     * @param context
     * @param operationId
     * @throws OperationError if operation is unknown
     * @returns true if operation can be executed
     */
    checkOperationPermission(context: OperationContext, operationId: string): boolean;
    /**
     * Check if an operation can be executed with the current context
     * @param context
     * @param operationId
     */
    checkOperation(context: OperationContext, operationId: string): Promise<void>;
    /**
     * Call an operation within the framework
     */
    callOperation(context: OperationContext, operationId: string): Promise<any>;
    /**
     * Get available operations
     * @returns
     */
    listOperations(): {
        [key: string]: Omit<OperationDefinition, "service" | "method">;
    };
    /**
     * Register a new operation within the app
     * @param operationId
     * @param definition
     */
    registerOperation(operationId: string, definition: OperationDefinition): void;
    /**
     * Register a request filtering
     *
     * Will apply to all requests regardless of the devMode
     * @param filter
     */
    registerRequestFilter(filter: RequestFilter<WebContext>): void;
    /**
     * Register a CORS request filtering
     *
     * Does not apply in devMode
     * @param filter
     */
    registerCORSFilter(filter: RequestFilter<WebContext>): void;
    /**
     * Register a new context provider
     * @param provider
     */
    registerContextProvider(provider: ContextProvider): void;
    /**
     * Validate the object with schema
     *
     * @param schema path to use
     * @param object to validate
     */
    validateSchema(webdaObject: CoreModel | string, object: any, ignoreRequired?: boolean): NoSchemaResult | SchemaValidResult;
    /**
     * Return webda current version
     *
     * @returns package version
     * @since 0.4.0
     */
    getVersion(): string;
    /**
     * To define the locales just add a locales: ['en-GB', 'fr-FR'] in your host global configuration
     *
     * @return The configured locales or "en-GB" if none are defined
     */
    getLocales(): string[];
    /**
     * Get a Logger for a class
     * @param clazz
     */
    getLogger(clazz: string | Service): Logger;
    /**
     * Add a route dynamicaly
     *
     * @param {String} url of the route can contains dynamic part like {uuid}
     * @param {Object} info the type of executor
     */
    addRoute(url: string, info: RouteInfo): void;
    /**
     * Remove a route dynamicly
     *
     * @param {String} url to remove
     */
    removeRoute(url: string): void;
    /**
     * Return current Router object
     */
    getRouter(): Router;
    /**
     * Check for a service name and return the wanted singleton or undefined if none found
     *
     * @param {String} name The service name to retrieve
     */
    getService<T extends Service>(name?: string): T;
    /**
     * Return a map of defined services
     * @returns {{}}
     */
    getServices(): {
        [key: string]: Service;
    };
    /**
     * Return a map of services that extends type
     * @param type The type of implementation
     * @returns {{}}
     */
    getServicesOfType<T extends Service>(type?: Constructor<T, [Core, string, any]>): {
        [key: string]: T;
    };
    getConfiguration(): Configuration;
    /**
     * Return a map of defined stores
     * @returns {{}}
     */
    getStores(): {
        [key: string]: Store;
    };
    /**
     * Return a map of defined models
     * @returns {{}}
     */
    getModels(): {
        [key: string]: CoreModelDefinition;
    };
    /**
     * Check for a model name and return the wanted class or throw exception if none found
     *
     * @param {String} name The model name to retrieve
     */
    getModel<T extends CoreModel = CoreModel>(name: any): CoreModelDefinition<T>;
    /**
     * Add to context information and executor based on the http context
     */
    updateContextWithRoute(ctx: WebContext): boolean;
    /**
     * Flush the headers to the response, no more header modification is possible after that
     *
     * This method should set the `context.setFlushedHeaders()` and use of `context.hasFlushedHeaders()`
     *
     * @abstract
     */
    flushHeaders(_context: WebContext): void;
    /**
     * Flush the entire response to the client
     */
    flush(_context: WebContext): void;
    /**
     * Return if Webda is in debug mode
     */
    isDebug(): boolean;
    /**
     * Return the global parameters of a domain
     */
    getGlobalParams(): any;
    /**
     * Get the system context
     * @returns
     */
    getGlobalContext(): GlobalContext;
    /**
     * Set the system context
     * @param context
     */
    setGlobalContext(context: GlobalContext): void;
    /**
     * Reinit one service
     * @param service
     */
    protected reinitService(service: string): Promise<void>;
    /**
     * Reinit all services with updated parameters
     * @param updates
     * @returns
     */
    reinit(updates: any): Promise<void>;
    /**
     * Get a full resolved service parameter
     *
     * @param service
     * @param configuration
     * @returns
     */
    getServiceParams(service: string, configuration?: {
        parameters?: any;
        services: any;
    }): any;
    protected createService(services: any, service: string): void;
    getBeans(): any;
    /**
     * @hidden
     *
     */
    protected createServices(excludes?: string[]): void;
    /**
     * A registry is a predefined store
     * @returns
     */
    getRegistry(): Store<RegistryEntry>;
    /**
     * Return the crypto service
     * @returns
     */
    getCrypto(): CryptoService;
    /**
     * Stop all services
     */
    stop(): Promise<void>;
    protected jsonFilter(key: string, value: any): any;
    static getMachineId(): string;
    /**
     * Init services and Beans along with Routes
     */
    initStatics(): void;
    /**
     * Get a context based on the info
     * @param info
     * @returns
     */
    newContext<T extends OperationContext>(info: ContextProviderInfo, noInit?: boolean): Promise<OperationContext>;
    /**
     * Create a new context for a request
     *
     * @class Service
     * @param httpContext THe HTTP request context
     * @param stream - The request output stream if any
     * @return A new context object to pass along
     */
    newWebContext<T extends WebContext>(httpContext: HttpContext, stream?: Writable, noInit?: boolean): Promise<T>;
    /**
     * Convert an object to JSON using the Webda json filter
     *
     * @class Service
     * @param {Object} object - The object to export
     * @return {String} The export of the strip object ( removed all attribute with _ )
     */
    toPublicJSON(object: any): string;
    /**
     * Return a UUID
     *
     * @param format to return different type of format
     * Plan to implement base64 and maybe base85
     */
    getUuid(format?: "ascii" | "base64" | "hex" | "binary" | "uuid"): string;
    /**
     * @override
     */
    emit<K extends keyof E>(eventType: K | symbol | string, event?: E[K], ...data: any[]): boolean;
    /**
     * Emit the event with data and wait for Promise to finish if listener returned a Promise
     */
    emitSync<K extends keyof E>(eventType: K | symbol, event?: E[K], ...data: any[]): Promise<any[]>;
    /**
     * Type the listener part
     * @param event
     * @param listener
     * @param queue
     * @returns
     */
    on<Key extends keyof E>(event: Key | symbol, listener: (evt: E[Key]) => void): this;
    /**
     * Logs
     * @param level
     * @param args
     */
    log(level: WorkerLogLevel, ...args: any[]): void;
    /**
     * Retrieve a global parameter
     */
    parameter(name: string): any;
    /**
     * Verify if a request can be done
     *
     * @param context Context of the request
     */
    protected checkRequest(ctx: WebContext): Promise<boolean>;
    /**
     * Verify if an origin is allowed to do request on the API
     *
     * @param context Context of the request
     */
    protected checkCORSRequest(ctx: WebContext): Promise<boolean>;
    /**
     * Export OpenAPI
     * @param skipHidden
     * @returns
     */
    exportOpenAPI(skipHidden?: boolean): OpenAPIV3.Document;
    /**
     * Get a metric object
     *
     * Use the Service.getMetric method if possible
     *
     * This is map from prometheus 3 types of metrics
     * Our hope is that we can adapt them to export to other
     * metrics system if needed
     *
     * @param type
     * @param configuration
     * @returns
     */
    getMetric<T = Gauge | Counter | Histogram>(type: Constructor<T, [MetricConfiguration<T>]>, configuration: MetricConfiguration<T>): T;
}
/**
 * Generic type for metric
 */
export type MetricConfiguration<T = Counter | Gauge | Histogram, K extends string = string> = T extends Counter ? CounterConfiguration<K> : T extends Gauge ? GaugeConfiguration<K> : HistogramConfiguration<K>;
/**
 * Export a Registry type alias
 */
export type Registry<T extends CoreModel = RegistryEntry> = Store<T>;
export { Counter, Gauge, Histogram };
