/*
@swagger
components:
schemas:
	Book:
		type: object
required:
	- title
	- author
	- finished
properties:
	id:
		type: integer
description: The auto-generated id of the book.
	title:
type: string
description: The title of your book.
	author:
type: string
description: Who wrote the book?
	finished:
	type: boolean
description: Have you finished reading it?
	createdAt:
	type: string
format: date
description: The date of the record creation.
	example:
title: The Pragmatic Programmer
author: Andy Hunt / Dave Thomas
finished: true
*/
let bcrypt = require('bcryptjs')
let multer = require('multer')
let express = require('express')
let router = express.Router()
const auth = require('../lib/auth')
var Recaptcha = require('express-recaptcha').RecaptchaV3;
var recaptcha = new Recaptcha('6LdlbrwZAAAAAKvtcVQhVl_QaNOqmQ4PgyW3SKHy', '6LdlbrwZAAAAAMAWPVDrL8eNPxrws6AMDtLf1bgd');
var reCAPTCHASecret = "6LdlbrwZAAAAAKvtcVQhVl_QaNOqmQ4PgyW3SKHy";
const Errors = require('../lib/errors.js')
var format = require('date-format');
let {
	User, Post, ProfilePicture, Item, userWall, StaffApplications, Inventory, AdminToken, PassKey, Thread, Category, Sequelize, Ip, Ban, sequelize
} = require('../models')
let pagination = require('../lib/pagination.js')
const sgMail = require('@sendgrid/mail');
const MailGen = require('mailgen')
const crypto = require("crypto")
const cryptoRandomString = require("crypto-random-string")
const rateLimit = require("express-rate-limit");

const emailLimiter = rateLimit({
	windowMs: 60000,
	max: 1, // limit each IP to 100 requests per windowMs
	message: "{\"errors\":[{\"name\":\"rateLimit\",\"message\":\"You may only make 1 request to this endpoint per minute.\",\"status\":429}]}"
});

function setUserSession(req, res, username, UserId, admin) {
	req.userData.loggedIn = true
	req.userData.username = username
	req.userData.UserId = UserId
	res.cookie('username', username)

	if(admin) { req.userData.admin = true }
}

router.post('/oidfhuisadhi8243', emailLimiter, async(req, res) => {
	try {
		await Ban.isIpBanned(req.ip)

		let userParams = {
			username: req.body.username,
			email: req.body.email,
			hash: req.body.password,
			passkey: req.body.passkey,
			admin: false,
			bodyColor: '#fffff',
			headColor: '#fffff',
			leftLegColor: '#fffff',
			rightLegColor: '#fffff',
			leftArmColor: '#fffff',
			rightArmColor: '#fffff',
			koins: '250',
			currency2: '0',
			picture: 'default',
			booster: false,
			theme: 'light',
			emailToken: crypto(16)
		}
		let user = await User.create(userParams)
		await Ip.createIfNotExists(req.ip, user)

		setUserSession(req, res, user.username, user.id, userParams.admin)
		res.json(user.toJSON())
	} catch (e) { next(e) }
})
router.post('/', emailLimiter, async(req, res, next) => {
	try {
		await Ban.isIpBanned(req.ip)

		let userParams = {
			username: req.body.username,
			email: req.body.email,
			hash: req.body.password,
			passkey: req.body.passkey,
			admin: false,
			bodyColor: '#ffffff',
			headColor: '#ffffff',
			leftLegColor: '#ffffff',
			rightLegColor: '#ffffff',
			leftArmColor: '#ffffff',
			rightArmColor: '#ffffff',
			koins: '250',
			currency2: '0',
			picture: 'default',
			developerMode: false,
			emailVerified: false,
			theme: 'light',
			emailToken: cryptoRandomString({length: 16})
		}
		let user = await User.create(userParams)
		await Ip.createIfNotExists(req.ip, user)

		setUserSession(req, res, user.username, user.id, userParams.admin)
		res.json(user.toJSON())
	} catch (e) { next(e) }
})
router.post('/job-application', auth, async(req, res, next) => {
	try {
		let userParams = {
			username: req.body.username,
			dob: req.body.dob,
			email: req.body.email,
			whyWork: req.body.whyWork,
			otherForm: req.body.otherForm,
			experience: req.body.experience,
			selectedOption: req.body.selectedOption,
		}
		await StaffApplications.submitApplication(userParams)
	} catch (e) { next(e) }
})
router.get('/:username', async(req, res, next) => {
	try {
		let queryObj = {
			attributes: {exclude: ['hash', 'email', 'emailVerified', 'koins', 'currency2', 'emailToken', 'passwordResetExpiry', 'passwordResetToken', 'experimentMode', 'developerMode', 'cookieOptOut', 'deleteCode', 'jwtOffset']},
			where: {username: req.params.username}
		}

		if (req.query.posts) {

			let {from, limit} = pagination.getPaginationProps(req.query, true)

			let postInclude = {
				model: Post,
				include: Post.includeOptions(),
				limit,
				order: [['id', 'DESC']]
			}
			if (from !== null) {
				postInclude.where = {id: {$lte: from}}
			}
			queryObj.include = [postInclude]

			let user = await User.findOne(queryObj)
			if (!user) throw Errors.accountDoesNotExist

			let meta = await user.getMeta(limit)

			res.json(Object.assign(user.toJSON(limit), {meta}))
		} else if (req.query.wall) {
			let {from, limit} = pagination.getPaginationProps(req.query, true)
			let postInclude = {
				model: userWall,
				include: userWall.includeOptions(),
				limit,
				order: [['id', 'DESC']],
			}
			if (from !== null) {
				postInclude.where = {id: {$lte: from}}
			}
			queryObj.include = [postInclude]

			let user = await User.findOne(queryObj)
			if (!user) throw Errors.accountDoesNotExist
			if (user.userWallOptOut) {
				throw Errors.userWallOptOut
			}

			res.json(Object.assign(user.toJSON(limit)))
		} else if (req.query.threads) {
			let queryString = ''

			Object.keys(req.query).forEach(query => {
				queryString += `&${query}=${req.query[query]}`
			})

			res.redirect('/api/v1/forums/category/ALL?username=' + req.params.username + queryString)
		} else if(req.query.marketplace) {
			let {from, limit} = pagination.getPaginationProps(req.query, true)
			let UserId = await User.findOne({
				where: {
					username: req.params.username
				}
			})
			if(!UserId) throw Errors.accountDoesNotExist
			let marketplace = await Item.findAll({
				where: {
					UserId: UserId.id
				}
			})
			let postInclude = {
				model: Item,
				include: { model: User, attributes: ['username', 'createdAt', 'id', 'color', 'picture', 'locked', 'admin', 'booster', 'executive', 'bot'] },
				limit,
				order: [['id', 'DESC']],
			}
			queryObj.include = [postInclude]

			let user = await User.findOne(queryObj)
			res.status(200)
			let meta = await user.getMeta(limit)

			res.json(Object.assign(user.toJSON(limit), {meta}))
		} else if(req.query.inventory) {
			let {from, limit} = pagination.getPaginationProps(req.query, true)
			let userLookup = await User.findOne({
				where: {
					username: req.params.username
				}
			})
			let marketplace = await Inventory.findAll({
				where: {
					UserId: userLookup.id
				}
			})
			let postInclude = {
				model: Inventory,
				include: { model: Item, include: { model: User, attributes: ['username', 'createdAt', 'id', 'color', 'picture', 'locked', 'admin', 'booster', 'executive', 'bot'] } },
				limit,
				order: [['id', 'DESC']],
			}
			queryObj.include = [postInclude]

			let user = await User.findOne(queryObj)
			res.status(200)
			let meta = await user.getMeta(limit)

			res.json(Object.assign(user.toJSON(limit), {meta}))
		} else {
			let user = await User.findOne(queryObj)
			if(!user) throw Errors.accountDoesNotExist

			res.json(user.toJSON())
		}


	} catch (err) { next(err) }
})

router.post('/login', async(req, res, next) => {
	try {
		await Ban.isIpBanned(req.ip, req.body.email)

		let user = await User.findOne({ where: {
			username: req.body.username
		}})
		if(user) {
			if(await user.comparePassword(req.body.password)) {
				await Ip.createIfNotExists(req.ip, user)

				setUserSession(req, res, user.username, user.id, user.admin)
				res.json({
					username: user.username,
					admin: user.admin,
					success: true
				})
			} else {
				res.status(401)
				res.json({
					errors: [Errors.invalidLoginCredentials]
				})
			}
		} else {
			res.status(401)
			res.json({
				errors: [Errors.invalidLoginCredentials]
			})
		}
	} catch (err) { next(err) }
})

router.post('/:username/logout', auth, async(req, res) => {
	req.userData.destroy(() => {
		res.clearCookie('username')
		res.clearCookie('admin')
		res.json({
			success: true
		})
	})
})

router.get('/:username/picture', async(req, res, next) => {
	try {
		let user = await User.findOne({
			where: {
				username: req.params.username
			}
		})
		if(!user) throw Errors.accountDoesNotExist

		let picture = await ProfilePicture.findOne({
			where: {
				UserId: user.id
			}
		})

		if(!picture) {
			res.status(404)
			res.end('')
		} else {
			res.writeHead(200, {
				'Content-Type': picture.mimetype,
				'Content-disposition': 'attachment;filename=profile',
				'Content-Length': picture.file.length
			})
			res.end(new Buffer(picture.file, 'binary'))
		}
	} catch (e) { next(e) }
})

router.get('/', async(req, res, next) => {
	try {
		let sortFields = {
			createdAt: 'X.id',
			username: 'X.username',
			threadCount: 'threadCount',
			postCount: 'postCount'
		};
		let offset = Number.isInteger(+req.query.offset) ? +req.query.offset : 0;
		let havingClause = '';
		if(req.query.role === 'admin') {
			havingClause = 'HAVING Users.admin = true';
		} else if(req.query.role === 'user') {
			havingClause = 'HAVING Users.admin = false';
		} else {
			havingClause = 'Having Users.hidden = false';
		}
		if(req.query.search) {
			//I.e. if there is not already a HAVING clause
			if(!havingClause.length) {
				havingClause = 'HAVING ';
			} else {
				havingClause += ' AND ';
			}
			havingClause += 'Users.username LIKE $search';
		}
		let sql = `	
			SELECT X.username, X.admin, X.picture, X.level, X.levelProgress, X.bot, X.booster, X.description, X.bodyColor, X.headColor, X.leftLegColor, X.rightLegColor, X.leftArmColor, X.rightArmColor, X.hidden, X.system, X.createdAt, X.contributor, X.postCount, COUNT(Threads.id) as threadCount	
			FROM (	
				SELECT Users.*, COUNT(Posts.id) as postCount	
				FROM Users	
				LEFT OUTER JOIN Posts	
				ON Users.id = Posts.UserId	
				GROUP BY Users.id	
				${havingClause}	
			) as X	
			LEFT OUTER JOIN threads	
			ON X.id = Threads.UserId	
			GROUP BY X.id	
			ORDER BY ${sortFields[req.query.sort] || 'X.id'} ${req.query.order === 'asc' ? 'DESC' : 'ASC'}	
			LIMIT 30	
			OFFSET ${offset}	
		`;
		let users = await sequelize.query(sql, {
			model: User,
			bind: { search: req.query.search + '%' }
		});
		res.json(users)
	} catch (e) { next(e) }
})

module.exports = router;
