"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getHistoricoLogin = exports.getHistoricoLogins = exports.checkVerificationCode = exports.requestVerificationCode = exports.editPassword = exports.logout = exports.refreshToken = exports.login = exports.loginAdmin = void 0;
const express_validator_1 = require("express-validator");
const bcryptjs_1 = __importDefault(require("bcryptjs"));
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
const aws_1 = require("../../services/aws");
const pagination_1 = require("../../util/pagination");
const validate_url_1 = __importDefault(require("../../util/validate-url"));
const verification_code_1 = __importDefault(require("../../util/verification-code"));
const date_fns_1 = require("date-fns");
const postmark_1 = __importDefault(require("../../services/postmark"));
const token_1 = require("../../util/token");
const prisma_1 = require("../../database/prisma");
const loginAdmin = async (req, res, next) => {
    try {
        const { email, password } = req.body;
        const errors = (0, express_validator_1.validationResult)(req);
        const userAgent = req.useragent;
        if (!errors.isEmpty()) {
            const error = new Error(errors.array()[0].msg);
            error.statusCode = 422;
            throw error;
        }
        const user = await prisma_1.prisma.user.findFirst({
            where: { email: email, isAdmin: true },
            include: { dadosPessoais: true },
        });
        if (!user) {
            const error = new Error('Senha ou email invalido!');
            error.statusCode = 422;
            throw error;
        }
        if (user.status !== 'Activo') {
            const error = new Error('Usuario ' + user.status);
            error.statusCode = 422;
            throw error;
        }
        const isEqual = await bcryptjs_1.default.compare(password, user.password);
        if (!isEqual) {
            const error = new Error('Senha ou email invalido!');
            error.statusCode = 422;
            throw error;
        }
        const dadosPessoais = user.dadosPessoais;
        if (dadosPessoais === null || dadosPessoais === void 0 ? void 0 : dadosPessoais.fotoURL) {
            dadosPessoais.fotoURL = await (0, aws_1.getURL)(dadosPessoais.fotoURL);
        }
        const accessToken = (0, token_1.generateAccessToken)(user.id);
        const refreshToken = (0, token_1.generateRefreshToken)(user.id);
        await prisma_1.prisma.refreshToken.create({
            data: {
                userId: user.id,
                token: refreshToken,
                expiresIn: (0, date_fns_1.addDays)(new Date(), 7),
            },
        });
        res.cookie('refreshToken', refreshToken, {
            httpOnly: true,
            secure: process.env.NODE_ENV === 'production',
            sameSite: 'strict',
        });
        const device = {
            browser: userAgent === null || userAgent === void 0 ? void 0 : userAgent.browser,
            version: userAgent === null || userAgent === void 0 ? void 0 : userAgent.version,
            os: userAgent === null || userAgent === void 0 ? void 0 : userAgent.os,
            platform: userAgent === null || userAgent === void 0 ? void 0 : userAgent.platform,
            source: userAgent === null || userAgent === void 0 ? void 0 : userAgent.source,
            isMobile: userAgent === null || userAgent === void 0 ? void 0 : userAgent.isMobile,
            isDesktop: userAgent === null || userAgent === void 0 ? void 0 : userAgent.isDesktop,
            isBot: userAgent === null || userAgent === void 0 ? void 0 : userAgent.isBot,
        };
        const historicoLogin = await prisma_1.prisma.historicoLogin.create({
            data: {
                userId: user.id,
                action: 'Login',
                device,
            },
        });
        res.status(201).json({
            msg: `Logado com sucesso!`,
            token: accessToken,
            refreshToken,
            user,
            historicoLogin,
        });
    }
    catch (error) {
        next(error);
    }
};
exports.loginAdmin = loginAdmin;
const login = async (req, res, next) => {
    try {
        const { email, password } = req.body;
        const errors = (0, express_validator_1.validationResult)(req);
        const userAgent = req.useragent;
        if (!errors.isEmpty()) {
            const error = new Error(errors.array()[0].msg);
            error.statusCode = 422;
            throw error;
        }
        const user = await prisma_1.prisma.user.findFirst({
            where: { email: email, isAdmin: false },
            include: { dadosPessoais: true },
        });
        if (!user) {
            const error = new Error('Senha ou email invalido!');
            error.statusCode = 422;
            throw error;
        }
        if (user.status !== 'Activo') {
            const error = new Error('Usuario ' + user.status);
            error.statusCode = 422;
            throw error;
        }
        const isEqual = await bcryptjs_1.default.compare(password, user.password);
        if (!isEqual) {
            const error = new Error('Senha ou email invalido!');
            error.statusCode = 422;
            throw error;
        }
        const dadosPessoais = user.dadosPessoais;
        if (dadosPessoais === null || dadosPessoais === void 0 ? void 0 : dadosPessoais.fotoURL) {
            dadosPessoais.fotoURL = await (0, aws_1.getURL)(dadosPessoais.fotoURL);
        }
        const accessToken = (0, token_1.generateAccessToken)(user.id);
        const refreshToken = (0, token_1.generateRefreshToken)(user.id);
        await prisma_1.prisma.refreshToken.create({
            data: {
                userId: user.id,
                token: refreshToken,
                autoRenew: true,
                expiresIn: (0, date_fns_1.addDays)(new Date(), 7),
            },
        });
        res.cookie('refreshToken', refreshToken, {
            httpOnly: true,
            secure: process.env.NODE_ENV === 'production',
            sameSite: 'strict',
        });
        const device = {
            browser: userAgent === null || userAgent === void 0 ? void 0 : userAgent.browser,
            version: userAgent === null || userAgent === void 0 ? void 0 : userAgent.version,
            os: userAgent === null || userAgent === void 0 ? void 0 : userAgent.os,
            platform: userAgent === null || userAgent === void 0 ? void 0 : userAgent.platform,
            source: userAgent === null || userAgent === void 0 ? void 0 : userAgent.source,
            isMobile: userAgent === null || userAgent === void 0 ? void 0 : userAgent.isMobile,
            isDesktop: userAgent === null || userAgent === void 0 ? void 0 : userAgent.isDesktop,
            isBot: userAgent === null || userAgent === void 0 ? void 0 : userAgent.isBot,
        };
        const historicoLogin = await prisma_1.prisma.historicoLogin.create({
            data: {
                userId: user.id,
                action: 'Login',
                device,
            },
        });
        res.status(201).json({
            msg: `Logado com sucesso!`,
            token: accessToken,
            refreshToken,
            user,
            historicoLogin,
        });
    }
    catch (error) {
        next(error);
    }
};
exports.login = login;
const refreshToken = async (req, res, next) => {
    try {
        let refreshToken;
        // Extrai o cookie refreshToken
        const tokenFromCookie = req.cookies.refreshToken;
        if (tokenFromCookie)
            refreshToken = tokenFromCookie;
        // Extrai do body
        const tokenFromBody = req.body.refreshToken;
        if (tokenFromBody)
            refreshToken = tokenFromBody;
        // Valida se esta presnte no request
        if (!refreshToken) {
            const error = new Error('Refresh token em falta');
            error.statusCode = 401;
            throw error;
        }
        // Busca o refresh token armazenado na BD
        const stored = await prisma_1.prisma.refreshToken.findFirst({ where: { token: refreshToken } });
        if (!stored) {
            const error = new Error('Refresh token inválido');
            error.statusCode = 403;
            throw error;
        }
        if (!stored.userId) {
            const error = new Error('Refresh token inválido');
            error.statusCode = 403;
            throw error;
        }
        if ((0, date_fns_1.isAfter)(new Date(), stored.expiresIn) && stored.autoRenew) {
            // Se o refresh token tiver autoRenew ativado e estiver expirado, gera um novo refresh token
            refreshToken = (0, token_1.generateRefreshToken)(stored.userId);
            await prisma_1.prisma.refreshToken.update({
                where: { id: stored.id },
                data: {
                    token: refreshToken,
                    expiresIn: (0, date_fns_1.addDays)(new Date(), 7),
                },
            });
        }
        else if ((0, date_fns_1.isAfter)(new Date(), stored.expiresIn) && !stored.autoRenew) {
            // Se o refresh token estiver expirado e não tiver autoRenew, rejeita a requisição
            const error = new Error('Refresh token expirado');
            error.statusCode = 403;
            throw error;
        }
        // Decodifica a token
        const decodedToken = jsonwebtoken_1.default.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET);
        // Gera o accessToken
        const accessToken = (0, token_1.generateAccessToken)(decodedToken.userId);
        // Retorna os dados do Usuario
        const user = await prisma_1.prisma.user.findUnique({
            where: { id: decodedToken.userId },
            include: { dadosPessoais: true },
        });
        if (!user) {
            const error = new Error('Utilizador não encontrado!');
            error.statusCode = 401;
            throw error;
        }
        const dadosPessoais = user.dadosPessoais;
        if (dadosPessoais) {
            dadosPessoais.fotoURL = await (0, aws_1.getURL)(dadosPessoais.fotoURL);
        }
        if (user === null || user === void 0 ? void 0 : user.assinatura) {
            user.assinatura = await (0, aws_1.getURL)(user.assinatura);
        }
        if (user === null || user === void 0 ? void 0 : user.carimbo) {
            user.carimbo = await (0, aws_1.getURL)(user.carimbo);
        }
        res.status(200).json({
            msg: 'Sucesso',
            token: accessToken,
            refreshToken: refreshToken, // Retorna o mesmo refresh token para o cliente
            user,
        });
    }
    catch (err) {
        next(err);
    }
};
exports.refreshToken = refreshToken;
const logout = async (req, res, next) => {
    try {
        const token = req.cookies.refreshToken;
        await prisma_1.prisma.refreshToken.deleteMany({ where: { token } });
        res.clearCookie('refreshToken');
        res.status(204).json({
            msg: 'Sucesso',
        });
    }
    catch (err) {
        next(err);
    }
};
exports.logout = logout;
const editPassword = async (req, res, next) => {
    try {
        const { newPassword, oldPassword, codigo, email, 
        // In case is an admin editing
        adminId, } = req.body;
        const errors = (0, express_validator_1.validationResult)(req);
        if (!errors.isEmpty()) {
            const error = new Error(errors.array()[0].msg);
            error.statusCode = 422;
            throw error;
        }
        /*
          Se nao for o admin editando entao
          o campo oldPassword e obrigatorio
        */
        if (!Boolean(oldPassword) && !Boolean(codigo)) {
            const error = new Error('O campo oldPassword é obrigatório!');
            error.statusCode = 422;
            throw error;
        }
        if (Boolean(adminId)) {
            const admin = await prisma_1.prisma.user.findFirst({
                where: { id: Number(adminId), isAdmin: true },
            });
            if (!admin) {
                const error = new Error('Admin não encontrado!');
                error.statusCode = 422;
                throw error;
            }
        }
        let user = await prisma_1.prisma.user.findFirst({ where: { email } });
        if (!user) {
            const error = new Error('Utilizador não encontrado!');
            error.statusCode = 422;
            throw error;
        }
        const hashPassword = await bcryptjs_1.default.hash(newPassword, 12);
        if (Boolean(oldPassword)) {
            const isEqual = await bcryptjs_1.default.compare(oldPassword, user.password);
            if (!isEqual) {
                const error = new Error('Senha antiga errada!');
                error.statusCode = 422;
                throw error;
            }
        }
        if (Boolean(codigo)) {
            if (user.codigoVerificao !== +codigo) {
                const error = new Error('Código inválido!');
                error.statusCode = 422;
                throw error;
            }
            // if (isAfter(new Date(), new Date(user.dataCV))) {
            //   const error: IError = new Error('Código expirado!');
            //   error.statusCode = 422;
            //   throw error;
            // }
        }
        user = await prisma_1.prisma.user.update({
            where: { id: user.id },
            data: { password: hashPassword },
        });
        // await postmarkMail.sendEmail({
        //   To: user.email,
        //   From: process.env.SENDER_EMAIL!,
        //   Subject: 'Alteração de senha',
        //   HtmlBody: `<h1>A sua senha foi alterada com sucesso!</h1>
        //       <h3>Dados de Acesso</h3>
        //       <p>User: ${user.email}</p>
        //       <p>Senha: ${newPassword}</p>
        //       <p><b>OBS:</b>Após o login, altere a sua senha na plataforma!</p>
        //     `,
        // });
        res.status(201).json({
            msg: `Sucesso!`,
            user,
        });
    }
    catch (error) {
        next(error);
    }
};
exports.editPassword = editPassword;
// Metodo para solicitar código de verificacao
const requestVerificationCode = async (req, res, next) => {
    try {
        const { email, isAdmin } = req.body;
        const errors = (0, express_validator_1.validationResult)(req);
        const codigo = (0, verification_code_1.default)(6);
        if (!errors.isEmpty()) {
            const error = new Error(errors.array()[0].msg);
            error.statusCode = 422;
            throw error;
        }
        let user = await prisma_1.prisma.user.findFirst({
            where: { email: email, isAdmin: Boolean(isAdmin) ? isAdmin : false },
        });
        if (!user) {
            const error = new Error('Utilizador não encontrado!');
            error.statusCode = 422;
            throw error;
        }
        // Date de expiracao do codigo
        const date = new Date();
        const dataCV = (0, date_fns_1.addMinutes)(date, 15);
        user = await prisma_1.prisma.user.update({
            where: { id: user.id },
            data: { codigoVerificao: codigo, dataCV },
        });
        await postmark_1.default.sendEmail({
            To: email,
            From: process.env.SENDER_EMAIL,
            Subject: 'Recuperação de senha',
            HtmlBody: `<h1>Código de verificação</h1>

          <p>
            O código de verificação enviado tem a duração de 15 minutos e não deve ser partilhado!
          </p>
  
          <h2>Código:</h2>
  
          <h3>${codigo}</h3>
  
          <p><i>Este e-mail foi gerado automaticamente pela plataforma <b>novocredito</b></i></p>
        `,
            MessageStream: 'outbound',
        });
        res.status(201).json({
            msg: `Sucesso!`,
            user,
        });
    }
    catch (error) {
        next(error);
    }
};
exports.requestVerificationCode = requestVerificationCode;
// Metodo para validar código de verificacao
const checkVerificationCode = async (req, res, next) => {
    try {
        const { email, codigo } = req.body;
        const errors = (0, express_validator_1.validationResult)(req);
        const date = new Date();
        if (!errors.isEmpty()) {
            const error = new Error(errors.array()[0].msg);
            error.statusCode = 422;
            throw error;
        }
        const user = await prisma_1.prisma.user.findFirst({
            where: { email: email },
            include: { dadosPessoais: true },
        });
        if (!user) {
            const error = new Error('Utilizador não encontrado!');
            error.statusCode = 422;
            throw error;
        }
        if (user) {
            if (user.codigoVerificao !== +codigo) {
                const error = new Error('Código inválido!');
                error.statusCode = 422;
                throw error;
            }
            if (!user.dataCV || (0, date_fns_1.isAfter)(date, new Date(user.dataCV))) {
                const error = new Error('Código expirado!');
                error.statusCode = 422;
                throw error;
            }
        }
        const dadosPessoais = user.dadosPessoais;
        if (dadosPessoais) {
            dadosPessoais.fotoURL = await (0, aws_1.getURL)(dadosPessoais.fotoURL);
        }
        // Salva o email como verificado.
        await prisma_1.prisma.user.update({
            where: { id: user.id },
            data: { emailVerified: true },
        });
        res.status(201).json({
            msg: `Sucesso!`,
            user,
        });
    }
    catch (error) {
        next(error);
    }
};
exports.checkVerificationCode = checkVerificationCode;
// Metodo para pegar os historicos de login dos admin
const getHistoricoLogins = async (req, res, next) => {
    try {
        let { userId, action, day, month, year } = req.query;
        const page = (0, pagination_1.getPage)(req.query.page);
        let paramPerPage = req.query.perPage;
        let perPage = 50;
        if (paramPerPage) {
            perPage = +paramPerPage;
        }
        let filterObject = {};
        if (Boolean(userId)) {
            filterObject.userId = Number(userId);
        }
        if (Boolean(action)) {
            const allowedActions = ['Login', 'Logout'];
            if (!allowedActions.includes(action)) {
                const error = new Error('Action inválida!');
                error.statusCode = 422;
                throw error;
            }
            filterObject.action = action;
        }
        if (Boolean(day)) {
            const date = new Date(day);
            const nextDate = new Date(`${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate() + 1}`);
            filterObject.createdAt = { gte: date, lte: nextDate };
        }
        const total = await prisma_1.prisma.historicoLogin.count({ where: filterObject });
        const historicoLogins = await prisma_1.prisma.historicoLogin.findMany({
            where: filterObject,
            take: perPage,
            skip: (0, pagination_1.skip)(page, perPage),
            orderBy: { createdAt: 'desc' },
            include: { user: { include: { dadosPessoais: true } } },
        });
        for (const historico of historicoLogins) {
            const user = historico === null || historico === void 0 ? void 0 : historico.user;
            const dadosPessoais = user === null || user === void 0 ? void 0 : user.dadosPessoais;
            if (dadosPessoais && !(0, validate_url_1.default)(dadosPessoais === null || dadosPessoais === void 0 ? void 0 : dadosPessoais.fotoURL)) {
                dadosPessoais.fotoURL = await (0, aws_1.getURL)(dadosPessoais.fotoURL);
            }
        }
        res.status(200).json({
            msg: 'Sucesso',
            historicoLogins,
            ...(0, pagination_1.paginationResult)(total, page, perPage),
        });
    }
    catch (err) {
        next(err);
    }
};
exports.getHistoricoLogins = getHistoricoLogins;
const getHistoricoLogin = async (req, res, next) => {
    try {
        let { id } = req.params;
        const historicoLogin = await prisma_1.prisma.historicoLogin.findUnique({
            where: { id: Number(id) },
            include: { user: { include: { dadosPessoais: true } } },
        });
        const user = historicoLogin === null || historicoLogin === void 0 ? void 0 : historicoLogin.user;
        const dadosPessoais = user === null || user === void 0 ? void 0 : user.dadosPessoais;
        if (dadosPessoais && !(0, validate_url_1.default)(dadosPessoais === null || dadosPessoais === void 0 ? void 0 : dadosPessoais.fotoURL)) {
            dadosPessoais.fotoURL = await (0, aws_1.getURL)(dadosPessoais.fotoURL);
        }
        res.status(200).json({
            msg: 'Sucesso',
            historicoLogin,
        });
    }
    catch (err) {
        next(err);
    }
};
exports.getHistoricoLogin = getHistoricoLogin;
