const bcrypt = require( 'bcrypt' );
const fs = require( 'fs-extra' );
const path = require( 'path' );
const glob = require( 'glob' );
const Mustache = require( 'mustache' );
const execSync = require( 'child_process' )
	.execSync;
const dnsSync = require( 'dns-sync' );
const jwt = require( 'jwt-simple' );
const moment = require( 'moment' );
const UUID = require( 'uuid' );
const Outputs = require( './Outputs.js' );
const Pagans = require( './Pagans.js' );
const config = require( '../tribes/townconf.js' );

const checkdata = require( `../nationchains/socialworld/contracts/checkdata.js`);
/*
tribeid manager

/tribes/tribeid
Manage a tribeid space
* create
* update by managing option and contract
* delete a tribeid
* check accountability and

*/
const Tribes = {};
Tribes.init = () => {
	console.group( 'init Tribes' );
	let tribeids = [];
	let routes = glob.sync( './routes/*.js' )
		.map( f => {
			return { url: `/${path.basename(f,'.js')}`, route: f }
		} );
	let DOMs = [];
	let appname = {};
	TribesGlobalConfig = glob.sync( `${config.tribes}/**/clientconf.json` )
		.map( f => {
			const conf = fs.readJSONSync( f );
			// check if plugins exist and add it in .plugins of each tribeid conf
			conf.plugins = glob.sync( `${config.tribes}/${conf.tribeid}/plugins/**/package.json` )
				.map( p => {
					const pack = fs.readJsonSync( p, 'utf8' );
					routes.push( { url: `/${pack.name}`, route: `${config.tribes}/${conf.tribeid}/plugins/${pack.name}/route.js` } );
					return pack;
				} );
			//Add here any other info to get a global view and init
			//...
			tribeids.push( conf.tribeid );
			DOMs = [ ...new Set( [ ...DOMs, ...conf.allowedDOMs ] ) ];
			if( conf.website ) appname[ conf.tribeid ] = Object.keys( conf.website )
			return conf;
		} );
	// store global conf fofs.existsSync( `${config.tmp}/clientconfglob.json` )r sharing to other api
	fs.outputJsonSync( `${config.tmp}/clientconfglob.json`, TribesGlobalConfig, {
		spaces: 2
	} );
	return { tribeids, routes, DOMs, appname }
}
Tribes.create = ( data ) => {
	/* data = clientconf.json
	{
	  "tribeid": "apixtribe",
	  "genericpsw": "Trze3aze!",
	  "website": {
	    "presentation":"https://www.apixtribe.org",
	    "webapp": "https://webapp.apixtribe.org"
	  },
	  "allowedDOMs": ["local.fr", "localhost:9002", "ndda.fr", "apixtribe.org"],
	  "clientname": "apixtribe",
	  "clientlogo": "",
		"geoloc": [],
	  "useradmin": {PUBKEY:"",EMAIL:"",LOGIN:"adminapixtribe",UUID:"adminapixtribe"},
	  "smtp": {
	    "emailFrom": "support@apixtribe.org",
	    "emailcc": [],
	    "service": "gmail",
	    "auth": {
	      "user": "antonin.ha@gmail.com",
	      "pass": "Ha06110"
	    }
	  },
	  "accepted-language": "fr,en",
	  "langueReferential": ["fr"]
	}
	What about:
	  "tribeid": same than the folder where all the client's file are stored
	  "genericpsw": a generic password for new user need upper lowercase number ans special char
	  "dnsname": a domain name belonging to the client
	  "subdns": "www", a sub domain subdns.dnsname give a public web access to
		"website": { keywebsite:url}, give access to config.tribes/tribeid/www/keywebsite/index.html,
	  "allowedDOMs": ["local.fr", "localhost:9002", "nnda.fr"], //for CORS, @TODO generate from prévious URL this allow this apixtribe instance to be accessible
	  "clientname": Name of the organisation if any,
	  "clientlogo": logo of the organisation if any,
	  "geoloc": [], if any
		"useradmin": { this is the 1st user create automaticaly to make gui available for the 1st user
			"PUBKEY":public key to be authentify without an email,
			"EMAIL":user email, we need at least one of authentification set up after the user can use both or only one
			"LOGIN": login to use for access admintribeid,
			"UUID": unique id normaly UUID but a uuid admintribeid is the same person in any apixtribe instance so we use it by convention.
			"xlang": lang used by this user
		},
	  "smtp": { smtp used to send email by nodemailer lib basic example with a google account
	    "emailFrom": "support@xx.fr",
	    "emailcc": [],
	    "service": "gmail",
	    "auth": {
	      "user": "antonin.ha@gmail.com",
	      "pass": "Ha06110"
	    }
	  },
	  "accepted-language": "fr,en", list of accepted-language in terme of http request.
	  "langueReferential": ["fr"], list of the text that have to be translate in referentials
	}
	*/
	//update tmp/confglog.json
	const dataclient = Tribes.init();
	//return in prod all instance apxinfo={tribeids:[],logins:[]}
	// in dev return only local
	//check tribeid name is unique
	logger.info( 'liste des tribeid', dataclient.tribeids )
	if( dataclient.tribeids.includes( data.tribeid ) ) {
		return { status: 403, payload: { model: "client", info: [ 'tribeidalreadyexist' ] } }
	}
	//loginsglob = {login:tribeid}
	let loginsglob = {};
	if( fs.existsSync( `${config.tmp}/loginsglob.json`, 'utf-8' ) ) {
		loginsglob = fs.readJsonSync( `${config.tmp}/loginsglob.json`, 'utf-8' );
	}
	const logins = Object.keys( loginsglob );
	if( logins.includes( data.useradmin.login ) ) {
		return { status: 403, payload: { model: "client", info: [ 'loginalreadyexist' ] } }
	}
	fs.ensureDirSync( `${config.tribes}/${data.tribeid}` );
	[ 'users', 'www', 'referentials', 'nationchains' ].forEach( r => {
		fs.copySync( `${config.mainDir}/setup/tribes/apixtribe/${r}`, `${config.tribes}/${data.tribeid}/${r}` );
	} )
	fs.outputJsonSync( `${config.tribes}/${data.tribeid}/clientconf.json`, data );
	const confcli = JSON.parse( Mustache.render( fs.readFileSync( `${config.mainDir}/setup/tribes/apixtribe/clientconf.mustache`, 'utf8' ), data ) );
	fs.outputJsonSync( `${config.tribes}/${data.tribeid}/clientconf.json`, confcli );

	return Pagans.createUser( {
		xpaganid: "setup",
		xworkon: data.tribeid,
		xlang: data.useradmin.xlang
	}, data.useradmin );
};
Tribes.archive = ( tribeid ) => {
	//A faire zip un repertoire tribeid dans
	// remove tribeid de data ou devdata
	try {
		fs.moveSync( `${config.tribes}/${tribeid}`, `${config.archivefolder}/${tribeid}` );
		//update apixtribeenv
		Tribes.init();
		return { status: 200, payload: { info: [ 'deletetribeidsuccessfull' ], models: 'Tribes', moreinfo: "TODO see in Tribes.archive" } }
	} catch ( err ) {
		logger.info( "Erreur d'archivage", err )
		return { status: 403, payload: { info: [ 'archiveerror' ], models: 'Tribes', moreinfo: err } }
	}
}
//////////////  Manage file for Tribes
Tribes.checkaccessfolder = ( folder, typeaccessrequested, useraccessrights, useruuid ) => {
	// check folder right



}

Tribes.checkaccessfiles = ( listfile, typeaccessrequested, useraccessrights, useruuid ) => {
	// @listfile to check accessright on file or folder
	// @typeaccessrequested on files R read or download, U for pdate, D for delete , O for owned  a Owner has all rights RUD on its files
	// @useraccessright from its account /userd/uuid.json
	// @useruuid public uuid user
	// return {'ok':[file auhtorized],'ko':[files not authorized]}

	const checkauthlistfile = { 'ok': [], 'ko': [] }
	let structf = []
	let inforep = { file: {}, dir: {} }
	let done;
	for( const f of listfile ) {
		done = false;
		if( !fs.existsSync( `${config.tribes}/${f}` ) ) {
			done = true;
			checkauthlistfile.ko.push( f )
			logger.info( `${f} file does not exist` )
		} else {
			structf = f.split( '/' );
		}
		//on ckeck tribeid existe / tribeid/object/
		if( !done &&
			useraccessrights.data[ structf[ 0 ] ] &&
			useraccessrights.data[ structf[ 0 ] ][ structf[ 1 ] ] &&
			useraccessrights.data[ structf[ 0 ] ][ structf[ 1 ] ].includes( typeaccessrequested ) ) {
			done = true;
			checkauthlistfile.ok.push( f );
		} else {
			// check if in folder we have a.info.json .file[f].shared{useruuid:'CRUDO'}
			logger.info( 'structf', structf )
			if( fs.existsSync( `${config.tribes}/${structf.slice(0,-1).join('/')}/.info.json` ) ) {
				inforep = fs.readJsonSync( `${config.tribes}/${structf.slice(0,-1).join('/')}/.info.json`, 'utf8' )
			}
			logger.info( `no accessrights for ${f} for ${useruuid} ` )
		}
		if( !done && inforep.file[ f ] && inforep.file[ f ] && inforep.file[ f ].shared && inforep.file[ f ].shared[ useruuid ] && inforep.file[ f ].shared[ useruuid ].includes( typeaccessrequested ) ) {
			done = true;
			checkauthlistfile.ok.push( f )
		}
		// If no authorization then ko
		if( !done ) {
			checkauthlistfile.ko.push( f )
		}
	} // end loop for
	//logger.info( 'checkauthlistfile', checkauthlistfile )
	return checkauthlistfile;
}

Tribes.dirls = ( tribeid, dir ) => {
	/*
	Return list of file into tribeid/dir
	*/
	let comment = { src: `${tribeid}/${dir}`, file: {}, dir: {} };
	if( fs.existsSync( `${config.tribes}/${tribeid}/${dir}/.info.json` ) ) {
		comment = fs.readJsonSync( `${config.tribes}/${tribeid}/${dir}/.info.json`, 'utf-8' );
	}
	const listfile = []
	const listdir = []
	glob.sync( `${config.tribes}/${tribeid}/${dir}/*` )
		.forEach( f => {
			//logger.info( f )
			const stats = fs.statSync( f );
			//	logger.info( stats )
			if( stats.isFile() ) {
				listfile.push( path.basename( f ) )
				if( !comment.file[ path.basename( f ) ] ) {
					comment.file[ path.basename( f ) ] = { tags: [], info: "", thumbb64: "" };
				}
				comment.file[ path.basename( f ) ].mtime = stats.mtime;
				comment.file[ path.basename( f ) ].ctime = stats.ctime;
				comment.file[ path.basename( f ) ].size = stats.size;
			}
			if( stats.isDirectory() ) {
				listdir.push( path.basename( f ) )
				if( !comment.dir[ path.basename( f ) ] ) {
					comment.dir[ path.basename( f ) ] = { tags: [], info: "", thumbb64: "" }
				}
				comment.dir[ path.basename( f ) ].nbfile = glob.sync( `${f}/*.*` )
					.length;
				comment.dir[ path.basename( f ) ].mtime = stats.mtime;
				comment.dir[ path.basename( f ) ].ctime = stats.mtime;
				logger.info( 'comment.dir', comment.dir )
			}
		} );
	// on remove les file or dir that was deleted
	Object.keys( comment.file )
		.forEach( f => {
			if( !listfile.includes( f ) ) delete comment.file[ f ]
		} )
	Object.keys( comment.dir )
		.forEach( d => {
			if( !listdir.includes( d ) ) delete comment.dir[ d ]
		} )
	//logger.info( comment )
	fs.outputJson( `${config.tribes}/${tribeid}/${dir}/.info.json`, comment, 'utf-8' );
	return { status: 200, payload: { info: [ 'succestogetls' ], models: 'Tribes', moreinfo: comment } }
};
Tribes.addspaceweb = ( data ) => {
	/*
  To create a public spaceweb accessible from https://dnsname/pageindex

	input:
	{dnsname:["archilinea.fr","www.archilinea.fr"], 1st is tha main dns other are just servername redirection
	tribeid:"archilinea", from req.session.header.xworkon
	website:"presentation",
	pageindex:"app_index_fr.html"
	mode:dev(local no ssl) | prod(IP + ssl)
}
  output:
	nginx conf and ssl to serve each https://dnsname to /{tribeid}/www/app/{website}


  Carefull this action is executed with root and restart nginx + apixtribe to work
	*/
	data.configdomain = config.tribes;
	data.porthttp = config.porthttp;
	console.assert( config.loglevel == "quiet", 'data to create spaceweb:', data );
	// create spaceweb app for tribeid/www/app/website/pageindexname.html
	if( !fs.existsSync( `${config.tribes}/${data.tribeid}/www/app/${data.website}` ) ) {
		fs.outputFileSync( `${config.tribes}/${data.tribeid}/www/app/${data.website}/${data.pageindex}`, `<h1>Hello ${data.tribeid} ${data.website} onto ${data.dnsname.join(',')}`, 'utf-8' )
	}
	//create logs folder
	fs.ensureDirSync( `${config.tribes}/${data.tribeid}/logs/nginx` );
	// add nginx http config
	const confnginx = fs.readFileSync( 'setup/nginx/modelwebsite.conf.mustache', 'utf-8' );
	fs.outputFileSync( `/etc/nginx/conf.d/${data.dnsname[0]}.conf`, Mustache.render( confnginx, data ), 'utf-8' );
	if( data.mode == "dev" ) {
		//add in /etc/hosts
		let hosts = fs.readFileSync( '/etc/hosts', 'utf8' );
		let chg = false;
		data.dnsname.forEach( d => {
			if( !hosts.includes( `127.0.0.1 ${d}` ) ) {
				hosts += `\n127.0.0.1 ${d}`;
				chg = true;
			}
			if( chg ) {
				fs.outputFileSync( '/etc/hosts', hosts, 'utf8' )
			}
		} );
	};
	//Ckeck dns respond
	data.dnsname.forEach( d => {
		if( !dnsSync.resolve( `${d}` ) ) {
			rep += `\nresolving ${d} will not responding valid IP, please setup domain redirection IP before runing this script`
		}
	} )
	//update clienconf.json
	const clientconf = fs.readJsonSync( `${config.tribes}/${data.tribeid}/clientconf.json` );
	clientconf.website[ data.website ] = data.dnsname[ 0 ];
	//merge allowedDOMs in unique concat
	clientconf.allowedDOMs = [ ...new Set( ...clientconf.allowedDOMs, ...data.dnsname ) ];
	fs.outputJsonSync( `${config.tribes}/${data.tribeid}/clientconf.json`, clientconf, 'utf-8' );
	if( !data.setup ) {
		// in setup apixtribe is not running and it will be start manually at the 1st run
		// in other case need to plan a restart for CORS
		setTimeout( Tribes.restartapixtribe, 300000, data.clienId );
	}
	const nginxrestart = execSync( `sudo systemctl restart nginx` )
		.toString();
	logger.info( 'Restart nginx', nginxrestart )
	if( data.mode == "prod" ) {
		// get ssl certificate ATTENTION il faut ajouter -d devant chaque domain qui redirige vers l'espace web.
		const certbot = execSync( `sudo certbot --nginx -d ${data.dnsname.join(' -d ')}` )
			.toString();
		logger.info( 'certbot is running A CHECKER POUR UNE VRAIE PROD ????', certbot )
	}
	//sh execution to update change requested
	return {
		status: 200,
		payload: {
			model: "Tribes",
			info: [ 'webspacecreated' ],
			moreinfo: "Space web well created"
		}
	};
}
Tribes.restartapixtribe = ( tribeid ) => {
	logger.info( 'A restarting was requested 5mn ago from a new spacedev for ' + tribeid )
	execSync( 'yarn restartpm2' );
}


module.exports = Tribes;
