'use strict';

const path = require('path');
const nconf = require('nconf');

nconf.argv().env({
	separator: '__',
});
const winston = require('winston');
const { fork } = require('child_process');

const { env } = process;
let worker;

env.NODE_ENV = env.NODE_ENV || 'development';

const configFile = path.resolve(__dirname, nconf.any(['config', 'CONFIG']) || 'config.json');
const prestart = require('./src/prestart');

prestart.loadConfig(configFile);

const db = require('./src/database');

module.exports = function (grunt) {
	const args = [];

	if (!grunt.option('verbose')) {
		args.push('--log-level=info');
		nconf.set('log-level', 'info');
	}
	prestart.setupWinston();

	grunt.initConfig({
		watch: {},
	});

	grunt.loadNpmTasks('grunt-contrib-watch');

	grunt.registerTask('default', ['watch']);

	grunt.registerTask('init', async function () {
		const done = this.async();
		let plugins = [];
		if (!process.argv.includes('--core')) {
			await db.init();
			plugins = await db.getSortedSetRange('plugins:active', 0, -1);
			addBaseThemes(plugins);
			if (!plugins.includes('nodebb-plugin-composer-default')) {
				plugins.push('nodebb-plugin-composer-default');
			}
			if (!plugins.includes('nodebb-theme-persona')) {
				plugins.push('nodebb-theme-persona');
			}
		}

		const styleUpdated_Client = plugins.map(p => `node_modules/${p}/*.less`)
			.concat(plugins.map(p => `node_modules/${p}/*.css`))
			.concat(plugins.map(p => `node_modules/${p}/+(public|static|less)/**/*.less`))
			.concat(plugins.map(p => `node_modules/${p}/+(public|static)/**/*.css`));

		const styleUpdated_Admin = plugins.map(p => `node_modules/${p}/*.less`)
			.concat(plugins.map(p => `node_modules/${p}/*.css`))
			.concat(plugins.map(p => `node_modules/${p}/+(public|static|less)/**/*.less`))
			.concat(plugins.map(p => `node_modules/${p}/+(public|static)/**/*.css`));

		const clientUpdated = plugins.map(p => `node_modules/${p}/+(public|static)/**/*.js`);
		const serverUpdated = plugins.map(p => `node_modules/${p}/*.js`)
			.concat(plugins.map(p => `node_modules/${p}/+(lib|src)/**/*.js`));

		const templatesUpdated = plugins.map(p => `node_modules/${p}/+(public|static|templates)/**/*.tpl`);
		const langUpdated = plugins.map(p => `node_modules/${p}/+(public|static|languages)/**/*.json`);

		grunt.config(['watch'], {
			styleUpdated_Client: {
				files: [
					'public/less/**/*.less',
					...styleUpdated_Client,
				],
				options: {
					interval: 1000,
				},
			},
			styleUpdated_Admin: {
				files: [
					'public/less/**/*.less',
					...styleUpdated_Admin,
				],
				options: {
					interval: 1000,
				},
			},
			clientUpdated: {
				files: [
					'public/src/**/*.js',
					'public/vendor/**/*.js',
					...clientUpdated,
					'node_modules/benchpressjs/build/benchpress.js',
				],
				options: {
					interval: 1000,
				},
			},
			serverUpdated: {
				files: [
					'app.js',
					'install/*.js',
					'src/**/*.js',
					'public/src/modules/translator.js',
					'public/src/modules/helpers.js',
					'public/src/utils.js',
					serverUpdated,
					'!src/upgrades/**',
				],
				options: {
					interval: 1000,
				},
			},
			templatesUpdated: {
				files: [
					'src/views/**/*.tpl',
					...templatesUpdated,
				],
				options: {
					interval: 1000,
				},
			},
			langUpdated: {
				files: [
					'public/language/en-GB/*.json',
					'public/language/en-GB/**/*.json',
					...langUpdated,
				],
				options: {
					interval: 1000,
				},
			},
		});
		const build = require('./src/meta/build');
		if (!grunt.option('skip')) {
			await build.build(true);
		}
		run();
		done();
	});

	function run() {
		if (worker) {
			worker.kill();
		}

		const execArgv = [];
		const inspect = process.argv.find(a => a.startsWith('--inspect'));

		if (inspect) {
			execArgv.push(inspect);
		}

		worker = fork('app.js', args, {
			env,
			execArgv,
		});
	}

	grunt.task.run('init');

	grunt.event.removeAllListeners('watch');
	grunt.event.on('watch', (action, filepath, target) => {
		let compiling;
		if (target === 'styleUpdated_Client') {
			compiling = 'clientCSS';
		} else if (target === 'styleUpdated_Admin') {
			compiling = 'acpCSS';
		} else if (target === 'clientUpdated') {
			compiling = 'js';
		} else if (target === 'templatesUpdated') {
			compiling = 'tpl';
		} else if (target === 'langUpdated') {
			compiling = 'lang';
		} else if (target === 'serverUpdated') {
			// empty require cache
			const paths = ['./src/meta/build.js', './src/meta/index.js'];
			paths.forEach(p => delete require.cache[require.resolve(p)]);
			return run();
		}

		require('./src/meta/build').build([compiling], (err) => {
			if (err) {
				winston.error(err.stack);
			}
			if (worker) {
				worker.send({ compiling: compiling });
			}
		});
	});
};

function addBaseThemes(plugins) {
	let themeId = plugins.find(p => p.includes('nodebb-theme-'));
	if (!themeId) {
		return plugins;
	}
	let baseTheme;
	do {
		try {
			baseTheme = require(`${themeId}/theme`).baseTheme;
		} catch (err) {
			console.log(err);
		}

		if (baseTheme) {
			plugins.push(baseTheme);
			themeId = baseTheme;
		}
	} while (baseTheme);
	return plugins;
}
