
'use strict';

var async = require('async');
var validator = require('validator');
var db = require('../database');
var utils = require('../../public/src/utils');
var plugins = require('../plugins');
var analytics = require('../analytics');
var user = require('../user');
var meta = require('../meta');
var posts = require('../posts');
var privileges = require('../privileges');
var categories = require('../categories');

module.exports = function (Topics) {

	Topics.create = function (data, callback) {
		// This is an internal method, consider using Topics.post instead
		var timestamp = data.timestamp || Date.now();
		var topicData;

		async.waterfall([
			function (next) {
				Topics.resizeAndUploadThumb(data, next);
			},
			function (next) {
				db.incrObjectField('global', 'nextTid', next);
			},
			function (tid, next) {
				topicData = {
					'tid': tid,
					'uid': data.uid,
					'cid': data.cid,
					'mainPid': 0,
					'title': data.title,
					'slug': tid + '/' + (utils.slugify(data.title) || 'topic'),
					'timestamp': timestamp,
					'lastposttime': 0,
					'postcount': 0,
					'viewcount': 0,
					'locked': 0,
					'deleted': 0,
					'pinned': 0
				};

				if (data.thumb) {
					topicData.thumb = data.thumb;
				}

				plugins.fireHook('filter:topic.create', {topic: topicData, data: data}, next);
			},
			function (data, next) {
				topicData = data.topic;
				db.setObject('topic:' + topicData.tid, topicData, next);
			},
			function (next) {
				async.parallel([
					function (next) {
						db.sortedSetsAdd([
							'topics:tid',
							'cid:' + topicData.cid + ':tids',
							'cid:' + topicData.cid + ':uid:' + topicData.uid + ':tids'
						], timestamp, topicData.tid, next);
					},
					function (next) {
						categories.updateRecentTid(topicData.cid, topicData.tid, next);
					},
					function (next) {
						user.addTopicIdToUser(topicData.uid, topicData.tid, timestamp, next);
					},
					function (next) {
						db.incrObjectField('category:' + topicData.cid, 'topic_count', next);
					},
					function (next) {
						db.incrObjectField('global', 'topicCount', next);
					},
					function (next) {
						Topics.createTags(data.tags, topicData.tid, timestamp, next);
					}
				], next);
			},
			function (results, next) {
				plugins.fireHook('action:topic.save', topicData);
				next(null, topicData.tid);
			}
		], callback);
	};

	Topics.post = function (data, callback) {
		var uid = data.uid;
		var title = data.title ? data.title.trim() : data.title;
		data.tags = data.tags || [];

		async.waterfall([
			function (next) {
				check(title, meta.config.minimumTitleLength, meta.config.maximumTitleLength, 'title-too-short', 'title-too-long', next);
			},
			function (next) {
				check(data.tags, meta.config.minimumTagsPerTopic, meta.config.maximumTagsPerTopic, 'not-enough-tags', 'too-many-tags', next);
			},
			function (next) {
				if (data.content) {
					data.content = data.content.rtrim();
				}
				check(data.content, meta.config.minimumPostLength, meta.config.maximumPostLength, 'content-too-short', 'content-too-long', next);
			},
			function (next) {
				categories.exists(data.cid, next);
			},
			function (categoryExists, next) {
				if (!categoryExists) {
					return next(new Error('[[error:no-category]]'));
				}
				privileges.categories.can('topics:create', data.cid, data.uid, next);
			},
			function (canCreate, next) {
				if (!canCreate) {
					return next(new Error('[[error:no-privileges]]'));
				}
				guestHandleValid(data, next);
			},
			function (next) {
				user.isReadyToPost(data.uid, data.cid, next);
			},
			function (next) {
				plugins.fireHook('filter:topic.post', data, next);
			},
			function (filteredData, next) {
				data = filteredData;
				Topics.create(data, next);
			},
			function (tid, next) {
				var postData = data;
				postData.tid = tid;
				postData.ip = data.req ? data.req.ip : null;
				posts.create(postData, next);
			},
			function (postData, next) {
				onNewPost(postData, data, next);
			},
			function (postData, next) {
				async.parallel({
					postData: function (next) {
						next(null, postData);
					},
					settings: function (next) {
						user.getSettings(uid, function (err, settings) {
							if (err) {
								return next(err);
							}
							if (settings.followTopicsOnCreate) {
								Topics.follow(postData.tid, uid, next);
							} else {
								next();
							}
						});
					},
					topicData: function (next) {
						Topics.getTopicsByTids([postData.tid], uid, next);
					}
				}, next);
			},
			function (data, next) {
				if (!Array.isArray(data.topicData) || !data.topicData.length) {
					return next(new Error('[[error:no-topic]]'));
				}

				data.topicData = data.topicData[0];
				data.topicData.unreplied = 1;
				data.topicData.mainPost = data.postData;
				data.postData.index = 0;

				analytics.increment(['topics', 'topics:byCid:' + data.topicData.cid]);
				plugins.fireHook('action:topic.post', data.topicData);

				if (parseInt(uid, 10)) {
					user.notifications.sendTopicNotificationToFollowers(uid, data.topicData, data.postData);
				}

				next(null, {
					topicData: data.topicData,
					postData: data.postData
				});
			}
		], callback);
	};

	Topics.reply = function (data, callback) {
		var tid = data.tid;
		var uid = data.uid;
		var content = data.content;
		var postData;
		var cid;

		async.waterfall([
			function (next) {
				Topics.getTopicField(tid, 'cid', next);
			},
			function (_cid, next) {
				cid = _cid;
				async.parallel({
					topicData: async.apply(Topics.getTopicData, tid),
					canReply: async.apply(privileges.topics.can, 'topics:reply', tid, uid),
					isAdminOrMod: async.apply(privileges.categories.isAdminOrMod, cid, uid),
				}, next);
			},
			function (results, next) {
				if (!results.topicData) {
					return next(new Error('[[error:no-topic]]'));
				}

				if (parseInt(results.topicData.locked, 10) === 1 && !results.isAdminOrMod) {
					return next(new Error('[[error:topic-locked]]'));
				}

				if (parseInt(results.topicData.deleted, 10) === 1 && !results.isAdminOrMod) {
					return next(new Error('[[error:topic-deleted]]'));
				}

				if (!results.canReply) {
					return next(new Error('[[error:no-privileges]]'));
				}

				guestHandleValid(data, next);
			},
			function (next) {
				user.isReadyToPost(uid, cid, next);
			},
			function (next) {
				plugins.fireHook('filter:topic.reply', data, next);
			},
			function (filteredData, next) {
				content = filteredData.content || data.content;
				if (content) {
					content = content.rtrim();
				}

				check(content, meta.config.minimumPostLength, meta.config.maximumPostLength, 'content-too-short', 'content-too-long', next);
			},
			function (next) {
				posts.create({
					uid: uid,
					tid: tid,
					handle: data.handle,
					content: content,
					toPid: data.toPid,
					timestamp: data.timestamp,
					ip: data.req ? data.req.ip : null
				}, next);
			},
			function (_postData, next) {
				postData = _postData;
				onNewPost(postData, data, next);
			},
			function (postData, next) {
				user.getSettings(uid, next);
			},
			function (settings, next) {
				if (settings.followTopicsOnReply) {
					Topics.follow(postData.tid, uid);
				}

				if (parseInt(uid, 10)) {
					user.setUserField(uid, 'lastonline', Date.now());
				}

				Topics.notifyFollowers(postData, uid);
				analytics.increment(['posts', 'posts:byCid:' + cid]);
				plugins.fireHook('action:topic.reply', postData);

				next(null, postData);
			}
		], callback);
	};

	function onNewPost(postData, data, callback) {
		var tid = postData.tid;
		var uid = postData.uid;
		async.waterfall([
			function (next) {
				Topics.markAsUnreadForAll(tid, next);
			},
			function (next) {
				Topics.markAsRead([tid], uid, next);
			},
			function (markedRead, next) {
				async.parallel({
					userInfo: function (next) {
						posts.getUserInfoForPosts([postData.uid], uid, next);
					},
					topicInfo: function (next) {
						Topics.getTopicFields(tid, ['tid', 'title', 'slug', 'cid', 'postcount', 'mainPid'], next);
					},
					parents: function (next) {
						Topics.addParentPosts([postData], next);
					},
					content: function (next) {
						posts.parsePost(postData, next);
					}
				}, next);
			},
			function (results, next) {
				postData.user = results.userInfo[0];
				postData.topic = results.topicInfo;
				postData.index = parseInt(results.topicInfo.postcount, 10) - 1;

				// Username override for guests, if enabled
				if (parseInt(meta.config.allowGuestHandles, 10) === 1 && parseInt(postData.uid, 10) === 0 && data.handle) {
					postData.user.username = validator.escape(String(data.handle));
				}

				postData.votes = 0;
				postData.bookmarked = false;
				postData.display_edit_tools = true;
				postData.display_delete_tools = true;
				postData.display_moderator_tools = true;
				postData.display_move_tools = true;
				postData.selfPost = false;
				postData.timestampISO = utils.toISOString(postData.timestamp);
				postData.topic.title = validator.escape(String(postData.topic.title));

				next(null, postData);
			}
		], callback);
	}

	function check(item, min, max, minError, maxError, callback) {
		if (!item || item.length < parseInt(min, 10)) {
			return callback(new Error('[[error:' + minError + ', ' + min + ']]'));
		} else if (item.length > parseInt(max, 10)) {
			return callback(new Error('[[error:' + maxError + ', ' + max + ']]'));
		}
		callback();
	}

	function guestHandleValid(data, callback) {
		if (parseInt(meta.config.allowGuestHandles, 10) === 1 && parseInt(data.uid, 10) === 0 && data.handle) {
			if (data.handle.length > meta.config.maximumUsernameLength) {
				return callback(new Error('[[error:guest-handle-invalid]]'));
			}
			user.existsBySlug(utils.slugify(data.handle), function (err, exists) {
				if (err || exists) {
					return callback(err || new Error('[[error:username-taken]]'));
				}
				callback();
			});
			return;
		}
		callback();
	}

};
