/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

const express = require('express');
const Meter = require('../models/Meter');
const User = require('../models/User');
const { log } = require('../log');
const validate = require('jsonschema').validate;
const { getConnection } = require('../db');
const { isTokenAuthorized } = require('../util/userRoles');
const requiredAdmin = require('./authenticator').adminAuthMiddleware;
const optionalAuthenticator = require('./authenticator').optionalAuthMiddleware;
const Point = require('../models/Point');

const router = express.Router();
router.use(optionalAuthenticator);

/**
 * Defines the format in which we want to send meters and controls what information we send to the client, if logged in or not.
 * @param meter
 * @returns {{id, name}}
 */
function formatMeterForResponse(meter, loggedIn) {
	const formattedMeter = {
		id: meter.id,
		name: meter.name,
		enabled: meter.enabled,
		displayable: meter.displayable,
		ipAddress: null,
		meterType: null,
		timeZone: null,
		gps: meter.gps
	};

	// Only logged in users can see IP addresses and types
	if (loggedIn) {
		formattedMeter.ipAddress = meter.ipAddress;
		formattedMeter.meterType = meter.type;
		formattedMeter.timeZone = meter.meterTimezone;
	}

	return formattedMeter;
}

/**
 * GET information on displayable meters (or all meters, if logged in as an admin.)
 */
router.get('/', async (req, res) => {
	try {
		const conn = getConnection();
		let query;
		const token = req.headers.token || req.body.token || req.query.token;
		if (req.hasValidAuthToken && (await isTokenAuthorized(token, User.role.ADMIN))) {
			query = Meter.getAll;
		} else {
			query = Meter.getDisplayable;
		}
		const rows = await query(conn);
		res.json(rows.map(row => formatMeterForResponse(row, req.hasValidAuthToken)));
	} catch (err) {
		log.error(`Error while performing GET all meters query: ${err}`, err);
	}
});

/**
 * GET information for a specific meter by id
 * Prohibits access to meters that are not displayable if not logged in
 * @param {int} meter_id
 */
router.get('/:meter_id', async (req, res) => {
	const validParams = {
		type: 'object',
		maxProperties: 1,
		required: ['meter_id'],
		properties: {
			meter_id: {
				type: 'string',
				pattern: '^\\d+$'
			}
		}
	};
	if (!validate(req.params, validParams).valid) {
		res.sendStatus(400);
	} else {
		const conn = getConnection();
		try {
			const meter = await Meter.getByID(req.params.meter_id, conn);
			if (meter.displayable || req.hasValidAuthToken) {
				// If the meter is displayable, fine. If the meter is
				// not displayable but the user is logged in, also fine.
				res.json(formatMeterForResponse(meter, req.hasValidAuthToken));
			} else {
				res.sendStatus(400);
			}
		} catch (err) {
			log.error(`Error while performing GET specific meter by id query: ${err}`, err);
			res.sendStatus(500);
		}
	}
});

router.post('/edit', requiredAdmin('edit meters'), async (req, res) => {
	const validParams = {
		type: 'object',
		maxProperties: 5,
		required: ['id', 'enabled', 'displayable', 'timeZone'],
		properties: {
			id: { type: 'integer' },
			enabled: { type: 'bool' },
			displayable: { type: 'bool' },
			timeZone: {
				oneOf: [
					{ type: 'string' },
					{ type: 'null' }
				]
			},
			gps: {
				oneOf: [
					{
						type: 'object',
						required: ['latitude', 'longitude'],
						properties: {
							latitude: { type: 'number', minimum: '-90', maximum: '90' },
							longitude: { type: 'number', minimum: '-180', maximum: '180' }
						}
					},
					{ type: 'null' }
				]
			}
		}
	};

	const validatorResult = validate(req.body, validParams);
	if (!validatorResult.valid) {
		log.warn(`Got request to edit meters with invalid meter data, errors:${validatorResult.errors}`);
		res.status(400);
	} else {
		const conn = getConnection();
		try {
			const meter = await Meter.getByID(req.body.id, conn);
			meter.enabled = req.body.enabled;
			meter.displayable = req.body.displayable;
			meter.meterTimezone = req.body.timeZone;
			meter.gps = (req.body.gps) ? new Point(req.body.gps.longitude, req.body.gps.latitude) : null;
			await meter.update(conn);
		} catch (err) {
			log.error('Failed to edit meter', err);
			res.status(500).json({ message: 'Unable to edit meters.', err });
		}
		res.status(200).json({ message: `Successfully edited meter ${req.body.id}` });
	}
});

module.exports = router;


@@ -892,16 +892,29 @@
 gged in 
+and an Admin 
 or not.%0A
@@ -929,16 +929,42 @@
 m meter%0A
+ * @param loggedInAsAdmin%0A
  * @retu
@@ -1027,24 +1027,31 @@
 er, loggedIn
+AsAdmin
 ) %7B%0A%09const f
@@ -1261,20 +1261,21 @@
 gged in 
-user
+Admin
 s can se
@@ -1312,16 +1312,23 @@
 loggedIn
+AsAdmin
 ) %7B%0A%09%09fo
@@ -1746,20 +1746,40 @@
 oken;%0A%09%09
-if (
+const loggedInAsAdmin = 
 req.hasV
@@ -1844,16 +1844,39 @@
 .ADMIN))
+;%0A%09%09if (loggedInAsAdmin
 ) %7B%0A%09%09%09q
@@ -1944,16 +1944,17 @@
 le;%0A%09%09%7D%0A
+%0A
 %09%09const 
@@ -2034,36 +2034,30 @@
 se(row, 
-req.hasValidAuthToke
+loggedInAsAdmi
 n)));%0A%09%7D

Fix formatting to consider admin role