import * as request from 'request';

type LogLevel
    = "trace"
    | "debug"
    | "info"
    | "warn"
    | "error"
    | "fatal"
    | "report";

interface LogInfo {
    time: Date;
    level: LogLevel;
    namespace?: string;
    file?: string;
    line?: number;
    stacktrace?: Error
    message: string
    output: string;
}

export function configure_logging(config: { level: LogLevel, appender: (logInfo: LogInfo) => void, disabled_action_groups?: string[] }): void;

type AuthenticatorConfig = {
    timeout?: number // defaults to 5000
    "max-pending-requests"?: number // defaults to 20000
}
type AuthenticationRequest
    = {method: 'secret', login: string, secret: string}
    | {method: 'access-token', token: string};
type AuthenticationResponse
    = {type: 'success', user: string, login?: string}
    | {type: 'continue', authentication: {token?: string}}
    | { type: 'failure', message?: string };
type AuthenticatorImpl = (request: AuthenticationRequest) => Promise<AuthenticationResponse>

type RequestFn = (options: request.OptionsWithUrl, callback: request.RequestCallback) => request.Request

type MetricsPublisherConfig = {
    filters?: {
        "non-matched": 'whitelist' | 'blacklist'
        publishers: [{
            publisher: Record<string, string | RegExp>,
            metrics: {
                blacklist?: (string | RegExp)[],
                whitelist?: (string | RegExp)[]
            }
        }]
    }
    heartbeats?: number // defaults to 1000
    buffer_size?: number; // defaults to 10000
    conflation?: {
        max_size?: number // defaults to 0
        interval?: number // defaults to 1000
    }
}

/**
 * Gateway Config
 */
type GatewayConfig = {
    ip?: string; // defaults to '0.0.0.0'
    port?: number | string; // defaults to 3434
    route?: string;
    authentication?: {
        token_ttl?: number
        default?: string // defaults to 'basic'
        available?: string[] // defaults to ['basic']
        win?: AuthenticatorConfig
        basic?: AuthenticatorConfig & {
            ttl?: number
        }
    } | (Partial<Record<string, (AuthenticatorConfig & {authenticator: AuthenticatorImpl})>>);
    metrics?: {
        publishers?: ('file' | 'raw' | 'rest' | 'kafka' | {
            startup?: () => Promise<unknown>,
            function: (value: unknown) => Promise<void> | void,
            cleanup?: (arg?: unknown) => Promise<void> | void,
            context?: unknown,
            configuration?: MetricsPublisherConfig & {
                'split-size'?: number // defaults to 1
            }
        })[];
        file?: MetricsPublisherConfig & {
            location: string;
            append?: boolean; // defaults to false
            'skip-status'?: boolean // defaults to false
            conflation: MetricsPublisherConfig['conflation'] & {
                'max-datapoints-repo': number // defaults to 50
            }
        };
        raw?: {
            location: string
        }
        rest?: MetricsPublisherConfig & {
            endpoint: string;
            authentication?: false | {
                user?: string
                password?: string,
                path?: string
            };
            "user-agent"?: string // defaults to "metrics-rest/0.1.x"
            headers?: { [key:string]: string };
            timeout?: number; //defaults to 1000
            conflation?: MetricsPublisherConfig['conflation'] & {
                "max-datapoints-repo"?: number // defaults 50
            };
            client?: {
                "request-fn"?: RequestFn
                sspi?: boolean //
                "max-in-flight"?: number //
                "log-level"?: LogLevel // defaults to warn
            }
        };
        kafka?: {
            topic: string;
            "publisher-config": object;
            conflation?: MetricsPublisherConfig['conflation'] & {
                "max-datapoints-repo"?: number // defaults 50
            };
        }
        filters?: MetricsPublisherConfig['filters']
        identity?: {
            system?: string // defaults to Glue
            service?: string // defaults to Gateway
        };
        interop?: {
            enabled?: boolean;
            invoke: {
                filters: {
                    methods: [{
                        name: (string | RegExp)
                        arguments: {
                            whitelist: (string | RegExp)[]
                            blacklist: (string | RegExp)[]
                        }
                    }]
                    "non-matched": 'whitelist' | 'blacklist'
                }
            }
        }
    }
    cluster?: {
        enabled?: boolean
        configuration?: {
            node_id?: string,
            [key: string]: unknown
        },
        type: 'p2p' | 'broker',
        p2p?: {
            directory: {
                type: 'static' | 'rest',
                members?: {node: string, endpoint: string}[],
                config?: {
                    directory_uri: string,
                    announce_interval?: number, // defaults to 10000
                    request?: RequestFn,
                    [key: string]: unknown
                }
            }
            binding?: {
                port?: number //defaults to 0
            }
        },
        broker?: {
            endpoint?: string
        },
        embedded_broker?: {
            enabled?: boolean,
            route?: string // defaults to /mesh-broker
        }
    }
    limits?: {
        max_connections?: number;
        large_msg_threshold?: number; // 20000
        node_buffer_size?: number; // 20000
    }
    security?: {
        origin_filters?: {
            non_matched: string
            missing: string
            blacklist?: []
            whitelist?: []
        }
    }
    memory?: {
        memory_limit?: number, // defaults to 1073741824 bytes (1GB)
        dump_location?: string, // defaults to current directory
        dump_prefix?: string, // defaults to 'Heap'
        report_interval?: number, // report schedule interval in ms. defaults to 600000 ms (10 min)
        max_backups?: number, // defaults to 10
    },
    request?: RequestFn // request function to use
    [key: string]: unknown //allow additional properties
}

interface Gateway {
    start(): Promise<Gateway>;

    stop(): Promise<Gateway>;

    info(): { endpoint: string };

    connect(cb: (client: GatewayClient, msg: GatewayMessage) => void): Promise<GatewayClient>;
}

interface GatewayClient {
    send(message: GatewayMessage): void;

    disconnect(): Promise<boolean>;
}

interface GatewayMessage {
    type: string
    domain: string
    destination: string
    peer_id: string
}

export function create(config: GatewayConfig): Gateway;
