'use strict';

const _ = require('lodash');
const nconf = require('nconf');
const db = require('../../database');
const user = require('../../user');
const categories = require('../../categories');
const analytics = require('../../analytics');
const plugins = require('../../plugins');
const translator = require('../../translator');
const meta = require('../../meta');
const activitypub = require('../../activitypub');
const helpers = require('../helpers');
const pagination = require('../../pagination');
const utils = require('../../utils');
const cache = require('../../cache');

const categoriesController = module.exports;

categoriesController.get = async function (req, res, next) {
	const [categoryData, parent, selectedData] = await Promise.all([
		categories.getCategories([req.params.category_id]),
		categories.getParents([req.params.category_id]),
		helpers.getSelectedCategory(req.params.category_id),
	]);

	const category = categoryData[0];
	if (!category) {
		return next();
	}

	category.parent = parent[0];

	const data = await plugins.hooks.fire('filter:admin.category.get', {
		req: req,
		res: res,
		category: category,
		customClasses: [],
	});
	data.category.name = translator.escape(String(data.category.name));
	data.category.description = translator.escape(String(data.category.description));

	res.render('admin/manage/category', {
		category: data.category,
		selectedCategory: selectedData.selectedCategory,
		customClasses: data.customClasses,
		postQueueEnabled: !!meta.config.postQueue,
	});
};

categoriesController.getAll = async function (req, res) {
	const rootCid = parseInt(req.query.cid, 10) || 0;
	const rootChildren = await categories.getAllCidsFromSet(`cid:${rootCid}:children`);
	async function getRootAndChildren() {
		const childCids = _.flatten(await Promise.all(rootChildren.map(categories.getChildrenCids)));
		return [rootCid].concat(rootChildren.concat(childCids));
	}

	// Categories list will be rendered on client side with recursion, etc.
	const cids = await getRootAndChildren();

	let rootParent = 0;
	if (rootCid) {
		rootParent = await categories.getCategoryField(rootCid, 'parentCid') || 0;
	}

	const fields = [
		'cid', 'name', 'nickname', 'icon', 'parentCid', 'disabled', 'link',
		'order', 'color', 'bgColor', 'backgroundImage', 'imageClass',
		'subCategoriesPerPage', 'description', 'descriptionParsed',
	];
	let categoriesData = await categories.getCategoriesFields(cids, fields);
	({ categories: categoriesData } = await plugins.hooks.fire('filter:admin.categories.get', { categories: categoriesData, fields: fields }));

	categoriesData = categoriesData.map((category) => {
		category.isLocal = utils.isNumber(category.cid);
		return category;
	});

	let tree = categories.getTree(categoriesData, rootParent);
	const cidsCount = rootCid && tree[0] ? tree[0].children.length : tree.length;

	const pageCount = Math.max(1, Math.ceil(cidsCount / meta.config.categoriesPerPage));
	const page = Math.min(parseInt(req.query.page, 10) || 1, pageCount);
	const start = Math.max(0, (page - 1) * meta.config.categoriesPerPage);
	const stop = start + meta.config.categoriesPerPage;

	function trim(c) {
		if (c.children) {
			c.subCategoriesLeft = Math.max(0, c.children.length - c.subCategoriesPerPage);
			c.hasMoreSubCategories = c.children.length > c.subCategoriesPerPage;
			c.showMorePage = Math.ceil(c.subCategoriesPerPage / meta.config.categoriesPerPage);
			c.children = c.children.slice(0, c.subCategoriesPerPage);
			c.children.forEach(c => trim(c));
		}
	}
	if (rootCid && tree[0] && Array.isArray(tree[0].children)) {
		tree[0].children = tree[0].children.slice(start, stop);
		tree[0].children.forEach(trim);
	} else {
		tree = tree.slice(start, stop);
		tree.forEach(trim);
	}

	let selectedCategory;
	if (rootCid) {
		selectedCategory = await categories.getCategoryData(rootCid);
	}
	const crumbs = await buildBreadcrumbs(selectedCategory, '/admin/manage/categories');
	res.render('admin/manage/categories', {
		categoriesTree: tree,
		selectedCategory: selectedCategory,
		breadcrumbs: crumbs,
		pagination: pagination.create(page, pageCount, req.query),
		categoriesPerPage: meta.config.categoriesPerPage,
		selectCategoryLabel: '[[admin/manage/categories:jump-to]]',
	});
};

async function buildBreadcrumbs(categoryData, url) {
	if (!categoryData) {
		return;
	}
	const breadcrumbs = [
		{
			text: categoryData.name,
			url: `${nconf.get('relative_path')}${url}?cid=${categoryData.cid}`,
			cid: categoryData.cid,
		},
	];
	const allCrumbs = await helpers.buildCategoryBreadcrumbs(categoryData.parentCid);
	const crumbs = allCrumbs.filter(c => c.cid);

	crumbs.forEach((c) => {
		c.url = `${url}?cid=${c.cid}`;
	});
	crumbs.unshift({
		text: '[[admin/manage/categories:top-level]]',
		url: url,
	});

	return crumbs.concat(breadcrumbs);
}

categoriesController.buildBreadCrumbs = buildBreadcrumbs;

categoriesController.getAnalytics = async function (req, res) {
	const [name, analyticsData, selectedData] = await Promise.all([
		categories.getCategoryField(req.params.category_id, 'name'),
		analytics.getCategoryAnalytics(req.params.category_id),
		helpers.getSelectedCategory(req.params.category_id),
	]);
	res.render('admin/manage/category-analytics', {
		name: name,
		analytics: analyticsData,
		selectedCategory: selectedData.selectedCategory,
	});
};

categoriesController.getFederation = async function (req, res) {
	const cid = req.params.category_id;
	let [_following, pending, followers, name, { selectedCategory }] = await Promise.all([
		db.getSortedSetMembers(`cid:${cid}:following`),
		db.getSortedSetMembers(`followRequests:cid.${cid}`),
		activitypub.notes.getCategoryFollowers(cid),
		categories.getCategoryField(cid, 'name'),
		helpers.getSelectedCategory(cid),
	]);

	const following = [..._following, ...pending].map(entry => ({
		id: entry,
		approved: !pending.includes(entry),
	}));

	await activitypub.actors.assert(followers);
	followers = await user.getUsersFields(followers, ['userslug', 'picture']);

	res.render('admin/manage/category-federation', {
		cid: cid,
		enabled: meta.config.activitypubEnabled,
		name,
		selectedCategory,
		following,
		followers,
	});
};

categoriesController.addRemote = async function (req, res) {
	let { handle, id } = req.body;
	if (handle && !id) {
		({ actorUri: id } = await activitypub.helpers.query(handle));
	}

	if (!id) {
		return res.sendStatus(404);
	}

	await activitypub.actors.assertGroup(id);
	const exists = await categories.exists(id);

	if (!exists) {
		return res.sendStatus(404);
	}

	const lastItem = await db.getSortedSetRevRangeWithScores('cid:0:children', 0, 0);
	const order = lastItem.length ? lastItem[0].score + 1 : 1;
	await Promise.all([
		db.sortedSetAdd('cid:0:children', order, id),
		categories.setCategoryField(id, 'order', order),
	]);
	cache.del('cid:0:children');
	cache.del('cid:0:children:all');

	res.sendStatus(200);
};

categoriesController.renameRemote = async (req, res) => {
	if (utils.isNumber(req.params.cid)) {
		return helpers.formatApiResponse(400, res);
	}

	const { name } = req.body;
	await categories.setCategoryField(req.params.cid, 'nickname', name);

	res.sendStatus(200);
};

categoriesController.removeRemote = async function (req, res) {
	if (utils.isNumber(req.params.cid)) {
		return helpers.formatApiResponse(400, res);
	}

	const parentCid = await categories.getCategoryField(req.params.cid, 'parentCid');
	await db.sortedSetRemove(`cid:${parentCid || 0}:children`, req.params.cid);
	await categories.clearParentCategoryCache(parentCid || 0);
	await categories.setCategoryField(req.params.cid, 'parentCid', 0);
	res.sendStatus(200);
};
