'use strict';

const Table = require('.');
const ModelPs = require('../utils/model_pubsub');

/**
 * LocalGroup
 *
 * An app-managed group with an explicit member list. Local groups behave exactly
 * like groups carried from SSO/LDAP: their names can be used as a Permission
 * subject (subjectType: 'group'), and in conf.auth.adminGroups / groupRoleMap.
 * Membership is merged into a session's identity by Permission.effectiveFor.
 */
class LocalGroup extends Table{
	static _key = 'name';
	static _keyMap = {
		'name': {isRequired: true, type: 'string', min: 1, max: 100},
		'members': {default: function(){return []}, isRequired: false, type: 'object'},
		'created_by': {isRequired: true, type: 'string', min: 3, max: 500},
		'created_on': {default: function(){return (new Date).getTime()}},
		'updated_on': {default: function(){return (new Date).getTime()}, always: true},
	}

	// Normalize a group name to a slug (lowercase, safe chars) so it round-trips
	// cleanly through URLs and matches consistently against session groups.
	static slug(name){
		return String(name || '').trim().toLowerCase().replace(/[^a-z0-9._-]+/g, '-').replace(/^-+|-+$/g, '');
	}

	static async create(data){
		data.name = this.slug(data.name);
		if(!data.name){
			throw new this.errors.ObjectValidateError([{key: 'name', message: 'A group name is required.'}]);
		}
		if(!Array.isArray(data.members)) data.members = [];
		return super.create(data);
	}

	async addMember(username){
		username = String(username || '').trim();
		if(!username){
			throw new this.constructor.errors.ObjectValidateError([{key: 'username', message: 'A username is required.'}]);
		}
		let members = Array.isArray(this.members) ? this.members : [];
		if(members.includes(username)) return this;
		return this.update({members: [...members, username]});
	}

	async removeMember(username){
		let members = (Array.isArray(this.members) ? this.members : []).filter(m => m !== username);
		return this.update({members});
	}

	// Expose the members as {group, username} objects (so the UI's per-member
	// remove button knows which group it belongs to) plus a count. Flows through
	// both the REST list and websocket payloads.
	toJSON(){
		let base = super.toJSON();
		let members = Array.isArray(base.members) ? base.members : [];
		return {
			...base,
			memberList: members.map(u => ({group: base.name, username: u})),
			memberCount: members.length,
		};
	}
}

LocalGroup.register(ModelPs(LocalGroup));

module.exports = {LocalGroup};
