// Type definitions for pino 6.3
// Project: https://github.com/pinojs/pino.git, http://getpino.io
// Definitions by: Peter Snider <https://github.com/psnider>
//                 BendingBender <https://github.com/BendingBender>
//                 Christian Rackerseder <https://github.com/screendriver>
//                 GP <https://github.com/paambaati>
//                 Alex Ferrando <https://github.com/alferpal>
//                 Oleksandr Sidko <https://github.com/mortiy>
//                 Harris Lummis <https://github.com/lummish>
//                 Raoul Jaeckel <https://github.com/raoulus>
//                 Cory Donkin <https://github.com/Cooryd>
//                 Adam Vigneaux <https://github.com/AdamVig>
//                 Austin Beer <https://github.com/austin-beer>
//                 Michel Nemnom <https://github.com/Pegase745>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 3.0

/// <reference types="node"/>

import stream = require('stream');
import http = require('http');
import { EventEmitter } from 'events';
import SonicBoom from 'sonic-boom';
import * as pinoStdSerializers from 'pino-std-serializers';
import * as PinoPretty from 'pino-pretty';

export = P;

/**
 * @param [optionsOrStream]: an options object or a writable stream where the logs will be written. It can also receive some log-line metadata, if the
 * relative protocol is enabled. Default: process.stdout
 * @returns a new logger instance.
 */
declare function P(optionsOrStream?: P.LoggerOptions | P.DestinationStream): P.Logger;

/**
 * @param [options]: an options object
 * @param [stream]: a writable stream where the logs will be written. It can also receive some log-line metadata, if the
 * relative protocol is enabled. Default: process.stdout
 * @returns a new logger instance.
 */
declare function P(options: P.LoggerOptions, stream: P.DestinationStream): P.Logger;

declare namespace P {
    /**
     * Holds the current log format version (as output in the v property of each log record).
     */
    const LOG_VERSION: number;
    const levels: LevelMapping;
    const symbols: {
        readonly setLevelSym: unique symbol;
        readonly getLevelSym: unique symbol;
        readonly levelValSym: unique symbol;
        readonly useLevelLabelsSym: unique symbol;
        readonly mixinSym: unique symbol;
        readonly lsCacheSym: unique symbol;
        readonly chindingsSym: unique symbol;
        readonly parsedChindingsSym: unique symbol;
        readonly asJsonSym: unique symbol;
        readonly writeSym: unique symbol;
        readonly serializersSym: unique symbol;
        readonly redactFmtSym: unique symbol;
        readonly timeSym: unique symbol;
        readonly timeSliceIndexSym: unique symbol;
        readonly streamSym: unique symbol;
        readonly stringifySym: unique symbol;
        readonly stringifiersSym: unique symbol;
        readonly endSym: unique symbol;
        readonly formatOptsSym: unique symbol;
        readonly messageKeySym: unique symbol;
        readonly nestedKeySym: unique symbol;
        readonly wildcardFirstSym: unique symbol;
        readonly needsMetadataGsym: unique symbol;
        readonly useOnlyCustomLevelsSym: unique symbol;
        readonly formattersSym: unique symbol;
        readonly hooksSym: unique symbol;
    };
    /**
     * Exposes the Pino package version. Also available on the logger instance.
     */
    const version: string;

    type SerializedError = pinoStdSerializers.SerializedError;
    type SerializedResponse = pinoStdSerializers.SerializedResponse;
    type SerializedRequest = pinoStdSerializers.SerializedRequest;

    /**
     * Provides functions for serializing objects common to many projects.
     */
    const stdSerializers: {
        /**
         * Generates a JSONifiable object from the HTTP `request` object passed to the `createServer` callback of Node's HTTP server.
         */
        req: typeof pinoStdSerializers.req;
        /**
         * Generates a JSONifiable object from the HTTP `response` object passed to the `createServer` callback of Node's HTTP server.
         */
        res: typeof pinoStdSerializers.res;
        /**
         * Serializes an Error object.
         */
        err: typeof pinoStdSerializers.err;
        /**
         * Returns an object:
         * ```
         * {
         *   req: {}
         * }
         * ```
         * where req is the request as serialized by the standard request serializer.
         * @param req The request to serialize
         * @return An object
         */
        mapHttpRequest: typeof pinoStdSerializers.mapHttpRequest;
        /**
         * Returns an object:
         * ```
         * {
         *   res: {}
         * }
         * ```
         * where res is the response as serialized by the standard response serializer.
         * @param res The response to serialize.
         * @return An object.
         */
        mapHttpResponse: typeof pinoStdSerializers.mapHttpResponse;
        /**
         * A utility method for wrapping the default error serializer. Allows custom serializers to work with the
         * already serialized object.
         * @param customSerializer The custom error serializer. Accepts a single parameter: the newly serialized
         * error object. Returns the new (or updated) error object.
         * @return A new error serializer.
         */
        wrapErrorSerializer: typeof pinoStdSerializers.wrapErrorSerializer;
        /**
         * A utility method for wrapping the default request serializer. Allows custom serializers to work with the
         * already serialized object.
         * @param customSerializer The custom request serializer. Accepts a single parameter: the newly serialized
         * request object. Returns the new (or updated) request object.
         * @return A new error serializer.
         */
        wrapRequestSerializer: typeof pinoStdSerializers.wrapRequestSerializer;
        /**
         * A utility method for wrapping the default response serializer. Allows custom serializers to work with the
         * already serialized object.
         * @param customSerializer The custom response serializer. Accepts a single parameter: the newly serialized
         * response object. Returns the new (or updated) response object.
         * @return A new error serializer.
         */
        wrapResponseSerializer: typeof pinoStdSerializers.wrapResponseSerializer;
    };
    /**
     * Provides functions for generating the timestamp property in the log output. You can set the `timestamp` option during
     * initialization to one of these functions to adjust the output format. Alternatively, you can specify your own time function.
     * A time function must synchronously return a string that would be a valid component of a JSON string. For example,
     * the default function returns a string like `,"time":1493426328206`.
     */
    const stdTimeFunctions: {
        /**
         * The default time function for Pino. Returns a string like `,"time":1493426328206`.
         */
        epochTime: TimeFn;
        /*
         * Returns the seconds since Unix epoch
         */
        unixTime: TimeFn;
        /**
         * Returns an empty string. This function is used when the `timestamp` option is set to `false`.
         */
        nullTime: TimeFn;
        /*
         * Returns ISO 8601-formatted time in UTC
         */
        isoTime: TimeFn;
    };

    /**
     * Equivalent of SonicBoom constructor options object
     */
    // TODO: use SonicBoom constructor options interface when available
    interface DestinationObjectOptions {
        fd?: string | number | undefined;
        dest?: string | undefined;
        minLength?: number | undefined;
        sync?: boolean | undefined;
    }

    /**
     * Create a Pino Destination instance: a stream-like object with significantly more throughput (over 30%) than a standard Node.js stream.
     * @param [dest]: The `destination` parameter, at a minimum must be an object with a `write` method. An ordinary Node.js
     *                `stream` can be passed as the destination (such as the result of `fs.createWriteStream`) but for peak log
     *                writing performance it is strongly recommended to use `pino.destination` to create the destination stream.
     * @returns A Sonic-Boom  stream to be used as destination for the pino function
     */
    function destination(
        dest?: string | number | DestinationObjectOptions | DestinationStream | NodeJS.WritableStream,
    ): SonicBoom;

    /**
     * Create an extreme mode destination. This yields an additional 60% performance boost.
     * There are trade-offs that should be understood before usage.
     * @param [fileDescriptor]: File path or numerical file descriptor, by default 1
     * @returns A Sonic-Boom  stream to be used as destination for the pino function
     */
    function extreme(fileDescriptor?: string | number): SonicBoom;

    /**
     * The pino.final method can be used to create an exit listener function.
     * This listener function can be supplied to process exit events.
     * The exit listener function will call the handler with
     * @param [logger]: pino logger that serves as reference for the final logger
     * @param [handler]: Function that will be called by the handler returned from this function
     * @returns Exit listener function that can be supplied to process exit events and will call the supplied handler function
     */
    function final(
        logger: Logger,
        handler: (error: Error, finalLogger: Logger, ...args: any[]) => void,
    ): (error: Error | null, ...args: any[]) => void;

    /**
     * The pino.final method can be used to acquire a final logger instance that synchronously flushes on every write.
     * @param [logger]: pino logger that serves as reference for the final logger
     * @returns Final, synchronous logger
     */
    function final(logger: Logger): Logger;

    interface LevelMapping {
        /**
         * Returns the mappings of level names to their respective internal number representation.
         */
        values: { [level: string]: number };
        /**
         * Returns the mappings of level internal level numbers to their string representations.
         */
        labels: { [level: number]: string };
    }
    type TimeFn = () => string;
    type MixinFn = () => object;

    interface DestinationStream {
        write(msg: string): void;
    }

    interface LoggerOptions {
        /**
         * Avoid error causes by circular references in the object tree. Default: `true`.
         */
        safe?: boolean | undefined;
        /**
         * The name of the logger. Default: `undefined`.
         */
        name?: string | undefined;
        /**
         * an object containing functions for custom serialization of objects.
         * These functions should return an JSONifiable object and they should never throw. When logging an object,
         * each top-level property matching the exact key of a serializer will be serialized using the defined serializer.
         */
        serializers?: { [key: string]: SerializerFn } | undefined;
        /**
         * Enables or disables the inclusion of a timestamp in the log message. If a function is supplied, it must
         * synchronously return a JSON string representation of the time. If set to `false`, no timestamp will be included in the output.
         * See stdTimeFunctions for a set of available functions for passing in as a value for this option.
         * Caution: any sort of formatted time will significantly slow down Pino's performance.
         */
        timestamp?: TimeFn | boolea