
const { transaction, pool } = require('../connection');

function fields (obj, keys) {
	const retval = {};
	for (const key of keys) {
		retval[key] = obj[key];
	}
	return retval;
}

/** Perform a restrict (aka select) operation on the relation.
 *
 * Recognised queries are:
 *
 * - tstamp:i; tolerance:j
 * - pos:λ,φ; radius: ρ
 *
 * Defaults:
 *
 * - tolerance: 1000 (ms)
 * - radius: 1 (m)
 *
 * Queries are ANDed together.
 */
function restrict (q="") {
	let sql = [];

	/* This takes in a string of the form:
	 * "key0=value0;key1=value1;key2=value=2"
	 * and returns an object like:
	 * { key0: "value0", key1: "value1", key2: "value=2" }
	 */
	const constraints = Object.fromEntries(q.split(/\s*;\s*/).map(i => {
		const parts = i.split(/(\s*:\s*)/);
		return [parts[0], parts.slice(2).join("")]
	}));

	// TODO Consider adding an event_tstamp field, or indexing meta->>tstamp
	if ("tstamp" in constraints) {
		const ts = new Date(constraints.tstamp);

		if (isNaN(ts)) {
			throw new Error("Invalid query");
		}

		const tolerance = constraints.tolerance ?? 1000;
		const ts0 = new Date(ts.valueOf()-Number(tolerance));
		const ts1 = new Date(ts.valueOf()+Number(tolerance));
		console.log(tolerance, ts, ts0, ts1);
		console.log(constraints);

		if (!isNaN(ts0) && !isNaN(ts1)) {
			sql.push(`(meta->>'tstamp')::timestamptz BETWEEN '${ts0.toISOString()}' AND '${ts1.toISOString()}'`);
		} else {
			throw new Error("Invalid query");
		}
	}

	if ("pos" in constraints) {
		const [ lon, lat ] = constraints.pos.split(",").map(i => Number(i));

		if (isNaN(lon) || isNaN(lat)) {
			throw new Error("Invalid query");
		}

		const radius = constraints.radius ?? 1;

		sql.push(`ST_DistanceSphere(geometry, ST_Point(${lon}, ${lat}, 4326)) <= ${radius}`)
	}

	return sql.length ? ("WHERE " + sql.join(" AND ")) : "";
}

async function get (opts = {}) {
	const client = await pool.connect();

	const text = `
		SELECT meta
		FROM real_time_inputs
		${restrict(opts.q)}
		ORDER BY tstamp DESC
		LIMIT $1
		OFFSET $2
	`;

	const values = [ opts.limit || 300, opts.offset || 0 ];

	const res = await client.query(text, values);
	client.release();
	// TODO Must change this to use utils/project instead.
	return opts.fields
		? res.rows.map(r => fields(r.meta, opts.fields.split(/[;,\s]+/)))
		: res.rows.map(r => r.meta);
}


module.exports = get;
