import { Readable } from "stream";
import { Counter, WebdaError } from "../index.js";
import { CoreModel } from "../models/coremodel.js";
import { MappingService, Store } from "../stores/store.js";
import { OperationContext, WebContext } from "../utils/context.js";
import { Service, ServiceParameters } from "./service.js";
/**
 * Represent basic EventBinary
 */
export interface EventBinary {
    object: BinaryFileInfo;
    service: BinaryService;
    /**
     * In case the Context is known
     */
    context?: OperationContext;
}
export interface EventBinaryUploadSuccess extends EventBinary {
    target: CoreModel;
}
/**
 * Sent before metadata are updated to allow alteration of the modification
 */
export interface EventBinaryMetadataUpdate extends EventBinaryUploadSuccess {
    target: CoreModel;
    metadata: BinaryMetadata;
}
/**
 * Emitted if binary does not exist
 */
export declare class BinaryNotFoundError extends WebdaError.CodeError {
    constructor(hash: string, storeName: string);
}
export interface BinaryFileInfo {
    /**
     * Hash of the binary
     */
    hash?: string;
    /**
     * Will be computed by the service
     *
     * hash of the content prefixed by 'WEBDA'
     */
    challenge?: string;
    /**
     * Size of the file
     */
    size: number;
    /**
     * Name of the file
     */
    name: string;
    /**
     * Mimetype
     */
    mimetype: string;
    /**
     * Metadatas stored along with the binary
     */
    metadata?: BinaryMetadata;
}
/**
 * Represent files attached to a model
 */
export type BinaryFiles = BinaryFileInfo[];
/**
 * Represent a file to store
 * @WebdaSchema
 */
export declare abstract class BinaryFile<T = any> implements BinaryFileInfo {
    /**
     * Current name
     */
    name: string;
    /**
     * Original name
     */
    originalname?: string;
    /**
     * Size of the binary
     */
    size: number;
    /**
     * Mimetype of the binary
     */
    mimetype: string;
    /**
     * Will be computed by the service
     *
     * hash of the content prefixed by 'WEBDA'
     */
    challenge?: string;
    /**
     * Will be computed by the service
     *
     * hash of the content
     */
    hash?: string;
    /**
     * Metadatas stored along with the binary
     */
    metadata?: T;
    constructor(info: BinaryFileInfo);
    /**
     * Set the information
     * @param info
     */
    set(info: BinaryFileInfo): void;
    /**
     * Retrieve a plain BinaryFileInfo object
     * @returns
     */
    toBinaryFileInfo(): BinaryFileInfo;
    abstract get(): Promise<Readable>;
    /**
     * Create hashes
     * @param buffer
     * @returns
     */
    getHashes(): Promise<{
        hash: string;
        challenge: string;
    }>;
}
export declare class LocalBinaryFile extends BinaryFile {
    /**
     * Path on the hard drive
     */
    path: string;
    constructor(filePath: string);
    /**
     * @override
     */
    get(): Promise<Readable>;
}
export declare class MemoryBinaryFile extends BinaryFile {
    /**
     * Content
     */
    buffer: Buffer;
    constructor(buffer: Buffer | string, info?: Partial<BinaryFileInfo>);
    /**
     * @override
     */
    get(): Promise<Readable>;
}
/**
 * Define the metadata for a Binary
 */
export type BinaryMetadata = any;
/**
 * This is a map used to retrieve binary
 *
 * @class BinaryMap
 */
export declare class BinaryMap<T = any> extends BinaryFile<T> {
    /**
     * Current context
     */
    __ctx: OperationContext;
    /**
     * Link to the binary store
     */
    __store: BinaryService;
    constructor(service: BinaryService, obj: BinaryFileInfo);
    /**
     * Get the binary data
     *
     * @returns
     */
    get(): Promise<Readable>;
    /**
     * Get into a buffer
     */
    getAsBuffer(): Promise<Buffer>;
    /**
     * Download the binary to a path
     *
     * Shortcut to call {@link Binary.downloadTo} with current object
     *
     * @param filename
     */
    downloadTo(filename: string): Promise<void>;
    /**
     * Set the http context
     * @param ctx
     */
    setContext(ctx: OperationContext): void;
}
/**
 * One Binary instance
 */
export declare class Binary<T = any> extends BinaryMap<T> {
    protected model: CoreModel;
    protected attribute: string;
    protected empty: boolean;
    constructor(attribute: string, model: CoreModel);
    /**
     * isEmpty
     * @returns
     */
    isEmpty(): boolean;
    /**
     * Ensure empty is set correctly
     * @param info
     */
    set(info: BinaryFileInfo): void;
    /**
     * Replace the binary
     * @param id
     * @param ctx
     * @returns
     */
    upload(file: BinaryFile): Promise<void>;
    /**
     * Delete the binary, if you need to replace just use upload
     */
    delete(): Promise<void>;
    /**
     * Return undefined if no hash
     * @returns
     */
    toJSON(): this;
}
/**
 * Define a Binary map stored in a Binaries collection
 */
export declare class BinariesItem<T = any> extends BinaryMap<T> {
    protected parent: BinariesImpl;
    constructor(parent: BinariesImpl, info: BinaryFileInfo);
    /**
     * Replace the binary
     * @param id
     * @param ctx
     * @returns
     */
    upload(file: BinaryFile): Promise<void>;
    /**
     * Delete the binary, if you need to replace just use upload
     */
    delete(): Promise<void>;
}
/**
 * Define a collection of Binary
 */
export declare class BinariesImpl<T = any> extends Array<BinariesItem<T>> {
    __service: BinaryService;
    protected model: CoreModel;
    protected attribute: string;
    assign(model: CoreModel, attribute: string): this;
    pop(): BinariesItem<T>;
    slice(): BinariesItem<T>[];
    unshift(): number;
    shift(): BinariesItem<T>;
    push(...args: any[]): number;
    /**
     * Upload a file to this model
     * @param file
     */
    upload(file: BinaryFile, replace?: BinariesItem): Promise<void>;
    /**
     * Delete an item
     * @param item
     */
    delete(item: BinariesItem): Promise<void>;
}
/**
 * Define a collection of Binary with a Readonly and the upload method
 */
export type Binaries<T = any> = Readonly<Array<BinariesItem<T>>> & {
    upload: (file: BinaryFile) => Promise<void>;
};
export declare class BinaryParameters extends ServiceParameters {
    /**
     * Define the map to Object collection
     *
     * key is a Store name
     * the string[] represent all valids attributes to store files in
     * @deprecated
     */
    map: {
        [key: string]: string[];
    };
    /**
     * Define the map of models
     * * indicates all models
     *
     * key is a Store name
     * the string[] represent all valids attributes to store files in * indicates all attributes
     */
    models: {
        [key: string]: string[];
    };
    /**
     * Expose the service to http
     * @deprecated will be removed in 4.0
     */
    expose?: {
        /**
         * URL to expose the service to
         */
        url: string;
        /**
         * Restrict some APIs
         */
        restrict?: {
            /**
             * Restrict GET
             */
            get?: boolean;
            /**
             * Restrict POST
             */
            create?: boolean;
            /**
             * Restrict DELETE
             */
            delete?: boolean;
            /**
             * Restrict update of metadata
             */
            metadata?: boolean;
        };
    };
    constructor(params: any, _service: Service);
}
export type BinaryEvents = {
    /**
     * Emitted when someone download a binary
     */
    "Binary.Get": EventBinary;
    "Binary.UploadSuccess": EventBinaryUploadSuccess;
    "Binary.MetadataUpdate": EventBinaryMetadataUpdate;
    "Binary.MetadataUpdated": EventBinaryUploadSuccess;
    "Binary.Create": EventBinaryUploadSuccess;
    "Binary.Delete": EventBinary;
};
/**
 * Define a BinaryModel with infinite field for binary map
 */
export type BinaryModel<T = {
    [key: string]: BinaryMap[];
}> = CoreModel & T;
/**
 * This is an abstract service to represent a storage of files
 * The binary allow you to expose this service as HTTP
 *
 * It supports two modes:
 *  - attached to a CoreModel (attach, detach, reattach)
 *  - pure storage with no managed id (read, write, delete)
 *
 * As we have deduplication builtin you can get some stats
 * - getUsageCount(hash)
 * - getUsageCountForRaw()
 * - getUsageCountForMap()
 *
 * The Binary storage should store only once a binary and reference every object that are used by this binary, so it can be cleaned.
 *
 *
 * @see FileBinary
 * @see S3Binary
 *
 * @exports
 * @abstract
 * @WebdaModda Binary
 */
export declare abstract class BinaryService<T extends BinaryParameters = BinaryParameters, E extends BinaryEvents = BinaryEvents> extends Service<T, E> implements MappingService<BinaryMap> {
    _lowercaseMaps: any;
    metrics: {
        upload: Counter;
        download: Counter;
        delete: Counter;
        metadataUpdate: Counter;
    };
    /**
     * @override
     */
    initMetrics(): void;
    /**
     * Redirect to the temporary link to S3 object
     * or return it if returnInfo=true
     *
     * @param ctx of the request
     * @param returnInfo
     */
    httpGet(context: WebContext): Promise<void>;
    /**
     * Get a UrlFromObject
     *
     */
    getRedirectUrlFromObject(binaryMap: BinaryMap, _context: OperationContext, _expires?: number): Promise<null | string>;
    /**
     * Define if binary is managed by the store
     * @param modelName
     * @param attribute
     * @returns -1 if not managed, 0 if managed but by default, 1 if managed and in the map, 2 if explicit with attribute and model
     */
    handleBinary(modelName: string, attribute: string): -1 | 0 | 1 | 2;
    /**
     * When you store a binary to be able to retrieve it you need to store the information into another object
     *
     * If you have a User object define like this : User = {'name': 'Remi', 'uuid': 'Loopingz'}
     * You will call the `store(userStore, 'Loopingz', 'images', filedata, {'type':'profile'})`
     * After a successful call the object will look like
     * ```
     * User = {
     *  'name': 'Remi',
     *  'uuid': 'Loopingz',
     *  'images': [
     *    {'type':'profile','hash':'a12545...','size':1245,'mime':'application/octet'}
     *   ]
     * }
     * ```
     *
     *
     * @param {CoreModel} object The object uuid to get from the store
     * @param {String} property The object property to add the file to
     * @param {Object} file The file by itself
     * @param {Object} metadata to add to the binary object
     * @emits 'binaryCreate'
     */
    abstract store(object: CoreModel, property: string, file: BinaryFile, metadata?: BinaryMetadata): Promise<void>;
    /**
     * The store can retrieve how many time a binary has been used
     */
    abstract getUsageCount(hash: string): Promise<number>;
    /**
     * Delete a binary
     *
     * @param {CoreModel} object The object uuid to get from the store
     * @param {String} property The object property to add the file to
     * @param {Number} index The index of the file to change in the property
     * @emits 'binaryDelete'
     */
    abstract delete(object: CoreModel, property: string, index?: number): Promise<void>;
    /**
     * Get a binary
     *
     * @param {Object} info The reference stored in your target object
     * @emits 'binaryGet'
     */
    get(info: BinaryMap): Promise<Readable>;
    /**
     * Download a binary to a file
     *
     * @param {Object} info The reference stored in your target object
     * @param {String} filepath to save the binary to
     */
    downloadTo(info: BinaryMap, filename: any): Promise<void>;
    /**
     * @override
     */
    resolve(): this;
    abstract _get(info: BinaryMap): Promise<Readable>;
    /**
     * Init the declared maps, adding reverse maps
     *
     * @param map
     */
    initMap(map: any): void;
    /**
     * Based on the raw Map init a BinaryMap
     * @param obj
     * @returns
     */
    newModel(obj: any): BinaryMap;
    /**
     * Read a stream to a buffer
     *
     * @param stream
     * @returns
     */
    static streamToBuffer(stream: Readable): Promise<Buffer>;
    /**
     * Check if a map is defined
     *
     * @param name
     * @param property
     */
    protected checkMap(object: CoreModel, property: string): void;
    /**
     * Ensure events are sent correctly after an upload and update the BinaryFileInfo in targetted object
     */
    uploadSuccess(object: BinaryModel, property: string, fileInfo: BinaryFileInfo | {
        toBinaryFileInfo: () => BinaryFileInfo;
    }): Promise<void>;
    /**
     * Cascade delete the object
     *
     * @param info of the map
     * @param uuid of the object
     */
    abstract cascadeDelete(info: BinaryMap, uuid: string): Promise<void>;
    /**
     *
     * @param targetStore
     * @param object
     * @param property
     * @param index
     * @returns
     */
    deleteSuccess(object: BinaryModel, property: string, index?: number): Promise<any>;
    /**
     * Get file either from multipart post or raw
     * @param req
     * @returns
     */
    _getFile(req: WebContext): Promise<BinaryFile>;
    /**
     * @override
     */
    initRoutes(): void;
    /**
     * Init the Binary system routes
     *
     * Making sure parameters.expose exists prior
     */
    _initRoutes(): void;
    /**
     * Return the name of the service for OpenAPI
     * @returns
     */
    protected getOperationName(): string;
    /**
     * Based on the request parameter verify it match a known mapping
     * @param ctx
     * @returns
     */
    _verifyMapAndStore(ctx: WebContext): Store<CoreModel>;
    /**
     * By default no challenge is managed so throws 404
     *
     * @param ctx
     */
    putRedirectUrl(_ctx: WebContext): Promise<{
        url: string;
        method?: string;
        headers?: {
            [key: string]: string;
        };
    }>;
    /**
     * Mechanism to add a data based on challenge
     */
    httpChallenge(ctx: WebContext<BinaryFile>): Promise<void>;
    /**
     * Manage the different routes
     * @param ctx
     */
    httpRoute(ctx: WebContext): Promise<void>;
}
