/* global Restivus */
class API extends Restivus {
	constructor(properties) {
		super(properties);
		this.logger = new Logger(`API ${ properties.version ? properties.version : 'default' } Logger`, {});
		this.authMethods = [];
		this.helperMethods = new Map();
		this.defaultFieldsToExclude = {
			joinCode: 0,
			$loki: 0,
			meta: 0
		};

		this._config.defaultOptionsEndpoint = function() {
			if (this.request.method === 'OPTIONS' && this.request.headers['access-control-request-method']) {
				if (RocketChat.settings.get('API_Enable_CORS') === true) {
					this.response.writeHead(200, {
						'Access-Control-Allow-Origin': RocketChat.settings.get('API_CORS_Origin'),
						'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept, X-User-Id, X-Auth-Token'
					});
				} else {
					this.response.writeHead(405);
					this.response.write('CORS not enabled. Go to "Admin > General > REST Api" to enable it.');
				}
			} else {
				this.response.writeHead(404);
			}

			this.done();
		};
	}

	addAuthMethod(method) {
		this.authMethods.push(method);
	}

	success(result={}) {
		if (_.isObject(result)) {
			result.success = true;
		}

		return {
			statusCode: 200,
			body: result
		};
	}

	failure(result, errorType) {
		if (_.isObject(result)) {
			result.success = false;
		} else {
			result = {
				success: false,
				error: result
			};

			if (errorType) {
				result.errorType = errorType;
			}
		}

		return {
			statusCode: 400,
			body: result
		};
	}


	unauthorized(msg) {
		return {
			statusCode: 403,
			body: {
				success: false,
				error: msg ? msg : 'unauthorized'
			}
		};
	}

	addRoute(routes, options, endpoints) {
		//Note: required if the developer didn't provide options
		if (typeof endpoints === 'undefined') {
			endpoints = options;
			options = {};
		}

		//Allow for more than one route using the same option and endpoints
		if (!_.isArray(routes)) {
			routes = [routes];
		}

		routes.forEach((route) => {
			//Note: This is required due to Restivus calling `addRoute` in the constructor of itself
			if (this.helperMethods) {
				Object.keys(endpoints).forEach((method) => {
					if (typeof endpoints[method] === 'function') {
						endpoints[method] = { action: endpoints[method] };
					}

					//Add a try/catch for each much
					const originalAction = endpoints[method].action;
					endpoints[method].action = function() {
						this.logger.debug(`${ this.request.method.toUpperCase() }: ${ this.request.url }`);
						let result;
						try {
							result = originalAction.apply(this);
						} catch (e) {
							this.logger.debug(`${ method } ${ route } threw an error:`, e);
							return RocketChat.API.v1.failure(e.message, e.error);
						}

						return result ? result : RocketChat.API.v1.success();
					};

					for (const [name, helperMethod] of this.helperMethods) {
						endpoints[method][name] = helperMethod;
					}

					//Allow the endpoints to make usage of the logger which respects the user's settings
					endpoints[method].logger = this.logger;
				});
			}

			super.addRoute(route, options, endpoints);
		});
	}
}

RocketChat.API = {};

const getUserAuth = function _getUserAuth() {
	const invalidResults = [undefined, null, false];
	return {
		token: 'services.resume.loginTokens.hashedToken',
		user() {
			if (this.bodyParams && this.bodyParams.payload) {
				this.bodyParams = JSON.parse(this.bodyParams.payload);
			}

			for (let i = 0; i < RocketChat.API.v1.authMethods.length; i++) {
				const method = RocketChat.API.v1.authMethods[i];

				if (typeof method === 'function') {
					const result = method.apply(this, arguments);
					if (!invalidResults.includes(result)) {
						return result;
					}
				}
			}

			let token;
			if (this.request.headers['x-auth-token']) {
				token = Accounts._hashLoginToken(this.request.headers['x-auth-token']);
			}

			return {
				userId: this.request.headers['x-user-id'],
				token
			};
		}
	};
};

RocketChat.API.v1 = new API({
	version: 'v1',
	useDefaultAuth: true,
	prettyJson: true,
	enableCors: false,
	auth: getUserAuth()
});

RocketChat.API.default = new API({
	useDefaultAuth: true,
	prettyJson: true,
	enableCors: false,
	auth: getUserAuth()
});
