import { Elastic6SimpleQueriable, CoreModel } from 'lemon-model';
import { StorageService } from './storage-service';
import { GeneralAPIController } from '../../controllers/general-api-controller';
/**
 * class: `CoreKey`
 * - to represent internal partition-key and model-id.
 */
export interface CoreKey<ModelType extends string> {
    /**
     * defualt internal partition-key
     */
    _id?: string;
    /**
     * namespace
     */
    ns?: string;
    /**
     * model-id
     */
    id: string;
    /**
     * model-type
     */
    type: ModelType;
}
/**
 * class: `CoreKeyMakeable`
 * - make internal key by type + id.
 */
export interface CoreKeyMakeable<ModelType extends string> {
    /**
     * get key object w/ internal partition-key (default as _id).
     *
     * @param type  type of model
     * @param id    id of model in type
     */
    asKey$(type: ModelType, id: string): CoreKey<ModelType>;
    /**
     * get key-string to use for query. (USED FOR CUSTOM ID NAME);
     *
     * @param type  type of model
     * @param id    id of model in type
     */
    asKey?(type: ModelType, id: string): string;
}
/**
 * type: ModelFilter
 *
 * @param model     the new model to update
 * @param origin    the old model (or 2nd model)
 * @return          the updated model.
 */
export declare type CoreModelFilter<T> = (model: T, origin?: T) => T;
/**
 * class: `CoreModelFilterable`
 * - support filters.
 */
export interface CoreModelFilterable<T> {
    afterRead: CoreModelFilter<T>;
    beforeSave: CoreModelFilter<T>;
    afterSave: CoreModelFilter<T>;
    beforeUpdate: CoreModelFilter<T>;
    afterUpdate: CoreModelFilter<T>;
}
/**
 * class: `StorageMakeable`
 * - makeable of `TypedStorageService`
 */
export interface StorageMakeable<T extends CoreModel<ModelType>, ModelType extends string> {
    /**
     * create storage-service w/ fields list.
     * @param type      type of model
     * @param fields    list of field (properties)
     * @param filter    filter of model.
     */
    makeStorageService(type: ModelType, fields: string[], filter: CoreModelFilterable<T>): TypedStorageService<T, ModelType>;
}
/**
 * class: `GeneralKeyMaker`
 * - use ':' as delimiter to join [ns, type, id]
 */
export declare class GeneralKeyMaker<ModelType extends string> implements CoreKeyMakeable<ModelType> {
    readonly NS: string;
    readonly DELIMITER: string;
    constructor(ns?: string, delimiter?: string);
    asKey$(type: ModelType, id: string): {
        ns: string;
        id: string;
        type: ModelType;
        _id: string;
    };
}
/**
 * class: `GeneralModelFilter`
 * - general model-filter with differential update.
 * - to customize, override this class.
 */
export declare class GeneralModelFilter<T extends CoreModel<ModelType>, ModelType extends string> implements CoreModelFilterable<T> {
    readonly FIELDS: string[];
    /**
     * default constructor
     */
    constructor(fields: string[]);
    /**
     * parse `.meta` to json
     * @param model     the current model
     * @param origin    the origin model
     */
    afterRead(model: T, origin?: T): T;
    /**
     * filter for before saving.
     * - make sure data conversion
     * - move the unknown fields to `.meta`.
     *
     * @param model     the current model
     * @param origin    the origin model
     */
    beforeSave(model: T, origin?: T): T;
    /**
     * called after saving the model.
     * - parse `.meta` back to json object.
     *
     * @param model     the saved model
     * @param origin    the origin model.
     */
    afterSave(model: T, origin?: T): T;
    /**
     * called before updating the model.
     * @param model         the updated model
     * @param incrementals  (optional) incremental fields.
     */
    beforeUpdate(model: T, incrementals?: T): T;
    /**
     * called after updating the model.
     * @param model     the updated model
     */
    afterUpdate(model: T): T;
    /**
     * override this `onBeforeSave()` in sub-class.
     * @param model     the current model
     * @param origin    (optional) the origin model
     */
    onBeforeSave(model: T, origin?: T): T;
}
/**
 * class: `ProxyStorageService`
 * - support `nextSeq()`, `doLock()`, `doRelease()`
 * - proxed storage-service to wrap the parent storage-service w/ more features.
 * - table is supposed to have internal-key as `_id` string.
 *
 * **Usage**
 * ```js
 * type MyType = '' | 'test';
 * interface MyModel extends CoreModel<MyType>{
 *  name?: string;
 * }
 * const storage = new ProxyStorageService<MyModel, MyType>(this, 'TestTable', ['id','name']);
 * const $test = storage.makeTypedStorageService('test');
 * ```
 */
export declare class ProxyStorageService<T extends CoreModel<ModelType>, ModelType extends string> implements StorageService<T> {
    static readonly AUTO_SEQUENCE = 1000000;
    static readonly TYPE_SEQUENCE = "sequence";
    readonly idName: string;
    readonly service: CoreKeyMakeable<ModelType>;
    readonly storage: StorageService<T>;
    readonly filters: CoreModelFilterable<T>;
    /**
     * create proxed storage-service.
     *
     * @param service   service to support `CoreKeyMakeable`
     * @param storage   table-name or the parent storage-service
     * @param fields    list of fields.
     * @param filters   filters of `CoreModelFilterable`
     * @param idName    (optional) internal partition-key (default as '_id')
     */
    constructor(service: CoreKeyMakeable<ModelType>, storage: StorageService<T> | string, fields: string[], filters?: CoreModelFilterable<T>, idName?: string);
    /**
     * factory function to create this `proxy-storage-service`
     * @param service   key-makeable
     * @param table     table-name
     * @param fields    list of fields.
     * @param filters   model filter.
     * @param idName    (optional) internal partition-key (default as '_id')
     */
    static create<T extends CoreModel<ModelType>, ModelType extends string>(service: CoreKeyMakeable<ModelType>, table: string, fields?: string[], filters?: CoreModelFilterable<T>, idName?: string): ProxyStorageService<T, ModelType>;
    /**
     * say hello()
     */
    hello: () => string;
    /**
     * read by _id
     */
    read: (_id: string) => Promise<T>;
    /**
     * read or create by _id
     */
    readOrCreate: (_id: string, model: T) => Promise<T>;
    /**
     * save by _id
     */
    save: (_id: string, model: T) => Promise<T>;
    /**
     * update by _id
     */
    update: (_id: string, model: T, incrementals?: T) => Promise<T>;
    /**
     * increment by _id
     */
    increment: (_id: string, model: T, $update?: T) => Promise<T>;
    /**
     * delete by _id
     */
    delete: (_id: string) => Promise<T>;
    /**
     * get key-id by type+id
     */
    asKey: (type: ModelType, id: string | number) => string;
    /**
     * get next auto-sequence number.
     *
     * @param type      type of seqeunce.
     * @param nextInit  (optional) initial next value if not exist.
     * @param nextStep  (optional) the incremental step to get next. (default 1)
     */
    nextSeq(type: ModelType, nextInit?: number, nextStep?: number): Promise<number>;
    /**
     * get uuid by type.
     * @param type
     */
    nextUuid(type?: ModelType): Promise<string>;
    /**
     * timer to generate the current-time (msec)
     */
    private $timer;
    setTimer: (timer: () => number) => () => number;
    getTime: () => number;
    /**
     * get time-stamp as now.
     */
    asTime(currentTime?: number): {
        createdAt: number;
        updatedAt: number;
        deletedAt: number;
    };
    /**
     * delete sequence-key.
     * @param type      type of seqeunce.
     */
    clearSeq(type: ModelType): Promise<void>;
    /**
     * read model by key + id with optional auto creation.
     *
     * @param type      model-type
     * @param id        node-id
     * @param $create   (optional) initial model if not exist. (or throw 404 error)
     */
    doRead(type: ModelType, id: string, $create?: T): Promise<T>;
    /**
     * delete model by id.
     *
     * @param type      model-type
     * @param id        node-id
     * @param destroy   flag to destroy (real delete)
     */
    doDelete(type: ModelType, id: string, destroy?: boolean): Promise<T>;
    /**
     * update model (or it will create automatically)
     *
     * @param type      model-type
     * @param id        node-id
     * @param node      model
     * @param incrementals (optional) fields to increment
     */
    doUpdate(type: ModelType, id: string, node: T, incrementals?: T): Promise<T>;
    /**
     * update model (or it will create automatically)
     *
     * @param type      model-type
     * @param id        node-id
     */
    doIncrement(type: ModelType, id: string, $inc: T, $up: T): Promise<T>;
    /**
     * save model by checking origin node.
     * - use `doSave()` rather than `doUpdate()` for both create & update.
     * - if `$create` is null, throw 404 error it if not found.
     *
     * @param type      model-type
     * @param id        node-id
     * @param node      node to save (or update)
     * @param $create   (optional) initial creation model if not found.
     */
    doSave(type: ModelType, id: string, node: T, $create?: T): Promise<T>;
    /**
     * lock data-entry by type+id w/ limited time tick
     * - WARN! must release lock by `doRelease()`
     *
     * `total-waited-time = tick * interval (msec)`
     *
     * @param type      model-type
     * @param id        model-id
     * @param tick      tick count to wait.
     * @param interval  timeout interval per each tick (in msec, default 1000 = 1sec)
     */
    doLock(type: ModelType, id: string, tick?: number, interval?: number): Promise<boolean>;
    /**
     * release lock by resetting lock = 0.
     *
     * @param type  model-type
     * @param id    model-id
     */
    doRelease(type: ModelType, id: string): Promise<boolean>;
    /**
     * create storage-service w/ fields list.
     * - idName should be `_id`
     *
     * @param table     table-name or dummy file name (ex: `dummy-data.yml`).
     * @param fields    required for dynamo table.
     * @param idName    internal partition-key name (default '_id')
     */
    static makeStorageService<T>(table: string, fields?: string[], idName?: string): StorageService<T>;
    /**
     * create proxy-storage-service by type
     * @param type      model-type
     */
    makeTypedStorageService<U extends T>(type: ModelType): TypedStorageService<U, ModelType>;
}
/**
 * class: `TypedStorageService`
 * - wrap id with type + id.
 */
export declare class TypedStorageService<T extends CoreModel<ModelType>, ModelType extends string> implements StorageService<T> {
    readonly type: ModelType;
    readonly storage: ProxyStorageService<T, ModelType>;
    constructor(service: ProxyStorageService<T, ModelType>, type: ModelType);
    /**
     * show self service name
     */
    hello: () => string;
    /**
     * get next auto-sequence id in number like `1000003`.
     *
     * @param step (optional) next step value (must be >0) (`0` means getting the current value)
     */
    nextId: (step?: number) => Promise<number>;
    /**
     * get uuid like `d01764cd-9ef2-41e2-9e88-68e79555c979`
     */
    nextUuid: () => Promise<string>;
    /**
     * read model by key + id with optional auto creation.
     * - throws '404 NOT FOUND' if not found.
     *
     * @param id        node-id
     */
    read: (id: string | number) => Promise<T>;
    /**
     * read model by key + id with optional auto creation.
     *
     * @param id        node-id
     * @param model     initial model if not exist. (or throw 404 error)
     */
    readOrCreate: (id: string | number, model: T) => Promise<T>;
    /**
     * update model (or it will create automatically)
     *
     * @param id        node-id
     * @param model     model to update
     * @param incrementals (optional) fields to increment.
     */
    update: (id: string | number, model: T, incrementals?: T) => Promise<T>;
    /**
     * insert model w/ auto generated id
     *
     * @param model     model to insert
     */
    insert: (node: T) => Promise<T>;
    /**
     * update model (or it will create automatically)
     *
     * ```ts
     * //before: { count: 1 };
     * const res = await storage.increment(1, { count: 2 }, { total: 2 });
     * //after : { count: 3, total: 2 }
     * ```
     *
     * @param id            node-id
     * @param $increments   model only with numbers
     */
    increment: (id: string | number, $increments: T, $update?: T) => Promise<T>;
    /**
     * delete model by id.
     *
     * @param id        node-id
     * @param destroy   flag to destroy (real delete)
     */
    delete: (id: string | number, destroy?: boolean) => Promise<T>;
    /**
     * save model by checking origin node.
     * - use `doSave()` rather than `doUpdate()` for both create & update.
     * - if `$create` is null, throw 404 error it if not found.
     *
     * @param id        node-id
     * @param node      node to save (or update)
     * @param $create   (optional) initial creation model.
     */
    save: (id: string | number, model: T, $create?: T) => Promise<T>;
    /**
     * lock data-entry by type+id w/ limited time tick
     * - WARN! must release lock by `release(id)`
     *
     * `total-waited-time = tick * interval (msec)`
     *
     * **[UPDATES]**
     * 1. read original node (or, throw 404 error)
     * 2. use internal lock.
     *
     * @param id        model-id to lock
     * @param tick      tick count to wait.
     * @param interval  timeout interval per each tick (in msec, default 1000 = 1sec)
     */
    lock: (id: string | number, tick?: number, interval?: number) => Promise<boolean>;
    /**
     * release lock by resetting lock = 0.
     * @param id    model-id
     */
    release: (id: string | number) => Promise<boolean>;
    /**
     * using `lock()`, guard func with auto lock & release.
     *
     * ```ts
     * const res = await storage.guard(async ()=>{
     *  return 'abc';
     * });
     * // res === 'abc'
     * ```
     */
    guard: <T_1>(id: string | number, handler: () => T_1 | Promise<T_1>, tick?: number, interval?: number) => Promise<any>;
    /**
     * make `UniqueFieldManager` for field.
     */
    makeUniqueFieldManager: (field: string) => UniqueFieldManager<T, ModelType>;
    /**
     * make `GeneralAPIController` for REST API w/ supporting basic CRUD
     */
    makeGeneralAPIController: (search?: Elastic6SimpleQueriable<any>, uniqueField?: string) => GeneralAPIController<this, string>;
}
/**
 * class: `ModelUtil`
 * - Helper functions for model.
 */
export declare class ModelUtil {
    static selfRead: <T>(self: any, key: string, defValue?: T) => T;
    static selfPop: <T>(self: any, key: string, defValue?: T) => T;
    /**
     * attach `.pop()` method to object.
     *
     * ```js
     * const data = CoreModelUtil.buildPop({'a':1});
     * assert( 1 === data.pop('a) );
     * const final = data.pop();
     * assert( final == data );
     */
    static buildPop: (thiz: any, popName?: string) => any;
}
/**
 * class: `UniqueFieldManager`
 * - support `.{field}` is unique in typed-storage-service.
 * - make lookup data entry to save the reverse mapping to origin id.
 * - set `.stereo` as '#' to mark as lookup. (to filter out from Elastic.search())
 * - set `.id` as `#{field}/{name}` or `#{name}`.
 * - set `.meta` as origin id.
 */
export declare class UniqueFieldManager<T extends CoreModel<ModelType>, ModelType extends string> {
    readonly type: ModelType;
    readonly field: string;
    readonly storage: TypedStorageService<T, ModelType>;
    constructor(storage: TypedStorageService<T, ModelType>, field?: string);
    hello: () => string;
    /**
     * validate value format
     * - just check empty string.
     * @param value unique value in same type+field.
     */
    validate(value: string): boolean;
    /**
     * convert to internal id by value
     * @param value unique value in same type group.
     */
    asLookupId(value: string): string;
    /**
     * lookup model by value
     * - use `.meta` property to link with the origin.
     * - mark `.stereo` as to '#' to distinguish normal.
     *
     * @param value unique value in same type group.
     * @param $creates (optional) create-set if not found.
     */
    findOrCreate(value: string, $creates?: T): Promise<T>;
    /**
     * update lookup table (or create)
     *
     * @param model target model
     * @param value (optional) new value of model.
     */
    updateLookup(model: T, value?: string): Promise<T>;
}
