import * as core from './core.js';
import * as form from './form.js';

const kRefreshInterval = 1 * 7 * 24 * 60 * 60 * 1000;

/**
 * Makes a Base64 value URL safe
 * @param {string} value
 * @returns TODOC
 */
function b64url(value) {
	value = value.replaceAll('+', '-').replaceAll('/', '_');
	let equals = value.indexOf('=');

	if (equals !== -1) {
		return value.substring(0, equals);
	} else {
		return value;
	}
}

/**
 * TODOC
 * @param {string} value
 * @returns
 */
function unb64url(value) {
	value = value.replaceAll('-', '+').replaceAll('_', '/');
	let remainder = value.length % 4;

	if (remainder == 3) {
		return value + '=';
	} else if (remainder == 2) {
		return value + '==';
	} else {
		return value;
	}
}

/**
 * Creates a JSON Web Token
 * @param {object} payload Object: {"name": "username"}
 * @returns the JWT
 */
function makeJwt(payload) {
	const ids = ssb.getIdentities(':auth');
	let id;

	if (ids?.length) {
		id = ids[0];
	} else {
		id = ssb.createIdentity(':auth');
	}

	const final_payload = b64url(
		base64Encode(
			JSON.stringify(
				Object.assign({}, payload, {
					exp: new Date().valueOf() + kRefreshInterval,
				})
			)
		)
	);
	const jwt = [
		b64url(base64Encode(JSON.stringify({alg: 'HS256', typ: 'JWT'}))),
		final_payload,
		b64url(ssb.hmacsha256sign(final_payload, ':auth', id)),
	].join('.');
	return jwt;
}

/**
 * Validates a JWT ?
 * @param {*} session TODOC
 * @returns
 */
function readSession(session) {
	let jwt_parts = session?.split('.');

	if (jwt_parts?.length === 3) {
		let [header, payload, signature] = jwt_parts;
		header = JSON.parse(utf8Decode(base64Decode(unb64url(header))));

		if (header.typ === 'JWT' && header.alg === 'HS256') {
			signature = unb64url(signature);
			let id = ssb.getIdentities(':auth');

			if (id?.length && ssb.hmacsha256verify(id[0], payload, signature)) {
				const result = JSON.parse(utf8Decode(base64Decode(unb64url(payload))));
				const now = new Date().valueOf();

				if (now < result.exp) {
					print(`JWT valid for another ${(result.exp - now) / 1000} seconds.`);
					return result;
				} else {
					print(`JWT expired by ${(now - result.exp) / 1000} seconds.`);
				}
			} else {
				print('JWT verification failed.');
			}
		} else {
			print('Invalid JWT header.');
		}
	}
}

/**
 * TODOC
 * @param {*} headers most likely an object
 * @returns
 */
function getCookies(headers) {
	let cookies = {};

	if (headers.cookie) {
		let parts = headers.cookie.split(/,|;/);
		for (let i in parts) {
			let equals = parts[i].indexOf('=');
			let name = parts[i].substring(0, equals).trim();
			let value = parts[i].substring(equals + 1).trim();
			cookies[name] = value;
		}
	}

	return cookies;
}

/**
 * Gets a user's permissions based on it's session ?
 * @param {*} session TODOC
 * @returns
 */
function getPermissions(session) {
	let permissions;
	let entry = readSession(session);
	if (entry) {
		permissions = getPermissionsForUser(entry.name);
		permissions.authenticated = entry.name !== 'guest';
	}
	return permissions || {};
}

/**
 * Get a user's permissions ?
 * @param {string} userName TODOC
 * @returns
 */
function getPermissionsForUser(userName) {
	let permissions = {};
	if (
		core.globalSettings &&
		core.globalSettings.permissions &&
		core.globalSettings.permissions[userName]
	) {
		for (let i in core.globalSettings.permissions[userName]) {
			permissions[core.globalSettings.permissions[userName][i]] = true;
		}
	}
	return permissions;
}

/**
 * TODOC
 * @param {*} headers
 * @returns
 */
function query(headers) {
	let session = getCookies(headers).session;
	let entry;
	let autologin = tildefriends.args.autologin;
	if ((entry = autologin ? {name: autologin} : readSession(session))) {
		return {
			session: entry,
			permissions: autologin
				? getPermissionsForUser(autologin)
				: getPermissions(session),
		};
	}
}

/**
 * Refreshes a JWT ?
 * @param {*} credentials TODOC
 * @returns
 */
function makeRefresh(credentials) {
	if (credentials?.session?.name) {
		return {
			token: makeJwt({name: credentials.session.name}),
			interval: kRefreshInterval,
		};
	}
}

export {query, makeRefresh};