var rTemplateNs = {};
var docPages = [];
var docContents = {};
var docCats = {};
var lastEdited = new Date().getTime();

function scanrTemplateNs(cont, gcmcont) {
	var query = {
		"action": "query",
		"format": "json",
		"prop": "revisions",
		"generator": "categorymembers",
		"formatversion": "2",
		"rvprop": "content",
		"rvslots": "main",
		"gcmtitle": "Category:Redirect templates",
		"gcmnamespace": "10",
		"gcmtype": "page",
		"gcmlimit": "50",
		"continue": cont || '',
		"gcmcontinue": gcmcont || ''
	}
	new mw.Api().get( query ).done( (result) => {
		result.query.pages.forEach( (e) => {
			if (e?.title && e.title.startsWith('Template:R ')) {
				var pageContent = e?.revisions?.[0]?.slots?.main?.content
				if (typeof pageContent === "string") {
					var nsMatches = Array.from(
						pageContent.matchAll(new RegExp("\\|\\s*(\\S*?) category\\s*=", "g")),
						(v) => (v[1])
					);
					if (nsMatches.length == 0) nsMatches = ['unknown'];
					rTemplateNs[e.title] = nsMatches;
				}
			}
		} );
		var keys = Object.keys(rTemplateNs);
		if (result["continue"]) {
			console.log("Got " + keys.length + " templates...")
			scanrTemplateNs(result["continue"]["continue"], result["continue"].gcmcontinue,);
		} else {
			console.log("Done " + keys.length + " templates!");
			keys.forEach( (e) => docPages.push(e + "/doc") );
			getDocContent();
		}
	} ).fail( (e) => console.warn(e) );
}

function getDocContent() {
	var limit = 50;
	if (docPages.length > 0) {
		titles = docPages.slice(0,limit).join('|');
		docPages = docPages.slice(limit)
		var query = {
			"action": "query",
			"format": "json",
			"prop": "revisions",
			"titles": titles,
			"formatversion": "2",
			"rvprop": "content",
			"rvslots": "main",
		}
		new mw.Api().get( query ).done( (result) => {
			result.query.pages.forEach( (e) => docContents[e.title.slice(0,-4)] = e?.revisions?.[0]?.slots?.main?.content );
			console.log("Got contents of " + Object.keys(docContents).length + " doc pages...");
			getDocContent();
		} ).fail( (e) => console.warn(e) );
	} else {
		console.log("Done with contents of " + Object.keys(docContents).length + " doc pages!");
		addCats();
	}
}

function addCats() {
	Object.keys(rTemplateNs).forEach( (e) => {
		if (docContents[e]) {
			docCats[e] = Array.from(
				docContents[e].matchAll(new RegExp("\\[\\[[Cc]ategory:(.*?) namespace redirect templates", "g")),
				(v) => (v[1].toLowerCase())
			);
			rTemplateNs[e].forEach( (f) => {
				if ( !docCats[e].includes(f) ) {
					var thisCat = '[[Category:' + f[0].toUpperCase() + f.substring(1) + ' namespace redirect templates]]'
					var re = /(Categories(?: go)? below this line.*?--\>)(.*?)((?:\s*\n*\s*\[\[[Cc]ategory\:.*?\]\])*)\n*/
					if (re.test(docContents[e])) {
						docContents[e] = docContents[e].replace(
							/(Categories(?: go)? below this line.*?--\>)(.*?)((?:\s*\n*\s*\[\[[Cc]ategory\:.*?\]\])*)\n*/, "$1$2$3\n"+thisCat
						).replace(
							new RegExp("\\n\\n" + thisCat), "\n"+thisCat
						);
						console.log(e + " added " + thisCat);
					} else {
						console.error("Category block not found in " + e);console.log(docContents[e]);
					}
				} else if (f == 'none') {
					console.warn("Template NS for " + e + " is 'none'");
				} else {
					console.warn(e + " already includes " + f)
				}
			} );
		} else {
			console.warn("Could not get docContents[" + e + "]");
		}
	} );
	writeDocs();
}

function makeEditFunc(page) {
	return function () {
		return $.Deferred(function (deferred) {
			var options = {
				"action": "edit",
				"format": "json",
				"title": page + "/doc",
				"text": docContents[page],
				"summary": "Add namespace-specific categories",
				"minor": 1,
				"nocreate": 1,
				"watchlist": "nochange",
			}
			console.log(new Date().getTime() + ": Editing " + options.title);
			lastEdited = new Date().getTime();
			var promise = new mw.Api().postWithEditToken(options);
			//var promise = $.when(() => {d = $.Deferred; return d.resolve().promise();}); //debug
			promise.done( () => console.log("Edited " + options.title) );
			promise.fail(function (code, obj) {
				console.warn(new Date().getTime() + ": Edit failed (" + obj.error.info + ").");
			});
			promise.always(function () {
				deferred.resolve();
			});
		});
	};
}


function delay() {
	return function() {
		return $.Deferred(function (deferred) {
			var interval = lastEdited + 667 - new Date().getTime();
			console.log(new Date().getTime() + ': Waiting ' + interval + 'ms...');
			setTimeout(function () {
				console.log(new Date().getTime() + ': Done waiting.');
				deferred.resolve();
			}, interval);
		});
	};
}

function writeDocs() {
	var articles = Object.keys(docContents);
	var deferred = makeEditFunc(articles[0])();
	for (var i = 1, len = articles.length; i < len; i++) {
		deferred = deferred.then(delay(len));
		deferred = deferred.then(makeEditFunc(articles[i]));
	}
	$.when(deferred).then( () => console.log("Edits done!") );
}

scanrTemplateNs();