'use strict';

const { SitemapStream, streamToPromise } = require('sitemap');
const nconf = require('nconf');

const db = require('./database');
const categories = require('./categories');
const topics = require('./topics');
const privileges = require('./privileges');
const meta = require('./meta');
const plugins = require('./plugins');
const utils = require('./utils');

const sitemap = module.exports;
sitemap.maps = {
	topics: [],
};

sitemap.render = async function () {
	const topicsPerPage = meta.config.sitemapTopics;
	const returnData = {
		url: nconf.get('url'),
		topics: [],
	};
	const [topicCount, categories, pages] = await Promise.all([
		db.getObjectField('global', 'topicCount'),
		getSitemapCategories(),
		getSitemapPages(),
	]);
	returnData.categories = categories.length > 0;
	returnData.pages = pages.length > 0;
	const numPages = Math.ceil(Math.max(0, topicCount / topicsPerPage));
	for (let x = 1; x <= numPages; x += 1) {
		returnData.topics.push(x);
	}

	return returnData;
};

async function getSitemapPages() {
	const urls = [{
		url: '',
		changefreq: 'weekly',
		priority: 0.6,
	}, {
		url: `${nconf.get('relative_path')}/recent`,
		changefreq: 'daily',
		priority: 0.4,
	}, {
		url: `${nconf.get('relative_path')}/users`,
		changefreq: 'daily',
		priority: 0.4,
	}, {
		url: `${nconf.get('relative_path')}/groups`,
		changefreq: 'daily',
		priority: 0.4,
	}];

	const data = await plugins.hooks.fire('filter:sitemap.getPages', { urls: urls });
	return data.urls;
}

sitemap.getPages = async function () {
	if (sitemap.maps.pages && Date.now() < sitemap.maps.pagesCacheExpireTimestamp) {
		return sitemap.maps.pages;
	}

	const urls = await getSitemapPages();
	if (!urls.length) {
		sitemap.maps.pages = '';
		sitemap.maps.pagesCacheExpireTimestamp = Date.now() + (1000 * 60 * 60 * 24);
		return sitemap.maps.pages;
	}

	sitemap.maps.pages = await urlsToSitemap(urls);
	sitemap.maps.pagesCacheExpireTimestamp = Date.now() + (1000 * 60 * 60 * 24);
	return sitemap.maps.pages;
};

async function getSitemapCategories() {
	const cids = await categories.getCidsByPrivilege('categories:cid', 0, 'find');
	return await categories.getCategoriesFields(cids, ['slug']);
}

sitemap.getCategories = async function () {
	if (sitemap.maps.categories && Date.now() < sitemap.maps.categoriesCacheExpireTimestamp) {
		return sitemap.maps.categories;
	}

	const categoryUrls = [];
	const categoriesData = await getSitemapCategories();
	categoriesData.forEach((category) => {
		if (category) {
			categoryUrls.push({
				url: `${nconf.get('relative_path')}/category/${category.slug}`,
				changefreq: 'weekly',
				priority: 0.4,
			});
		}
	});

	if (!categoryUrls.length) {
		sitemap.maps.categories = '';
		sitemap.maps.categoriesCacheExpireTimestamp = Date.now() + (1000 * 60 * 60 * 24);
		return sitemap.maps.categories;
	}

	sitemap.maps.categories = await urlsToSitemap(categoryUrls);
	sitemap.maps.categoriesCacheExpireTimestamp = Date.now() + (1000 * 60 * 60 * 24);
	return sitemap.maps.categories;
};

sitemap.getTopicPage = async function (page) {
	if (parseInt(page, 10) <= 0) {
		return;
	}

	const numTopics = meta.config.sitemapTopics;
	const start = (parseInt(page, 10) - 1) * numTopics;
	const stop = start + numTopics - 1;

	if (sitemap.maps.topics[page - 1] && Date.now() < sitemap.maps.topics[page - 1].cacheExpireTimestamp) {
		return sitemap.maps.topics[page - 1].sm;
	}

	const topicUrls = [];
	let tids = await db.getSortedSetRange('topics:tid', start, stop);
	tids = await privileges.topics.filterTids('topics:read', tids, 0);
	const topicData = await topics.getTopicsFields(tids, ['tid', 'title', 'slug', 'lastposttime']);

	if (!topicData.length) {
		sitemap.maps.topics[page - 1] = {
			sm: '',
			cacheExpireTimestamp: Date.now() + (1000 * 60 * 60 * 24),
		};
		return sitemap.maps.topics[page - 1].sm;
	}

	topicData.forEach((topic) => {
		if (topic) {
			topicUrls.push({
				url: `${nconf.get('relative_path')}/topic/${topic.slug}`,
				lastmodISO: utils.toISOString(topic.lastposttime),
				changefreq: 'daily',
				priority: 0.6,
			});
		}
	});

	sitemap.maps.topics[page - 1] = {
		sm: await urlsToSitemap(topicUrls),
		cacheExpireTimestamp: Date.now() + (1000 * 60 * 60 * 24),
	};

	return sitemap.maps.topics[page - 1].sm;
};

async function urlsToSitemap(urls) {
	if (!urls.length) {
		return '';
	}
	const smStream = new SitemapStream({ hostname: nconf.get('url') });
	urls.forEach(url => smStream.write(url));
	smStream.end();
	return (await streamToPromise(smStream)).toString();
}

sitemap.clearCache = function () {
	if (sitemap.maps.pages) {
		sitemap.maps.pagesCacheExpireTimestamp = 0;
	}
	if (sitemap.maps.categories) {
		sitemap.maps.categoriesCacheExpireTimestamp = 0;
	}
	sitemap.maps.topics.forEach((topicMap) => {
		topicMap.cacheExpireTimestamp = 0;
	});
};

require('./promisify')(sitemap);
