const util = require('util');
const hashPassword = require('./hashPassword');
const { User } = require('@dougal/user');
const { pool } = require('../connection');
// const Organisations = require('../../organisations/Organisations');


class ServerUser extends User {

	client // PostgreSQL client connection

	constructor (data, client) {
		super (data);
		if (data?.password) this.password = data.password;
		if (data?.hash) this.values.hash = data.hash;
		this.client = client;
	}

	/*
	 * Getters
	 */

	get hash () {
		return this.values.hash;
	}

	/*
	 * Setters
	 */

	set password (v) {
		if (v?.length) {
			this.values.hash = hashPassword(this.id, v);
		} else {
			delete this.values.hash;
		}
	}

	/*
	 * Validation methods
	 */

	/*
	 * Filtering methods
	 */

	/*
	 * General methods
	 */

	checkPassword (text) {
		return Boolean(this.hash && hashPassword(this.id, text) == this.hash);
	}

	/*
	 * Conversion and presentation methods
	 */

	toJSON () {
		return {
			...super.toJSON(),
			hash: this.hash
		}
	}

	[util.inspect.custom] (depth, options) {
		return `User ${this.id} (${this.name}) { ${this.organisations.entries().map( ([k, v]) => {
			let str = "";
			str += k+": ";
			str += v.read ? "r" : "-";
			str += v.write ? "w" : "-";
			str += v.edit ? "e" : "-";
			return str;
		}).join("; ")} }`;
	}

	/*
	 * SQL methods
	 */

	/** Read user(s) from the database
	 *
	 * @a client is a database connection
	 * @a id is an optional user ID
	 *
	 * If `id` is not provided, it will return
	 * all users in the database.
	 */
	static async fromSQL (client, id) {
		if (!client) {
			client = pool;
		}

		if (id) {
			// Get a specific user
			const text = `
				SELECT key, data, last_modified
				FROM public.keystore
				WHERE type = 'user' AND key = $1;
			`;

			const res = await client.query(text, [ id ]);
			const obj = res.rows[0];

			if (obj) {
				return new ServerUser({...obj.data, id: obj.key, last_modified: obj.last_modified}, client);
			}
		} else {
			// Get all users
			const text = `
				SELECT key, data, last_modified
				FROM public.keystore
				WHERE type = 'user';
			`;

			const res = await client.query(text);

			return res.rows?.map(row => new ServerUser({
				...row.data,
				id: row.key,
				last_modified: row.last_modified
			}, client));
		}
	}

	static async authenticateSQL (name, password, client) {
		if (!client) {
			client = pool;
		}

		const text = `
			SELECT key, data, last_modified
			FROM public.keystore
			WHERE type = 'user'
				AND data->>'name' = $1
			ORDER BY key;
		`;

		const res = await client.query(text, [name]);
		for (const row of res.rows) {
			const user = new ServerUser({
				...row.data,
				id: row.key,
				last_modified: row.last_modified
			}, client);
			if (user.checkPassword(password)) {
				user.password = null; // Remove the hash from the reply
				return user;
			}
		}
	}

	/** Save this user to the database
	 */
	async save () {
		if (!this.client) {
			this.client = pool;
		}

		const data = this.toJSON();
		const id = data.id;
		delete data.id;

		const text = `
			INSERT INTO public.keystore AS t
			(type, key, data)
			VALUES ('user', $1, $2::jsonb)
			ON CONFLICT (type, key)
			DO UPDATE SET
				data = EXCLUDED.data
			WHERE
				t.type = EXCLUDED.type AND t.key = EXCLUDED.key
			RETURNING key, data, last_modified;
		`;

		const res = await this.client?.query(text, [ id, data ]);

		if (res?.rows?.length) {
			const row = res.rows[0];
			return new ServerUser({
				...row.data,
				id: row.key,
				last_modified: row.last_modified
			}, this.client);
		}
	}

	/** Delete this user from the database
	 */
	async remove () {
		if (!this.client) {
			this.client = pool;
		}

		const text = `
			DELETE FROM public.keystore
			WHERE type = 'user' AND key = $1;
		`;

		const res = await this.client?.query(text, [ this.id ]);
		console.log("ID", this.id);
		console.log(res);
		return res.rowCount;
	}

}


if (typeof module !== 'undefined' && module.exports) {
    module.exports = ServerUser; // CJS export
}

// ESM export
if (typeof exports !== 'undefined' && !exports.default) {
    exports.default = ServerUser; // ESM export
}
