{{#imports}}
/// <reference path="{{&.}}" />
{{/imports}}

import request, { SuperAgentStatic, SuperAgentRequest, Response } from "superagent";

export type RequestHeaders = {
    [header: string]: string;
}
export type RequestHeadersHandler = (headers: RequestHeaders) => RequestHeaders;

export type ConfigureAgentHandler = (agent: SuperAgentStatic) => SuperAgentStatic;

export type ConfigureRequestHandler = (agent: SuperAgentRequest) => SuperAgentRequest;

export type CallbackHandler = (err: any, res?: request.Response) => void;

{{#definitions}}
export type {{&name}} = {{#tsType}}{{> type}}{{/tsType}};

{{/definitions}}

export type Logger = { log: (line: string) => any };

export interface ResponseWithBody<T> extends Response {
    body: T;
}

export interface CommonRequestOptions {
  $queryParameters?: {[param: string]: any};
  $domain?: string;
  $path?: string | ((path: string) => string);
}

/**
 * {{&description}}
 * @class {{&className}}
 * @param {(string)} [domainOrOptions] - The project domain.
 */
export class {{&className}} {

    private domain: string = "{{&domain}}";
    private errorHandlers: CallbackHandler[] = [];
    private requestHeadersHandler?: RequestHeadersHandler;
    private configureAgentHandler?: ConfigureAgentHandler;
    private configureRequestHandler?: ConfigureRequestHandler;

    constructor(domain?: string, private logger?: Logger) {
        if(domain) {
            this.domain = domain;
        }
    }

    getDomain() {
        return this.domain;
    }

    addErrorHandler(handler: CallbackHandler) {
        this.errorHandlers.push(handler);
    }

    setRequestHeadersHandler(handler: RequestHeadersHandler) {
        this.requestHeadersHandler = handler;
    }

    setConfigureAgentHandler(handler: ConfigureAgentHandler) {
        this.configureAgentHandler = handler;
    }

    setConfigureRequestHandler(handler: ConfigureRequestHandler) {
        this.configureRequestHandler = handler;
    }

    private request(method: string, url: string, body: any, headers: RequestHeaders, queryParameters: any, form: any, reject: CallbackHandler, resolve: CallbackHandler) {
        if(this.logger) {
          this.logger.log(`Call ${method} ${url}`);
        }

        const agent = this.configureAgentHandler
            ? this.configureAgentHandler(request)
            : request;

        let req = agent(method, url);
        if (this.configureRequestHandler) {
            req = this.configureRequestHandler(req);
        }
     
        req = req.query(queryParameters);

        if(body) {
            req.send(body);
        
            if(typeof(body) === 'object' && !(body.constructor.name === 'Buffer')) {
                headers['Content-Type'] = 'application/json';
            }
        }

        if(Object.keys(form).length > 0) {
            req.type('form');
            req.send(form);
        }

        if (this.requestHeadersHandler) {
            headers = this.requestHeadersHandler({...headers});
        }

        Object.keys(headers).forEach(key => {
            req.set(key, headers[key]);
        });

        req.end((error, response) => {
            if(error || !response.ok) {
                reject(error);
                this.errorHandlers.forEach(handler => handler(error));
            } else {
                resolve(response);
            }
        });
    }

{{#methods}}
    {{> method}}

{{/methods}}
}

export default {{&className}};
