/// <reference types="node" />
import * as http from 'http';
import { OneOrMore } from '../models/Generics';
import ServerManager from '../methods/servermanager';
import { Invokable } from '../models/Invokable';

export interface CraydentHttp extends http.Server {
    routes: Route[];
    /**
     * @deprecated Load balancing should be done by the infrastructure
     */
    loadBalance: (ips: string | string[]) => void;
    // #region all overloads
    all<T extends string, P extends readonly Parameter[]>(
        config: {
            path: T;
            callback: RequestHandler<T, P>;
            readonly parameters?: P;
        }
    ): void;
    all<T extends string, P extends Parameter[]>(
        path: T,
        callback: RequestHandler<T, P>,
        parameters?: P
    ): void;
    // all<T extends string, P extends readonly Parameter[]>(
    //     configOrPath: T | {
    //         path: T;
    //         callback: RequestHandler<T, P>;
    //         readonly parameters?: P;
    //     },
    //     callback?: RequestHandler<T, P>,
    //     parameters?: P
    // ): void;
    // #endregion

    // #region delete overloads
    delete<T extends string, P extends readonly Parameter[]>(
        config: {
            path: T;
            callback: RequestHandler<T, P>;
            readonly parameters?: P;
        }
    ): void;
    delete<T extends string, P extends readonly Parameter[]>(
        path: T,
        callback: RequestHandler<T, P>,
        parameters?: P
    ): void;
    // delete<T extends string, P extends readonly Parameter[]>(
    //     configOrPath: T | {
    //         path: T;
    //         callback: RequestHandler<T, P>;
    //         readonly parameters?: P;
    //     },
    //     callback?: RequestHandler<T, P>,
    //     parameters?: Parameter[]
    // ): void;
    // #endregion

    // #region get overloads
    get<T extends string, P extends readonly Parameter[]>(
        config: {
            path: T;
            callback: RequestHandler<T, P>;
            readonly parameters?: P;
        }
    ): void;
    get<T extends string>(
        path: T,
        callback: RequestHandler<T>
    ): void;
    // get<T extends string, P extends readonly Parameter[]>(
    //     configOrPath: T | {
    //         path: T;
    //         callback: RequestHandler<T, P>;
    //         readonly parameters?: P;
    //     },
    //     callback?: RequestHandler<T>
    // ): void;
    // #endregion

    // #region patch overloads
    patch<T extends string, P extends readonly Parameter[]>(
        config: {
            path: T;
            callback: RequestHandler<T, P>;
            readonly parameters?: P;
        }
    ): void;
    patch<T extends string, P extends readonly Parameter[]>(
        path: T,
        callback: RequestHandler<T, P>,
        parameters?: P
    ): void;
    // patch<T extends string, P extends readonly Parameter[]>(
    //     configOrPath: T | {
    //         path: T;
    //         callback: RequestHandler<T, P>;
    //         readonly parameters?: P;
    //     },
    //     callback?: RequestHandler<T, P>,
    //     parameters?: P
    // ): void;
    // #endregion

    // #region post overloads
    post<T extends string, P extends readonly Parameter[]>(
        config: {
            path: T;
            callback: RequestHandler<T, P>;
            readonly parameters?: P;
        }
    ): void;
    post<T extends string, P extends readonly Parameter[]>(
        path: T,
        callback: RequestHandler<T, P>,
        parameters?: P
    ): void;
    // post<T extends string, P extends readonly Parameter[]>(
    //     configOrPath: T | {
    //         path: T;
    //         callback: RequestHandler<T, P>;
    //         readonly parameters?: P;
    //     },
    //     callback?: RequestHandler<T, P>,
    //     parameters?: P
    // ): void;
    // #endregion

    // #region put overloads
    put<T extends string, P extends readonly Parameter[]>(
        config: {
            path: T;
            callback: RequestHandler<T, P>;
            readonly parameters?: P;
        }
    ): void;
    put<T extends string, P extends readonly Parameter[]>(
        path: T,
        callback: RequestHandler<T, P>,
        parameters?: P
    ): void;
    // put<T extends string, P extends readonly Parameter[]>(
    //     configOrPath: T | {
    //         path: T;
    //         callback: RequestHandler<T, P>;
    //         readonly parameters?: P;
    //     },
    //     callback?: RequestHandler<T, P>,
    //     parameters?: P
    // ): void;
    // #endregion

    // #region use overloads
    use<T extends string>(
        path: T,
        callback: RequestHandler<T>
    ): void | ((this: CraydentHttp, callback: OneOrMore<RequestHandler>) => void);
    use<T extends string>(
        callback: RequestHandler<T>
    ): void | ((this: CraydentHttp, callback: OneOrMore<RequestHandler>) => void);
    // #endregion
}
export interface Route<T extends string = string, P extends Parameter[] = Parameter[]> {
    path: T;
    callback: RequestHandler<T, P>[];
    method: 'all' | 'delete' | 'get' | 'middleware' | 'patch' | 'post' | 'put';
    parameters?: P;
}
export interface Parameter {
    name: string;
    type?: 'array' | 'bool' | 'boolean' | 'date' | 'email' | 'int' | 'number' | 'object' | 'regexp' | 'string';
    required?: boolean;
    default?: any;
}
type ParameterTypeMap = {
    string: string;
    email: string;
    object: { [key: string]: any }
    number: number;
    int: number;
    bool: boolean;
    boolean: boolean;
    array: any[];
    date: Date;
    regexp: RegExp;
};

type ParameterToType<P extends Parameter> =
    P['type'] extends keyof ParameterTypeMap
    ? P['required'] extends true
    ? ParameterTypeMap[P['type']] // Required field
    : ParameterTypeMap[P['type']] | undefined // Optional field
    : any;

type MapParameters<Params extends readonly Parameter[]> = {
    [P in Params[number]as P['name']]: ParameterToType<P>;
};

export type ExtractParams<T extends string> =
    T extends `${infer _Start}\${${infer Param}}${infer Rest}`
    ? { [K in Param | keyof ExtractParams<Rest>]: string }
    : {};


export type RequestHandlerParams<
    T extends string,
    P extends readonly Parameter[],
    Request extends typeof http.IncomingMessage,
    Response extends typeof http.ServerResponse> = {
        request: InstanceType<Request>;
        response: InstanceType<Response> & { req: InstanceType<Request> };
        value?: ExtractParams<T> & MapParameters<P>;
        next?: Invokable;
    };

export type RequestHandler<
    T extends string = string,
    P extends readonly Parameter[] = readonly Parameter[],
    Request extends typeof http.IncomingMessage = typeof http.IncomingMessage,
    Response extends typeof http.ServerResponse = typeof http.ServerResponse
> = (this: ServerManager, params: RequestHandlerParams<T, P, Request, Response>) => any | Promise<any> | GeneratorFunction | AsyncGeneratorFunction;