import * as http from 'http';
import * as net from 'net';
import * as oas3 from 'openapi3-ts';
import { Context as KoaContext } from 'koa';
import { Callback, ParametersByLocation, ParametersMap, HttpIncomingMessage } from './basicTypes';
import { Readable } from 'stream';
import { ExegesisOptions, ParameterLocation, ParameterLocations } from '.';
import { BodyParser } from './bodyParser';
import { IValidationError, ResponseValidationResult, ValidatorFunction } from './validation';
export interface HttpHeaders {
    [header: string]: number | string | string[];
}
export interface ExegesisRoute {
    path: string;
}
export interface ExegesisResponse {
    statusCode: number;
    statusMessage: string | undefined;
    headers: HttpHeaders;
    body: Buffer | string | Readable | any;
    connection: net.Socket;
    socket: net.Socket;
    ended: boolean;
    setStatus(status: number): this;
    status(status: number): this;
    setBody(body: any): this;
    /**
     * Set the value of a header.
     * @param header - the header to set.
     * @param value - the value to set the header to.
     */
    header(header: string, value: number | string | string[] | undefined): this;
    set(header: string, value: number | string | string[] | undefined): this;
    /**
     * Set the JSON content of the response.  Note that this will call `JSON.stringify()`
     * immediately if response validation is enabled, because there may be `toJSON()`
     * functions on the object or any nested values (e.g. if some values are Mongoose objects).
     * This means we'll need to parse that string to do validation though.  If you
     * know your object is a pure POJO, call `res.pureJson()` instead.
     */
    json(json: any): this;
    /**
     * Sets the JSON content of the response to the object provided.  Note that
     * while `toJSON()` on the object or any child objects will be
     * respsected when the object is serialized, it will be ignored for purposes
     * of response validation.
     */
    pureJson(json: any): this;
    end(): void;
    redirect(status: number, url: string): this;
    redirect(url: string): this;
    setHeader(name: string, value: number | string | string[] | undefined): void;
    getHeader(name: string): number | string | string[] | undefined;
    getHeaderNames(): string[];
    getHeaders(): HttpHeaders;
    hasHeader(name: string): boolean;
    removeHeader(name: string): void;
    writeHead(statusCode: number, headers?: HttpHeaders): void;
    writeHead(statusCode: number, statusMessage?: string, headers?: HttpHeaders): void;
}
export interface ExegesisContextBase {
    readonly req: HttpIncomingMessage;
    readonly origRes: http.ServerResponse;
    readonly res: ExegesisResponse;
    api: any;
    security?: {
        [scheme: string]: AuthenticationSuccess;
    };
    user?: any;
    parameterLocations?: ParameterLocations;
    makeError(statusCode: number, message: string): Error;
    makeValidationError(message: string, parameterLocation: ParameterLocation): Error;
    /**
     * Returns true if the response has already been sent.
     */
    isResponseFinished(): boolean;
}
export interface ExegesisContext extends ExegesisContextBase {
    parameterLocations: ParameterLocations;
    params: ParametersByLocation<ParametersMap<any>>;
    requestBody: any;
    options: ExegesisOptions;
    route: ExegesisRoute;
    baseUrl: string;
}
export interface ExegesisPluginContext extends ExegesisContextBase {
    getParams(): Promise<ParametersByLocation<ParametersMap<any>>>;
    getParams(done: Callback<ParametersByLocation<ParametersMap<any>>>): void;
    getRequestBody(): Promise<any>;
    getRequestBody(done: Callback<any>): void;
}
export interface OAS3ApiInfo {
    openApiDoc: oas3.OpenAPIObject;
    serverPtr: string | undefined;
    serverObject: oas3.ServerObject | undefined;
    pathItemPtr: string;
    pathItemObject: oas3.PathItemObject;
    operationPtr: string | undefined;
    operationObject: oas3.OperationObject | undefined;
    requestBodyMediaTypePtr: string | undefined;
    requestBodyMediaTypeObject: oas3.MediaTypeObject | undefined;
}
export type PromiseController = (context: ExegesisContext) => any;
export type CallbackController = (context: ExegesisContext, done: Callback<any>) => void;
export type Controller = PromiseController | CallbackController;
export interface ControllerModule {
    [operationId: string]: Controller;
}
export interface Controllers {
    [controllerName: string]: ControllerModule;
}
export interface AuthenticationFailure {
    type: 'invalid' | 'missing';
    status?: number;
    message?: string;
    challenge?: string;
}
export interface AuthenticationSuccess {
    type: 'success';
    user?: any;
    roles?: string[] | undefined;
    scopes?: string[] | undefined;
    [name: string]: any;
}
export type AuthenticationResult = AuthenticationSuccess | AuthenticationFailure;
export interface AuthenticatorInfo {
    in?: 'query' | 'header' | 'cookie';
    name?: string;
    scheme?: string;
}
export type PromiseAuthenticator = (context: ExegesisPluginContext, info: AuthenticatorInfo) => AuthenticationResult | undefined | Promise<AuthenticationResult | undefined>;
export type CallbackAuthenticator = (context: ExegesisPluginContext, info: AuthenticatorInfo, done: Callback<AuthenticationResult | undefined>) => void;
export type Authenticator = PromiseAuthenticator | CallbackAuthenticator;
export interface Authenticators {
    [scheme: string]: Authenticator;
}
/**
 * Result returned by the exegesisRunner.
 */
export interface HttpResult {
    headers: HttpHeaders;
    status: number;
    body: NodeJS.ReadableStream | undefined;
}
/**
 * A function which takes in a request and response, and returns an HttpResult.
 *
 * @throws {ValidationError} - If a validation error occurs in the parameters or the body.
 * @throws {HttpError} - If a non-validation error occurs, and an HTTP error code is suggested.
 * @throws {Error} - If any other error occurs.
 */
export type ExegesisRunner = (req: http.IncomingMessage, res: http.ServerResponse, ctx: KoaContext) => Promise<HttpResult | undefined>;
export type ParsedParameterValidator = (parameterValues: ParametersByLocation<ParametersMap<any>>) => IValidationError[] | null;
export interface ResolvedOperation {
    parseParameters: () => ParametersByLocation<ParametersMap<any>>;
    validateParameters: ParsedParameterValidator;
    parameterLocations: ParameterLocations;
    bodyParser: BodyParser | undefined;
    validateBody: ValidatorFunction | undefined;
    exegesisControllerName: string | undefined;
    operationId: string | undefined;
    controllerModule: ControllerModule | undefined;
    controller: Controller | undefined;
    validateResponse(response: ExegesisResponse, validateDefaultResponses: boolean): ResponseValidationResult;
    authenticate(context: ExegesisContext): Promise<{
        [scheme: string]: AuthenticationSuccess;
    } | undefined>;
}
export interface ResolvedPath<T> {
    operation: ResolvedOperation | undefined;
    api: T;
    /** List of methods the client is allowed to send to this path.  e.g. `['get', 'post']`. */
    allowedMethods: string[];
    /** The path of the operation being accessed.  e.g. "/users/1234". */
    path: string;
    /**
     * The "base" of the `path`.  `${baseUrl}${path}` represents the full
     * URL being accessed.  For OAS3, for example you can set a URL like
     * `https://myserver.com/v1` in the `Server` object, which would be reflected
     * here.
     */
    baseUrl: string;
}
export interface ApiInterface<T> {
    /**
     * Resolve an incoming request.
     *
     * @param method - The HTTP method used (e.g. 'GET').
     * @param url - The URL used to retrieve this request.
     * @param headers - Any headers sent along with the request.
     * @throws {ValidationError} if some parameters cannot be parsed.
     */
    resolve(method: string, url: string, headers: http.IncomingHttpHeaders): ResolvedPath<T> | undefined;
}
export interface ExegesisPluginInstance {
    /**
     * Called exactly once, before Exegesis "compiles" the API document.
     * Plugins must not modify apiDoc here.
     *
     * @param data.apiDoc - the API document.
     */
    preCompile?: ((data: {
        apiDoc: any;
    }) => void | Promise<void>) | ((data: {
        apiDoc: any;
    }, done: Callback<void>) => void);
    /**
     * Called before routing.  Note that the context hasn't been created yet,
     * so you just get a raw `req` and `res` object here.
     */
    preRouting?: ((data: {
        req: http.IncomingMessage;
        res: http.ServerResponse;
    }) => void | Promise<void>) | ((data: {
        req: http.IncomingMessage;
        res: http.ServerResponse;
    }, done: Callback<void>) => void);
    /**
     * Called immediately after the routing phase.  Note that this is
     * called before Exegesis verifies routing was valid - the
     * `pluginContext.api` object will have information about the
     * matched route, but will this information may be incomplete.
     * For example, for OAS3 we may have matched a route, but not
     * matched an operation within the route. Or we may have matched
     * an operation but that operation may have no controller defined.
     * (If we failed to match a route at all, this will not be called.)
     *
     * If your API added a route to the API document, this function is a
     * good place to write a reply.
     *
     * @param pluginContext - the plugin context.
     */
    postRouting?: ((pluginContext: ExegesisPluginContext) => void | Promise<void>) | ((pluginContext: ExegesisPluginContext, done: Callback<void>) => void);
    /**
     * Called for each request, after security phase and before input
     * is parsed and the controller is run.  This is a good place to
     * do extra security checks.  The `exegesis-plugin-roles` plugin,
     * for example, generates a 403 response here if the authenticated
     * user has insufficient privliedges to access this path.
     *
     * Note that this function will not be called if a previous pluing
     * has already written a response.
     *
     * @param pluginContext - the plugin context.
     */
    postSecurity?: ((pluginContext: ExegesisPluginContext) => void | Promise<void>) | ((pluginContext: ExegesisPluginContext, done: Callback<void>) => void);
    /**
     * Called immediately after the controller has been run, but before
     * any response validation.  This is a good place to do custom
     * response validation.  If you have to deal with something weird
     * like XML, this is where you'd handle it.
     *
     * This function can modify the contents of the response.
     *
     * @param context - The exegesis plugin context.
     */
    postController?: ((pluginContext: ExegesisContext) => void | Promise<void>) | ((pluginContext: ExegesisContext, done: Callback<void>) => void);
    /**
     * Called after the response validation step.  This is the last step before
     * the response is converted to JSON and written to the output.
     */
    postResponseValidation?: ((pluginContext: ExegesisContext) => void | Promise<void>) | ((pluginContext: ExegesisContext, done: Callback<void>) => void);
}
export interface ExegesisPlugin {
    info: {
        name: string;
    };
    makeExegesisPlugin(data: {
        apiDoc: any;
    }): ExegesisPluginInstance;
}
