/*
@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, teamWall, TeamInvite, TeamMemberRole, Transaction, teamPicture, userWall, StaffApplications, AdminToken, PassKey, Thread, Category, Sequelize, Ip, Ban, sequelize, Team, TeamMembers, TeamRoles
} = 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('/create', emailLimiter, auth, async(req, res, next) => {
    try {
        await Ban.isIpBanned(req.ip)
        let user = await User.findOne({where: {
                username: req.userData.username
            }})
        if(user.koins >= 300) {
            let userParams = {
                username: req.body.username,
                name: req.body.name,
                description: "This is the " + req.body.username + " team!",
                banned: false,
                banReason: "No reason provided",
                itemsOptOut: false,
                forumEnabled: false,
                teamWallOptOut: false,
                approved: false,
                picture: "default",
                OwnerId: req.userData.UserId
            }
            let team = await Team.create(userParams)
            let teamInfo = team.toJSON()
            console.log(teamInfo)
            let teamRoleMembers = {
                name: "Members",
                teamId: teamInfo.id,
                priority: 2
            }
            let teamRoleAdmins = {
                name: "Administrators",
                teamId: teamInfo.id,
                administrator: true,
                priority: 1
            }
            let memberRole = await TeamRoles.create(teamRoleMembers)
            let memberRoleInfo = memberRole.toJSON()
            let adminRole = await TeamRoles.create(teamRoleAdmins)
            let adminRoleInfo = adminRole.toJSON()
            let teamAddOwner = {
                userId: req.userData.UserId,
                teamId: teamInfo.id,
                roles: {"deprecated": "deprecated"}
            }
            let teamRoleOwner = {
                UserId: req.userData.UserId,
                TeamId: teamInfo.id,
                RoleId: adminRoleInfo.id,
                Role2Id: memberRoleInfo.id
            }
            await TeamMembers.create(teamAddOwner)
            await TeamMemberRole.create(teamRoleOwner)
            await Transaction.create({UserId: user.id, priceOfPurchase: 300, text: user.username + ' purchased a Kaverti Team for 300 koins', limited: false, ipId: Ip.createIfNotExists(req.ip, user)})
            res.json(team.toJSON())
        } else {
            throw Errors.insufficientKoins
        }
    } catch (e) { next(e) }
})

router.get('/view/:username', async(req, res, next) => {
    try {
        let queryObj = {
            attributes: {exclude: ['banReason', 'TeamRoleId']},
            where: {username: req.params.username}
        }
        let queryObjBanned = {
            attributes: {exclude: ['banReason', 'description', 'picture', 'forumEnabled', 'TeamRoleId', 'OwnerId']},
            where: {username: req.params.username}
        }
        if(req.query.wall) {
            let {from, limit} = pagination.getPaginationProps(req.query, true)
            let postInclude = {
                model: teamWall,
                include: teamWall.includeOptions(),
                limit,
                order: [['id', 'DESC']],
            }
            if (from !== null) {
                postInclude.where = {id: {$lte: from}}
            }
            queryObj.include = [postInclude]

            let user = await Team.findOne(queryObj)
            if (!user) throw Errors.accountDoesNotExist
            if(user.banned) {
                throw Errors.teamBanned
            }
            if (user.userWallOptOut) {
                throw Errors.userWallOptOut
            }
            res.json(Object.assign(user.toJSON(limit)))
        } else {
            let team = await Team.findOne(queryObj)
            if (!team) throw Errors.accountDoesNotExist
            if(!team.banned) {
                res.json(team.toJSON())
            } else {
                let team = await Team.findOne(queryObjBanned)
                res.json(team.toJSON())
            }
        }


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

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

        let picture = await teamPicture.findOne({
            where: {
                TeamId: user.id
            }
        })

        if(!picture) {
            res.status(404)
            res.json({picture: "https://cdn.kaverti.com/teams/unknown-light.png"})
        } else if(!user.banned) {
            res.writeHead(200, {
                'Content-Type': picture.mimetype,
                'Content-disposition': 'attachment;filename=kaverti-team-profile-picture',
                'Content-Length': picture.file.length
            })
            res.end(new Buffer.from(picture.file, 'binary'))
        } else {
            res.status(404)
            res.json({picture: "https://cdn.kaverti.com/teams/unknown-light.png"})
        }
    } catch (e) { next(e) }
})

router.get('/view/:username/members', async(req, res, next) => {
    try {
        let user = await Team.findOne({
            where: {
                username: req.params.username
            }
        })
        if(user) {
            if(user.banned) {
                res.status(200)
                res.json([])
            }
            let team = await TeamMembers.findAll({
                order: [['id', 'DESC']],
                where: {
                    TeamId: user.id
                },
                include: [
                    { model: User, attributes: ['username', 'createdAt', 'id', 'color', 'picture', 'description'] }
                ]
            })
            res.json(team)
        } else {
            throw Errors.accountDoesNotExist
        }
    } catch (e) { next(e) }
})

router.get('/view/:username/userRoles', async(req, res, next) => {
    try {
        let user = await Team.findOne({
            where: {
                username: req.params.username
            }
        })
        if(user) {
            if(user.banned) {
                res.status(200)
                res.json([])
            }
            let team = await TeamMemberRole.findAll({
                order: [['id', 'DESC']],
                where: {
                    TeamId: user.id
                },
                include: [
                    { model: User, attributes: ['username', 'createdAt', 'id', 'color', 'picture'] },
                    { model: TeamRoles, as: 'Role' },
                    { model: TeamRoles, as: 'Role2' },
                    { model: TeamRoles, as: 'Role3' },
                    { model: TeamRoles, as: 'Role4' },
                    { model: TeamRoles, as: 'Role5' },
                    { model: TeamRoles, as: 'Role6' },
                    { model: TeamRoles, as: 'Role7' },
                    { model: TeamRoles, as: 'Role8' },
                    { model: TeamRoles, as: 'Role9' },
                    { model: TeamRoles, as: 'Role10' },


                ]
            })
            res.json(team)
        } else {
            throw Errors.accountDoesNotExist
        }
    } catch (e) { next(e) }
})

router.get('/view/:username/roles', async(req, res, next) => {
    try {
        let user = await Team.findOne({
            where: {
                username: req.params.username
            }
        })
        if(user) {
            if(user.banned) {
                res.status(200)
                res.json([])
            }
            let team = await TeamRoles.findAll({
                order: [['id', 'DESC']],
                where: {
                    TeamId: user.id
                },
            })
            res.json(team)
        } else {
            throw Errors.accountDoesNotExist
        }
    } catch (e) { next(e) }
})

router.get('/view/:username/roles/id/:id', async(req, res, next) => {
    try {
        let user = await Team.findOne({
            where: {
                username: req.params.username
            }
        })
        if(user) {
            if(user.banned) {
                res.status(200)
                res.json([])
            }
            let team = await TeamMemberRole.findAll({
                order: [['id', 'DESC']],
                where: {
                    TeamId: user.id,
                    RoleId: req.params.id
                },
                include: { model: User, attributes: ['username', 'createdAt', 'id', 'color', 'picture'] },
            })
            res.json(team)
        } else {
            throw Errors.accountDoesNotExist
        }
    } catch (e) { next(e) }
})

router.get('/view/:username/roles/name/:id', async(req, res, next) => {
    try {
        let user = await Team.findOne({
            where: {
                username: req.params.username
            }
        })
        let userParam = await User.findOne({
            where: {
                username: req.params.id
            }
        })
        if(user && userParam) {
            if(user.banned) {
                res.status(200)
                res.json([])
            }
            let team = await TeamMemberRole.findAll({
                order: [['id', 'DESC']],
                where: {
                    TeamId: user.id,
                    UserID: userParam.id
                },
                include: [ { model: User, attributes: ['username', 'createdAt', 'id', 'color', 'picture'] }, { model: TeamRoles, as: 'Role' } ]
            })
            res.json(team)
        } else {
            throw Errors.accountDoesNotExist
        }
    } catch (e) { next(e) }
})

router.get('/', async(req, res, next) => {
    try {
        let sortFields = {
            createdAt: 'X.id',
            username: 'X.username',
        };
        let offset = Number.isInteger(+req.query.offset) ? +req.query.offset : 0;
        let havingClause = 'Having Teams.banned = false';

        if(req.query.search) {
            //I.e. if there is not already a HAVING clause
            if(!havingClause.length) {
                havingClause = 'HAVING ';
            } else {
                havingClause += ' AND ';
            }
            havingClause += 'Team.username LIKE $search';
        }
        let sql = `	
			SELECT X.username, X.approved, X.name, X.picture, X.id, X.forumEnabled, X.description, X.banned, X.createdAt, X.updatedAt	
			FROM (	
				SELECT Teams.*	
				FROM Teams	
				GROUP BY Teams.id	
				${havingClause}	
			) as X	
			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: Team,
            bind: { search: req.query.search + '%' }
        });
        res.json(users)
    } catch (e) { next(e) }
})

router.put('/join/:username', auth, async(req, res, next) => {
    try {
        await Ban.ReadOnlyMode(req.userData.UserId)
        let team = await Team.findOne({
            where: { username: req.params.username }
        })
        if(team) {
            if(team.banned) {
                throw Errors.teamBanned
            }
            if(team.inviteOnly) {
                throw Errors.inviteOnly
            }
            let queryObj3 = {
                where: {userId: req.userData.UserId, teamId: team.id},
            }
            let teamJoinTest = await TeamMembers.findOne(queryObj3)
            if(teamJoinTest) {
                throw Errors.joinedTeam
            }
            let role = await TeamRoles.findOne({
                where: {teamId: team.id, name: "Members"}
            })
            let join = {
                userId: req.userData.UserId,
                teamId: team.id,
                roles: {"deprecated": "deprecated"}
            }
            console.log(role)
            let roleUser = {
                UserId: req.userData.UserId,
                TeamId: team.id,
                RoleId: role.id
            }
            await TeamMembers.create(join)
            await TeamMemberRole.create(roleUser)
            res.status(200)
            res.json({success: true})
        } else {
            throw Errors.teamDoesNotExist
        }
    } catch (e) { next(e) }
})

router.get('/check/:username', auth, async(req, res, next) => {
    try {
        let team = await Team.findOne({
            where: {username: req.params.username}
        });
        if(team) {
            let queryObj3 = {
                where: {userId: req.userData.UserId, teamId: team.id},
            }
            if(team.banned) {
                res.status(200)
                res.json({success: false})
            }
            let teamJoinTest = await TeamMembers.findOne(queryObj3)
            if (teamJoinTest) {
                res.status(200)
                res.json({success: true})
            } else if (!teamJoinTest) {
                res.status(200)
                res.json({success: false})
            }
        } else {
            throw Errors.teamDoesNotExist
        }
    } catch (e) { next(e) }
})

router.put('/leave/:username', auth, async(req, res, next) => {
    try {
        await Ban.ReadOnlyMode(req.userData.UserId)
        let team = await Team.findOne({
            where: { username: req.params.username }
        })
        if(team) {
            if(team.banned) {
                throw Errors.teamBanned
            }
            let queryObj3 = {
                where: {userId: req.userData.UserId, teamId: team.id},
            }
            let teamLeaveTeam = await TeamMembers.findOne(queryObj3)
            if(teamLeaveTeam) {
                let queryObj4 = {
                    where: {UserId: req.userData.UserId, TeamId: team.id},
                }
                let teamLeaveRoles = await TeamMemberRole.findAll(queryObj4)
                await teamLeaveTeam.leaveTeam()
                teamLeaveRoles.forEach((TeamMemberRole) => TeamMemberRole.leaveTeam());
                res.status(200)
                res.json({success: true})
            } else {
                throw Errors.notInTeam
            }
        } else {
            throw Errors.teamDoesNotExist
        }
    } catch (e) { next(e) }
})
router.get('/invite/:username', async(req, res, next) => {
    try {
        let code = await TeamInvite.findOne({
            where: {code: req.params.username},
            include: {model: Team, attributes: { exclude: [ 'banReason' ]}}
        })
        if (code) {
            if(code.maxUses === 0) {
                res.status(200)
                res.json(code.toJSON())
            } else if(code.uses >= code.maxUses) {
                throw Errors.invalidInvite
            } else if(code.uses < code.maxUses) {
                res.status(200)
                res.json(code.toJSON())
            } else {
                throw Errors.invalidInvite
            }
        } else {
            throw Errors.invalidInvite
        }
    } catch (e) { next(e) }
})

router.post('/invite/:code', auth, async(req, res, next) => {
    try {
        await Ban.ReadOnlyMode(req.userData.UserId)
        let code = await TeamInvite.findOne({
            where: {code: req.params.code}
        })
        if (code) {
            let team = await Team.findOne({
                where: {id: code.TeamId}
            })
            if (team.banned) {
                throw Errors.teamBanned
            }
            let queryObj3 = {
                where: {userId: req.userData.UserId, teamId: team.id},
            }
            let teamJoinTest = await TeamMembers.findOne(queryObj3)
            if (teamJoinTest) {
                throw Errors.joinedTeam
            }
            if(code.maxUses >= code.uses) {
                throw Errors.inviteInvalid
            } else if(code.maxUses === 0) {
                let role = await TeamRoles.findOne({
                    where: {teamId: team.id, name: "Members"}
                })
                if(code.RoleId >= 0) {
                    let roleLookup = await TeamInvite.findOne({
                        where: {code: req.body.RoleId, TeamId: team.id}
                    })
                    if(roleLookup) {
                        let join = {
                            userId: req.userData.UserId,
                            teamId: team.id,
                            roles: {"deprecated": "deprecated"}
                        }
                        console.log(role)
                        let roleUser = {
                            UserId: req.userData.UserId,
                            TeamId: team.id,
                            RoleId: role.id,
                            Role2Id: roleLookup.id
                        }
                        await TeamMembers.create(join)
                        await TeamMemberRole.create(roleUser)
                        res.status(200)
                        res.json({success: true})
                    } else {
                        let join = {
                            userId: req.userData.UserId,
                            teamId: team.id,
                            roles: {"deprecated": "deprecated"}
                        }
                        console.log(role)
                        let roleUser = {
                            UserId: req.userData.UserId,
                            TeamId: team.id,
                            RoleId: role.id
                        }
                        await TeamMembers.create(join)
                        await TeamMemberRole.create(roleUser)
                        res.status(200)
                        res.json({success: true})
                    }
                } else {
                    let join = {
                        userId: req.userData.UserId,
                        teamId: team.id,
                        roles: {"deprecated": "deprecated"}
                    }
                    console.log(role)
                    let roleUser = {
                        UserId: req.userData.UserId,
                        TeamId: team.id,
                        RoleId: role.id
                    }
                    await TeamMembers.create(join)
                    await TeamMemberRole.create(roleUser)
                    res.status(200)
                    res.json({success: true})
                }
            } else if(code.uses < code.maxUses) {
                let role = await TeamRoles.findOne({
                    where: {teamId: team.id, name: "Members"}
                })
                let join = {
                    userId: req.userData.UserId,
                    teamId: team.id,
                    roles: {"deprecated": "deprecated"}
                }
                console.log(role)
                let roleUser = {
                    UserId: req.userData.UserId,
                    TeamId: team.id,
                    RoleId: role.id,
                }
                await TeamMembers.create(join)
                await TeamMemberRole.create(roleUser)
                res.status(200)
                res.json({success: true})
            } else {
                throw Errors.inviteInvalid
            }
        } else {
            throw Errors.inviteInvalid
        }
    } catch (e) { next(e) }
})

module.exports = router;
