import winston, { Logger } from 'winston';
import express, { Request as Request$1, Application, Response, NextFunction, Router } from 'express';
import mongoose, { Document, SaveOptions, FilterQuery, Mongoose, Model, ClientSession } from 'mongoose';
import _ from 'lodash';
import Agenda, { DefineOptions, Job } from 'agenda';
import { AxiosError } from 'axios';
import { Kafka, Producer, logLevel, Consumer } from 'kafkajs';
import moment from 'moment-timezone';
import { S3Client } from '@aws-sdk/client-s3';

type LogLevel = 'info' | 'warn' | 'error' | 'debug';

declare const createLogger: (opts: {
    dir?: string | undefined;
    filePrefix?: string | undefined;
    enableConsoleLog: boolean;
    enableFileLog: boolean;
    logLevel: LogLevel;
    timestampFormat?: string;
}) => winston.Logger;

interface MongooseOptionInterface {
    returnObject: boolean;
    new: boolean;
    many?: boolean;
}

/**
 * Default ABSTRACT class: CrudService
 * this class will be used as base class
 * to create all the crud service around the project
 *
 * Note: We're using keyword object in this file as this required as abstract class
 */
declare abstract class CrudService<T, U> {
    abstract groupDBFields: unknown;
    /**
     * Projections Mapping Only
     * This will be used in MongoDB Query
     */
    abstract projections: string[];
    protected idFieldPrefix: string;
    protected primaryKey: string;
    protected primaryValueLength: number;
    protected primaryValueSeparator: string;
    /**
     * This is Actually Array of strings
     * This will be just a reference to mapping
     *
     * This will be used in filtering the projection fields/columns
     */
    protected defaultProjections: never[];
    protected _count: number;
    private _itemsPerPage;
    get itemsPerPage(): number;
    /**
     * This function must be static
     * neither it won't work
     *
     * @param doc
     * @param ret
     * @param options
     */
    static modifyMongooseDoc: (doc: Document, ret: Record<string, unknown>, options: Record<string, unknown>) => Record<string, unknown>;
    getProjections: () => string[];
    prepareCrudListAsObject<T>(list: Document[]): T[];
    /**
     * Well, usually mongoose handles this conversion
     * i.e. this string to ObjectId conversion
     * but in case of pipelines in aggregation it doesn't
     * in that case, we have to convert it manually
     *
     * so, this function exists
     * please use this to convert the string id to ObjectId
     *
     * @param id
     */
    createMongooseObjectId(id: string): mongoose.Types.ObjectId;
    /**
     * Prepare projection
     * for Dates
     *
     * @param dateField
     * @param returnTime
     */
    dateStringProject(dateField: string, returnTime?: boolean): Record<string, unknown>;
    findRandomEntry<T>(filters?: Record<string, unknown>): Promise<T>;
    initiateIndex(): Promise<void>;
    /**
     * Use the following functions as information merger in a object
     * in a way that can be used in MongoDB to search the query
     *
     * @param filters
     * @param wildCardSearch
     * @param dateFrom
     * @param dateTo
     */
    abstract prepareFilters(filters: Record<string, unknown>, wildCardSearch: string, dateFrom: Date | string, //moment.Moment,
    dateTo: Date | string): void;
    abstract prepareAggregation<T = unknown>(req: Request$1, filters: Record<string, unknown>, wildCardSearch: string, dateFrom: Date, dateTo: Date, projections?: Record<string, unknown>, virtualConditional?: Partial<U>): void;
    abstract count(req: Request$1, filters: Record<string, unknown>, wildCardSearch: string, dateFrom: Date, //moment.Moment,
    dateTo: Date, //moment.Moment
    virtualConditional?: Partial<U>): any;
    abstract sum(req: Request$1, filters: Record<string, unknown>, wildCardSearch: string, dateFrom: Date, dateTo: Date, baseKey: string, sumKey: string): any;
    abstract find(req: Request$1, filters: Record<string, unknown>, wildCardSearch: string, dateFrom: Date | string, //moment.Moment,
    dateTo: Date | string, //moment.Moment,
    page: number, itemsPerPage: number, project: string[], saveOptions: SaveOptions, virtualConditional: Partial<U>, isStreaming: boolean): any;
    abstract findAndCount(req: Request$1, filters: FilterQuery<T>, wildCardSearch: string, dateFrom: Date, dateTo: Date, page: number, itemsPerPage: number, projections: string[], virtualConditional?: Partial<U>): Promise<{
        totalData: T[];
        totalCount: number;
    }>;
    abstract getRandomEntry(): any;
    abstract findOne(req: Request$1, filters: Record<string, unknown>, project: string[], saveOptions: SaveOptions): any;
    abstract findOneAndUpdate(req: Request$1, filters: Record<string, unknown>, inputs: Record<string, unknown>, saveOptions: SaveOptions): any;
    abstract update(req: Request$1, filters: Record<string, unknown>, inputs: Record<string, unknown>, mongooseOption: MongooseOptionInterface, ...args: Record<string, unknown>[]): any;
    abstract create(req: Request$1, inputs: Record<string, unknown>, saveOptions: SaveOptions, ...args: Record<string, unknown>[]): any;
    abstract createMultiple(req: Request$1, inputs: Record<string, unknown>[], saveOptions: SaveOptions, ...args: Record<string, unknown>[]): any;
    abstract delete(req: Request$1, filters: Record<string, unknown>, saveOptions: SaveOptions): any;
    protected abstract getMongoose(): Mongoose;
    protected abstract getDefaultProjections(): string[];
    protected abstract getLogger(): Logger;
    protected abstract getModel(): Model<Document>;
    /**
     * Prepare projection
     * @param projections
     */
    protected prepareProjections: (projections?: string[]) => _.Dictionary<unknown>;
    protected prepareFilterIds(filter: Record<string, unknown>, keys: string[]): void;
    protected filterObjectKeys: <T_1>(inputObj: Record<string, unknown>, keys?: string[], keepId?: boolean) => T_1;
    protected prepareDateFilterAggregation(dateFrom: Date, dateTo: Date): Record<string, unknown>;
    /**
     * Initiates a mongoose session with mongoose library
     * and start the transaction we well
     * Here, we don't need to call startTransaction method,
     * but only we have to call commit or abort transaction
     *
     * @param saveOptions
     * @protected
     */
    protected startSessionTransaction(saveOptions?: SaveOptions): Promise<ClientSession>;
    /**
     * End the transaction conditionally
     * And the condition is that saveOption or saveOption's session
     * must be empty in order to mark the session ended
     * it is because we have to mark only those sessions which has been initiated by function itself
     *
     * @param session
     * @param saveOptions
     * @protected
     */
    protected endSessionTransaction(session: ClientSession, saveOptions?: SaveOptions): Promise<void>;
    protected commitSessionTransaction(session: ClientSession, saveOptions?: SaveOptions): Promise<void>;
    protected abortSessionTransaction(session: ClientSession, saveOptions: mongoose.SaveOptions | undefined, error: Error): Promise<void>;
    /**
     * Please use this method after passing the session object
     * in other functions
     *
     * @param session
     * @protected
     */
    protected assertSessionInTransaction(session: ClientSession): void;
    protected abstract primaryKeyPrefix(): string;
    protected addPagination(aggregations: Record<string, unknown>[], itemsPerPage: number, page?: number, sort?: boolean): Record<string, unknown>[];
}

declare class LogBuilder {
    private _log;
    constructor(title: string, titleValue?: string | undefined);
    static create(title: string): LogBuilder;
    addNewLine(prefix?: string | undefined): LogBuilder;
    addSection(key: string | undefined, value?: string | undefined): LogBuilder;
    addErrorSection(message: string | undefined): LogBuilder;
    addWarningSection(message: string | undefined): LogBuilder;
    build(skipNewLine?: boolean): string;
}

interface JobOptionInterface {
    cron?: string;
    timeStartAt?: string;
    timeRepeatAt?: string;
    timeRepeatEvery?: string;
    startDate?: Date;
    endDate?: Date;
    data?: unknown;
    deleteIfExists?: boolean;
    logBuilder: LogBuilder;
}

declare abstract class JobService<T> {
    protected _title: string | undefined;
    get title(): string;
    register: ({ agenda, options, }: {
        agenda: Agenda;
        options?: DefineOptions | undefined;
    }) => void;
    onHandle: (job: Job) => Promise<void>;
    remove(): Promise<void>;
    scheduleIfNot(inputs: T, opts?: JobOptionInterface): Promise<void>;
    schedule(inputs: T, opts: JobOptionInterface): Promise<void>;
    protected abstract getLogger(): Logger;
    protected abstract getMongoose(): Mongoose;
    protected abstract getAgenda(): Agenda;
    protected abstract onRun(job: Job, data: T, opts: JobOptionInterface): Promise<void>;
}

declare class ExceptionMonitorService {
    private readonly _id;
    private _logger;
    private queueService;
    constructor({ kafkaConnection, kafkaTopic, kafkaGroupId, kafkaClientId, id, logger, }: {
        kafkaConnection: string;
        kafkaTopic: string;
        kafkaGroupId: string;
        kafkaClientId: string;
        id: string;
        logger?: winston.Logger;
    });
    send(error: Error): Promise<void> | undefined;
    listen(): void;
    private handleListenCallback;
}

interface opts {
    port: number;
    logger: winston.Logger;
    logRoutes: boolean;
    enableCluster: boolean;
    exceptionMonitor?: ExceptionMonitorService;
    before: (server: Application) => Promise<void>;
    after?: (server: Application) => Promise<void>;
}
declare function startApplication(opts: opts): Promise<void>;

declare class ApiResponse<T> {
    private static ERR_KEY_DEFAULT;
    private result;
    private success;
    private code;
    private httpCode;
    private message;
    private errors;
    private page;
    private itemsCount;
    private allItemsCount;
    constructor();
    addError(err: string | string[] | Error | AxiosError<ApiResponse<T>>): this;
    setErrors(errors: string[] | Error[] | AxiosError<ApiResponse<T>>[]): this;
    getErrors(): {
        [key: string]: string[];
    };
    setAppCode(code: number): this;
    getAppCode(): number;
    setHttpCode(code: number): this;
    getHttpCode(): number;
    setMessage(message: string): this;
    getMessage(): string;
    setResult(result: T): this;
    getResult(): T;
    setItemsCount(count: number): this;
    getItemsCount(): number;
    setPage(page: number): this;
    getPage(): number;
    setSuccess(success?: boolean): this;
    getSuccess(): boolean;
    setAllItemsCount(totalItems: number): this;
    getAllItemsCount(): number;
    markSuccess(): this;
    createResponse(): ApiResponse<T>;
}

type Callback1Void<Input> = (input: Input) => void;

declare abstract class BaseKafkaService<Child> {
    protected _logger: Logger | undefined;
    protected _host: string | undefined;
    protected _pollTimeInterval: number | undefined;
    protected _pollTimeout: number | undefined;
    protected _numConsumers: number | undefined;
    protected _topic: string | undefined;
    protected _group: string | undefined;
    protected _clientId: string | undefined;
    protected kafka: Kafka;
    protected producer: Producer | undefined;
    protected onNewMessageReceived: ((message: Record<string, string[]>) => void) | undefined;
    private consumers;
    protected constructor(host: string, clientId: string);
    setLogger(value: Logger): Child;
    setPollTimeInterval(value: number | undefined): Child;
    clientId(value: string | undefined): Child;
    setPollTimeout(value: number | undefined): Child;
    setNumConsumers(value: number | undefined): Child;
    setTopic(value: string | undefined): Child;
    setGroup(value: string | undefined): Child;
    disconnect(): void;
    toWinstonLogLevel: (level: logLevel) => "info" | "warn" | "error" | "debug";
    init(): Promise<void>;
    setUp(): void;
    protected abstract getThis(): Child;
    protected initSender(): Promise<void>;
    protected startPolling(): void;
    protected _poll(consumer: Consumer): Promise<void>;
    protected setOnMessageReceived(handler: Callback1Void<Record<string, string[]>>): Promise<void>;
    protected initConsumer(): Promise<void>;
}

interface QueueInterface<Entry> {
    send(key: string, message: Entry | Entry[]): void;
    init(): void;
    deInit(): void;
}

declare class KafkaQueueService<Entry> extends BaseKafkaService<KafkaQueueService<Entry>> implements QueueInterface<Entry> {
    constructor(host: string, clientId: string);
    send(key: string, message: Entry | Entry[]): Promise<void>;
    deInit(): void;
    onReceive(handler: Callback1Void<Record<string, Entry[]>>): Promise<KafkaQueueService<Entry>>;
    protected getThis(): KafkaQueueService<Entry>;
}

declare class DateHelper {
    monthsName: string[];
    toH2HDateString(date: Date): string;
    toFrontendString(date: Date): string;
    getLastMonthsNames(num: number): string[];
    getMonthName(num: number): string;
    parseToDate(dateStr: string): Date | undefined;
    /**
     * To parse the given date string
     * with the given timezone in string format
     * and return the moment object for the same
     *
     * This return UTC formatted moment object
     *
     * @param dateStr
     * @param timezone
     */
    parseByTimeZone(dateStr: string, timezone: string): moment.Moment;
    /**
     * Converts the given date object to given timezone
     * and return the UTC formatted timezone
     *
     * @param date
     * @param timezone
     */
    convertToTimezone(date: Date, timezone: string): moment.Moment;
    /**
     * Returns number of days in the current month
     */
    daysInThisMonth(): number;
    /**
     * Check if two dates fall in the same week
     * @returns boolean
     */
    doesWeekMatch(dateA: Date, dateB: Date): boolean;
    /**
     * Check if the month of two dates fall in same year
     * @returns boolean
     */
    sameMonthYear(dateA: Date, dateB: Date): boolean;
    /**
     * Convert dateFrom
     * @returns date
     */
    dateFrom(date: string): Date;
    /**
     * Convert dateTo
     * @returns date
     */
    dateTo(date: string): Date;
}

declare class ObjectHelper {
    removeNullAndEmptyValues(obj: Record<string, unknown>): void;
    /**
     * Matches and search by using regex expression
     * in the given string
     *
     * @param object
     * @param pattern
     * @param exactMatch
     */
    doObjectContainKeyPattern(object: Record<string, unknown>, pattern: string, exactMatch?: boolean): boolean;
    /**
     * Matches and search by using regex expression
     * in the given string
     *
     * @param object
     * @param pattern
     */
    removeObjectKeyByPattern(object: Record<string, unknown>, pattern: string): Record<string, unknown>;
    /**
     * Filter out the object by matching the given string pattern
     * by using the regex expression object
     *
     * @param obj
     * @param pattern
     */
    filterObjectByKeys(obj: Record<string, unknown>, pattern: string): {
        [p: string]: unknown;
    };
}

/**
 * Attach user to req.currentUser
 * @param hashedToken string
 */
declare const internalAuth: (hashedToken: string) => (req: Request$1, res: Response, next: NextFunction) => Promise<void>;

interface Request extends express.Request {
    id: string;
}

/**
 * Logger Interceptor
 * To Intercept all the request and response
 */
declare const logRequestResponse: () => (request: Request, response: Response, next: NextFunction | undefined) => Promise<void>;

declare const _default: {
    query: string;
    headers: string;
    body: string;
    params: string;
};

declare class StringUtils {
    static get REGEX_EMAIL(): RegExp;
    static get REGEX_MOBILE(): RegExp;
    static isValidEmail(str: string, regex: RegExp): boolean;
    static isValidMobile(str: string, regex: RegExp): boolean;
    ucfirst(str: string): string;
    /**
     * To Generate a Random Alphabetical String
     * for the given number of digits
     *
     * @param digits Number of digits to be generated
     */
    generateRandomString(digits: number): string;
    /**
     * This function basically create a new string
     * with the help of given string
     *
     * @param str The String for which a new string has to be created
     */
    generateShaSum(str: string): Promise<string>;
}

declare class RequestUtils {
    static getTokenFromHeader: (req: Request$1) => string | undefined;
}

declare class ProcessUtils {
    static sleep(ms: number): Promise<void>;
}

declare class PathUtils {
    static addSlashPrefix(input: string): string;
    static prepare(...args: (string | undefined)[]): string;
}

declare function setUpSwagger({ app, title, version, description, baseURL, dirToScan, routePath, routePrefix, tags, contact, }: {
    app: express.Application;
    title: string;
    version?: string;
    description?: string;
    baseURL: string;
    dirToScan?: string[];
    routePath?: string;
    routePrefix?: string;
    tags?: {
        name: string;
        description?: string;
    }[];
    contact?: {
        name?: 'Castler';
        url?: 'https://www.castler.com';
    };
}): void;

declare class AppEnvUtils {
    init(): void;
}

declare class AwsEnvUtils {
    private client;
    private secretId;
    constructor({ region, secretId }: {
        region: string;
        secretId: string;
    });
    init(): Promise<number>;
    protected apply(envs: Record<string, never>): number;
}

interface DocumentInterface {
    name: string;
    ext: string;
    mimeType: string;
}

interface FileService {
    save(doc: DocumentInterface): void;
    find(): DocumentInterface[];
    findOne(): DocumentInterface | undefined;
    download(): void;
}

declare class LocalStorageService implements FileService {
    download(): void;
    find(): DocumentInterface[];
    findOne(): DocumentInterface | undefined;
    save(doc: DocumentInterface): void;
}

declare class S3StorageService implements FileService {
    client: S3Client;
    constructor(keyId: string, accessKey: string, region: string);
    download(): void;
    find(): DocumentInterface[];
    findOne(): DocumentInterface | undefined;
    save(doc: DocumentInterface): void;
}

/**
 * @swagger
 * /health:
 *   get:
 *     summary: Status check for project
 *     description: Check and tell if project is up or not
 *     tags:
 *       - Health Check Controller
 *     responses:
 *       200:
 *         description: A Blank response.
 */
declare function setUpExpressBase({ server, exceptionMonitor, allowedOrigins, allowedHeaders, useCORS, routePrefix, createRoutes, }: {
    server: Application;
    exceptionMonitor?: ExceptionMonitorService;
    useCORS?: boolean;
    allowedOrigins?: string[];
    allowedHeaders?: string[];
    routePrefix: string;
    createRoutes: () => Router;
}): void;

declare abstract class LogUtils {
    protected readonly _logger: Logger;
    protected constructor(_logger: Logger);
    error: (message: string, ...meta: unknown[]) => void;
    info: (message: string, ...meta: unknown[]) => void;
    warn: (message: string, ...meta: unknown[]) => void;
    silly: (message: string, ...meta: unknown[]) => void;
    alert: (message: string, ...meta: unknown[]) => void;
}

declare abstract class BaseError extends Error {
    error: {
        [key: string]: string[];
    } | undefined;
    protected constructor(message: string, error?: {
        [key: string]: string[];
    } | undefined);
    toString(): string;
}

declare class RequestValidationError extends BaseError {
    constructor(error: {
        [key: string]: string[];
    } | undefined);
}

declare class UnauthorizedError extends BaseError {
    constructor();
}

export { ApiResponse, AppEnvUtils, AwsEnvUtils, BaseError, BaseKafkaService, CrudService, DateHelper, ExceptionMonitorService, type FileService, type JobOptionInterface, JobService, KafkaQueueService, LocalStorageService, LogBuilder, type LogLevel, LogUtils, type MongooseOptionInterface, ObjectHelper, PathUtils, ProcessUtils, type QueueInterface, type Request, RequestUtils, RequestValidationError, S3StorageService, StringUtils, UnauthorizedError, createLogger, internalAuth, logRequestResponse, _default as requestFields, setUpExpressBase, setUpSwagger, startApplication };
