/// <reference types="node" />

import { EventEmitter } from 'events';
import { IncomingHttpHeaders } from 'http';
import { IncomingMessage } from 'http';
import { ListenOptions } from 'net';
import { OutgoingHttpHeaders } from 'http';
import { Server } from 'http';
import { ServerResponse } from 'http';
import { Socket } from 'net';
import { Stream } from 'stream';
import { URL as URL_2 } from 'url';
import { URLSearchParams as URLSearchParams_2 } from 'url';

export declare function compose<Context>(composables: TunComposable<Context>[]): (ctx: Context, next?: TunComposable<Context> | undefined) => Promise<any>;

/**
 * Wrapped file for formidable
 */
declare interface File_2 {
    size: number;
    path: string;
    name: string;
    type: string;
    lastModifiedDate?: Date;
    hash?: string;
    toJSON(): Object;
}
export { File_2 as File }

/* Excluded from this release type: _getRequestCookieByName */

export declare class HttpError extends Error {
    status: number;
    constructor({ status, message, error }: HttpErrorOptions);
}

export declare interface HttpErrorOptions {
    status?: number;
    message?: string;
    error?: Error;
}

/**
 * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
 */
export declare const HttpMethod: {
    readonly GET: "GET";
    readonly HEAD: "HEAD";
    readonly POST: "POST";
    readonly PUT: "PUT";
    readonly DELETE: "DELETE";
    readonly CONNECT: "CONNECT";
    readonly OPTIONS: "OPTIONS";
    readonly TRACE: "TRACE";
    readonly PATCH: "PATCH";
};

/**
 * @see https://stackoverflow.com/questions/50784444/add-description-attribute-to-enum-and-read-this-description-in-typescript
 */
export declare const HttpStatus: {
    readonly CONTINUE: 100;
    readonly SWITCHING_PROTOCOLS: 101;
    readonly PROCESSING: 102;
    readonly OK: 200;
    readonly CREATED: 201;
    readonly ACCEPTED: 202;
    readonly NON_AUTHORITATIVE_INFORMATION: 203;
    readonly NO_CONTENT: 204;
    readonly RESET_CONTENT: 205;
    readonly PARTIAL_CONTENT: 206;
    readonly MULTI_STATUS: 207;
    readonly ALREADY_REPORTED: 208;
    readonly IM_USED: 226;
    readonly MULTIPLE_CHOICES: 300;
    readonly MOVED_PERMANENTLY: 301;
    readonly FOUND: 302;
    readonly SEE_OTHER: 303;
    readonly NOT_MODIFIED: 304;
    readonly USE_PROXY: 305;
    readonly TEMPORARY_REDIRECT: 307;
    readonly PERMANENT_REDIRECT: 308;
    readonly BAD_REQUEST: 400;
    readonly UNAUTHORIZED: 401;
    readonly PAYMENT_REQUIRED: 402;
    readonly FORBIDDEN: 403;
    readonly NOT_FOUND: 404;
    readonly METHOD_NOT_ALLOWED: 405;
    readonly NOT_ACCEPTABLE: 406;
    readonly PROXY_AUTHENTICATION_REQUIRED: 407;
    readonly REQUEST_TIMEOUT: 408;
    readonly CONFLICT: 409;
    readonly GONE: 410;
    readonly LENGTH_REQUIRED: 411;
    readonly PRECONDITION_FAILED: 412;
    readonly PAYLOAD_TOO_LARGE: 413;
    readonly URI_TOO_LONG: 414;
    readonly UNSUPPORTED_MEDIA_TYPE: 415;
    readonly RANGE_NOT_SATISFIABLE: 416;
    readonly EXPECTATION_FAILED: 417;
    readonly IM_A_TEAPOT: 418;
    readonly UNPROCESSABLE_ENTITY: 422;
    readonly LOCKED: 423;
    readonly FAILED_DEPENDENCY: 424;
    readonly UPGRADE_REQUIRED: 426;
    readonly PRECONDITION_REQUIRED: 428;
    readonly TOO_MANY_REQUESTS: 429;
    readonly REQUEST_HEADER_FIELDS_TOO_LARGE: 431;
    readonly INTERNAL_SERVER_ERROR: 500;
    readonly NOT_IMPLEMENTED: 501;
    readonly BAD_GATEWAY: 502;
    readonly SERVICE_UNAVAILABLE: 503;
    readonly GATEWAY_TIMEOUT: 504;
    readonly HTTP_VERSION_NOT_SUPPORTED: 505;
    readonly VARIANT_ALSO_NEGOTIATES: 506;
    readonly INSUFFICIENT_STORAGE: 507;
    readonly LOOP_DETECTED: 508;
    readonly NOT_EXTENDED: 510;
    readonly NETWORK_AUTHENTICATION_REQUIRED: 511;
};

/**
 * Map for ranslate `HttpStatus`
 */
export declare const HttpStatusMessage: Record<number, string>;

export declare const mimeExtMap: Record<string, string>;

export declare const RAW_REQUEST: unique symbol;

export declare const RAW_RESPONSE: unique symbol;

/* Excluded from this release type: _stringifyTunCookie */

export declare class TunApplication extends EventEmitter {
    middleware: TunComposable<TunContext>[];
    mountObj: Record<string, any>;
    constructor();
    use(fn: TunComposable<TunContext> | TunComposable<TunContext>): this;
    mount(name: string, obj: any): void;
    callback(): (_req: IncomingMessage, _res: ServerResponse) => Promise<void>;
    listen(option?: ListenOptions): Server;
}

export declare type TunComposable<Context> = (ctx: Context, next: () => Promise<any>) => Promise<any> | void;

export declare class TunContext {
    req: TunRequest;
    res: TunResponse;
    /**
     * sth you may need to store and access
     */
    state: Record<string, any>;
    constructor(_req: IncomingMessage, _res: ServerResponse);
    /**
     * shorthand for `res.body`
     */
    get body(): string | Object | any[] | Stream | Buffer | null;
    set body(val: string | Object | any[] | Stream | Buffer | null);
    getRequestCookie(name: string): string | undefined;
}

export declare interface TunCookie {
    name: string;
    value: string;
    domain?: string;
    path?: string;
    maxAge?: number;
    expires?: Date;
    httpOnly?: boolean;
    secure?: boolean;
}

export declare class TunRequest {
    #private;
    [RAW_REQUEST]: IncomingMessage;
    /**
     * formdata fields
     * @see [bodyparser](https://github.com/tunframework/tun-bodyparser)
     */
    fields: Record<string, string>;
    _fields: Record<string, string | Array<string>>;
    /**
     * formdata files
     * @see [bodyparser](https://github.com/tunframework/tun-bodyparser)
     */
    files: Record<string, File_2>;
    _files: Record<string, File_2 | File_2[]>;
    /**
     * request body
     * @see [bodyparser](https://github.com/tunframework/tun-bodyparser)
     */
    body: any;
    /**
     * @example "/product/abc" matches "/product/:id", ctx.req.slugs.id === "abc"
     * @see [rest-router](https://github.com/tunframework/tun-rest-router)
     */
    slugs: Record<string, any>;
    constructor(req: IncomingMessage, options?: TunRequestOptions);
    get header(): IncomingHttpHeaders;
    set header(val: IncomingHttpHeaders);
    get headers(): IncomingHttpHeaders;
    set headers(val: IncomingHttpHeaders);
    /**
     *
     * http methods
     *
     * refers: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
     *
     * Get/Set request method, useful for implementing middleware such as methodOverride().
     *
     */
    get method(): "GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "CONNECT" | "OPTIONS" | "TRACE" | "PATCH";
    set method(val: "GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "CONNECT" | "OPTIONS" | "TRACE" | "PATCH");
    /**
     * Return request Content-Length as a number when preqent, or undefined.
     */
    get length(): number | undefined;
    get url(): string;
    set url(val: string);
    get originalUrl(): string | undefined;
    /**
     * Get origin of URL, include protocol and host.
     * @example http://example.com
     */
    get origin(): string | undefined;
    /**
     * Get full request URL, include protocol, host and url.
     * @example http://example.com/foo/bar?q=1
     */
    get href(): string;
    get path(): string;
    set path(val: string);
    get querystring(): string;
    set querystring(val: string);
    get search(): string;
    set search(val: string);
    /**
     * Get host (hostname:port) when present.
     * Supports X-Forwarded-Host when app.proxy is true,
     * otherwise Host is used.
     */
    get host(): string | undefined;
    /**
     * Get hostname when present.
     * Supports X-Forwarded-Host when app.proxy is true,
     * otherwise Host is used.
     *
     * //// ignore: If host is IPv6, delegates parsing to WHATWG URL API, Note This may impact performance.
     */
    get hostname(): string;
    /**
     * Get WHATWG parsed URL object.
     *
     * refers: https://nodebeginner.org/blog/post/nodejs-tutorial-whatwg-url-parser/
     *
     * url模块现在(node 8.0+)提供了一个额外的实现，它实现了标准化的WHATWG URL API，使得Node.js的url-parsing代码与Web浏览器解析URL的方式相同
     *
     * 原有的 'url.parse()' Nodejs 被认为
     */
    get URL(): URL_2;
    get type(): string;
    /**
     * Get request charset when present, or undefined:
     * @example "utf-8"
     */
    get charset(): string | undefined;
    get query(): URLSearchParams_2;
    set query(val: URLSearchParams_2);
    get protocol(): string | string[];
    get secure(): boolean;
    /**
     * Request remote address. Supports X-Forwarded-For when app.proxy is true.
     */
    get ip(): string | undefined;
    /**
     * When X-Forwarded-For is present and app.proxy is enabled an array of these ips is returned,
     * ordered from upstream to downstream.
     * When disabled an empty array is returned.
     *
     * refers: https://support.stackpath.com/hc/en-us/articles/360021658292-Getting-Real-Client-IPs-with-X-Forwarded-For
     */
    get ips(): (string | undefined)[];
    /**
     * Return subdomains as an array.
     *
     * Subdomains are the dot-separated parts of the host before the main domain of the app.
     * By default, the domain of the app is assumed to be the last two parts of the host.
     * This can be changed by setting app.subdomainOffset.
     *
     * For example, if the domain is "tobi.ferrets.example.com": If app.subdomainOffset is not set,
     * ctx.subdomains is ["ferrets", "tobi"].
     * If app.subdomainOffset is 3, ctx.subdomains is ["tobi"].
     */
    get subdomains(): string[];
    /**
     * Check if the incoming request contains the "Content-Type" header field,
     * and it contains any of the give mime types.
     * If there is no request body, null is returned.
     * If there is no content type, or the match fails false is returned.
     * Otherwise, it returns the matching content-type.
     */
    is(...types: string[]): string | false;
    /**
     * Check if the given type(s) is acceptable,
     * returning the best match when true, otherwise false.
     * The type value may be one or more mime type string such as "application/json",
     * the extension name such as "json",
     * or an array ["json", "html", "text/plain"].
     *
     * refers: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Accept
     */
    accepts(...types: string[]): string | false;
    /**
     * Check if encodings are acceptable,
     * returning the best match when true, otherwise false.
     * Note that you should include identity as one of the encodings!
     *
     * // Accept-Encoding: gzip
     * ctx.acceptsEncodings('gzip', 'deflate', 'identity');
     * // "gzip"
     *
     * ctx.acceptsEncodings(['gzip', 'deflate', 'identity']);
     * // "gzip"
     *
     * Note that the identity encoding (which means no encoding)
     * could be unacceptable if the client explicitly sends identity;q=0.
     * Although this is an edge case,
     * you should still handle the case where this method returns false.
     */
    acceptsEncodings(...encodings: Array<string | string[]>): string | boolean | string[];
    /**
     * Check if charsets are acceptable,
     * returning the best match when true, otherwise false.
     */
    acceptsCharsets(...charsets: string[]): string | boolean;
    /**
     * Check if langs are acceptable,
     * returning the best match when true,
     * otherwise false.
     *
     * When no arguments are given all accepted languages are returned as an array
     *
     * refers: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Accept-Language
     * @returns return wheather (given) languages acceptable, or all accepted languages (when no arguments are given)
     */
    acceptsLanguages(...langs: Array<string | string[]>): string | boolean | string[];
    /**
     * Check if the request is idempotent.
     *
     * refers: https://www.cnblogs.com/weidagang2046/archive/2011/06/04/idempotence.html
     */
    idempotent(): boolean;
    get socket(): Socket;
    /**
     * Return request header.
     *
     * field lower-kebad-case-field-name
     */
    get(field: string): string | string[] | undefined;
}

export declare interface TunRequestOptions {
    proxy?: boolean;
    subdomainOffset?: number;
}

export declare class TunResponse {
    #private;
    [RAW_REQUEST]: IncomingMessage;
    [RAW_RESPONSE]: ServerResponse;
    cookies: TunCookie[];
    constructor(req: IncomingMessage, res: ServerResponse);
    get header(): OutgoingHttpHeaders;
    get headers(): OutgoingHttpHeaders;
    get socket(): Socket | null;
    get status(): number;
    set status(val: number);
    /**
     * Get response status message.
     * By default, it is associated with response.status.
     */
    get message(): string;
    set message(val: string);
    set length(val: number);
    /**
     * Return response Content-Length as a number when present,
     * or deduce from ctx.body when possible, or undefined.
     */
    get length(): number;
    get body(): string | Buffer | Stream | Object | Array<any> | null;
    set body(val: string | Buffer | Stream | Object | Array<any> | null);
    /**
     * Get a response header field value with case-insensitive field.
     *
     * e.g. const etag = ctx.response.get('ETag');
     */
    get(field: string): string | number | string[] | undefined;
    /**
     * Set response header field to value:
     *
     * e.g. `ctx.request.set('Cache-Control', 'no-cache');`
     * ```
     * ctx.request.set({
     *   'Etag': '1234',
     *   'Last-Modified': date
     * })
     * ```
     */
    set(field: string, value: string | number | string[]): void;
    /**
     * Append additional header field with value val.
     */
    append(field: string, value: string | number | string[]): void;
    /**
     * Remove header field.
     */
    remove(field: string): void;
    /**
     * Get request Content-Type void of parameters such as "charset".
     */
    get type(): {
        mineType?: keyof typeof mimeExtMap;
        suffixType?: string;
        rawType?: string;
    };
    set type(val: {
        mineType?: keyof typeof mimeExtMap;
        suffixType?: string;
        rawType?: string;
    });
    /**
     * Very similar to ctx.request.is().
     * Check whether the response type is one of the supplied types.
     * This is particularly useful for creating middleware that manipulate responses.
     */
    is(...types: string[]): string | false;
    /**
     * Perform a [302] redirect to url.
     *
     * The string "back" is special-cased to provide Referrer support,
     * when Referrer is not present alt or "/" is used.
     */
    redirect(url: string, alt: string): void;
    /**
     * 设置 响应的 附件下载 Content-Disposition 头，并设置相应 Content-Type 头
     *
     * refers:
     * https://github.com/jshttp/content-disposition
     * https://github.com/jshttp/content-disposition/blob/master/index.js
     */
    attachment(filename: string): void;
    get headerSent(): boolean;
    /**
     * Return '' if header not exists
     *
     * Set the Last-Modified header as an appropriate UTC string.
     * You can either set it as a Date or date string.
     */
    get lastModified(): Date | string;
    set lastModified(date: Date | string);
    /**
     * Set the ETag of a response including the wrapped "s.
     * Note that there is no corresponding response.etag getter.
     *
     * e.g. `crypto.createHash('md5').update(ctx.body).digest('hex')`
     */
    get etag(): string;
    set etag(val: string);
    /**
     * 对向CDN缓存服务等 响应 说明区分某个字段的值做两份缓存(gzip|identity)？
     *
     * refers: https://imququ.com/post/vary-header-in-http.html
     */
    vary(field: string): void;
    /**
     * Flush any set headers, and begin the body.
     */
    flushHeaders(): void;
}

export { }
