import { ApiServer } from './index';
import { versionNumberToObject, slashed } from './helpers';
import { Request, Response, Route } from './types';
import { ResourceNotFoundError, InternalError } from './errors';

export class RequestHandler {
    private _middlewareCount = 0;
    private _middleware:Function[] = [];

    constructor(
        private ApiServer: ApiServer,
        private request: Request,
        private response: Response
    ) {
        this.route();

        if(this.request.route) {
            this.middleware();
            this.handle();
        } else {
            response.send(new ResourceNotFoundError);
        }
    }

    private middleware() {
        let route:any = this.request.route;

        this._middleware = [];
        this._middleware = this.ApiServer.middleware.slice(0);
        if(route.middleware) this._middleware = this._middleware.concat(route.middleware); 
    }

    private handle (...args: Array<any>) {

        let route:any = this.request.route;

        if(this._middlewareCount < this._middleware.length && this._middleware[this._middlewareCount]) {
            this._middlewareCount++;
            return this._middleware[this._middlewareCount - 1](this.request, this.response, this.handle.bind(this));
        }

        // Service
        if(route && typeof route.service === 'function') {
            return route.service(this.request, this.response);
        } else {
            this.response.send(new InternalError);
            
            throw new Error('Route service must be a function');
        }
    }
    

    // Part of route handler by Mihkel Baranov
    private 
() {
        return this.ApiServer.routes.find(route => {
            let version = versionNumberToObject(route.version || '1.0.0');
            let requestUrl = this.request.url;

            let path = slashed(route.path);
            let regex = new RegExp(path.replace(/:[^\s/]+/g, "([^/\]+)"));
            let matches = slashed(requestUrl.split("?")[0]).match(regex);
            let params = path.match(/:[^\s/]+/g);

            if (matches && matches[0] === matches["input"] && route.method === this.request.method) {
                for (let k in params) {
                    this.request.params[params[k].slice(1)] = decodeURI(matches[parseInt(k) + 1]);
                }

                this.request.route = <Route>route;
                
                return true;
            }
        });
    }

    // Static
    static handle(ApiServer: ApiServer, request: Request, response: Response) {
        return new RequestHandler(ApiServer, request, response);
    }
}
route