"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const crypto = __importStar(require("crypto"));
const User_1 = require("../../shared/types/User");
const Errors_1 = require("../Errors");
const Functions_1 = require("../Functions");
const PasswordResetConfirmed_1 = __importDefault(require("../mail/PasswordResetConfirmed"));
const PasswordResetRequest_1 = __importDefault(require("../mail/PasswordResetRequest"));
const RegisterVerify_1 = __importDefault(require("../mail/RegisterVerify"));
function setRefreshHeaders(jwt, { req, res }) {
    var _a, _b;
    const JWT_REFRESH_EXPIRES = process.env.JWT_REFRESH_EXPIRES;
    const maxAge = Number.parseInt(JWT_REFRESH_EXPIRES);
    const secureOptions = `HttpOnly; Secure;`;
    const options = ((_a = req === null || req === void 0 ? void 0 : req.headers.host) === null || _a === void 0 ? void 0 : _a.indexOf("localhost")) === 0 ? "" : secureOptions;
    res.setHeader("Set-Cookie", `refreshToken=${jwt.refreshToken};${options} Max-Age=${maxAge}; Path=/;`);
    res.setHeader("Access-Control-Allow-Credentials", "true");
    if (((_b = req.headers.host) === null || _b === void 0 ? void 0 : _b.indexOf("localhost")) === 0) {
        res.setHeader("Access-Control-Allow-Origin", "http://localhost:3001");
    }
    delete jwt.refreshToken;
    return jwt;
}
/**
 *
 *
 * Our resolvers for this plugin
 */
exports.default = {
    Query: {
        userById: (_obj, args, { models, user }) => __awaiter(void 0, void 0, void 0, function* () {
            const currentUser = yield user();
            if (currentUser.id === args.id || Functions_1.checkUserHasRole(currentUser, User_1.roles.ADMIN)) {
                return yield models.userById(args.id);
            }
            return null;
        }),
        paginateUsers: (_obj, { size, page }, { models, user }) => __awaiter(void 0, void 0, void 0, function* () {
            Functions_1.checkUserHasRole(yield user(), User_1.roles.ADMIN);
            const offset = (page - 1) * size;
            const allUsers = yield models.allUsers();
            const items = allUsers.slice(offset, offset + size);
            const paginated = {
                currentPage: page,
                totalItems: allUsers.length,
                lastPage: Math.ceil(allUsers.length / size),
                perPage: size,
                items: items,
            };
            return new Promise((res) => res(paginated));
        }),
    },
    Mutation: {
        userEdit: (_obj, args, { models, user }) => __awaiter(void 0, void 0, void 0, function* () {
            const currentUser = yield user();
            if (args.id !== currentUser.id)
                Functions_1.checkUserHasRole(yield user(), User_1.roles.ADMIN);
            return models.userEdit(args.id, args.userInput);
        }),
        userUpdate: (_obj, args, { models, user }) => __awaiter(void 0, void 0, void 0, function* () {
            const currentUser = yield user();
            return models.userEdit(currentUser.id, args.userUpdateInput);
        }),
        userRegister: (_obj, args, { models, notify }) => __awaiter(void 0, void 0, void 0, function* () {
            Functions_1.validateAuthInput(args.data.email);
            Functions_1.validatePasswordCreate(args.data);
            if (yield models.userByEmail(args.data.email)) {
                throw new Errors_1.AccountExistsError();
            }
            const user = yield models.userRegister(args.data);
            user.verifyHash = yield Functions_1.generateVerifyHash(user, models.userEdit);
            yield notify.sendMail(new RegisterVerify_1.default(user));
            return user;
        }),
        userAuthenticate: (_obj, args, { models, eventEmitter, res, req }) => __awaiter(void 0, void 0, void 0, function* () {
            Functions_1.validateAuthInput(args.email);
            const jwt = yield models.userAuthenticate(args.email, args.password);
            eventEmitter === null || eventEmitter === void 0 ? void 0 : eventEmitter.emit("loggedIn", {
                user: args.email,
            });
            return setRefreshHeaders(jwt, { req, res });
        }),
        userTokenRefresh: (_obj, _args, { req, res, models }) => __awaiter(void 0, void 0, void 0, function* () {
            var _a;
            const rx = /([^;=\s]*)=([^;]*)/g;
            const obj = {};
            for (let m; (m = rx.exec((_a = req === null || req === void 0 ? void 0 : req.headers.cookie) !== null && _a !== void 0 ? _a : ""));) {
                obj[m[1]] = decodeURIComponent(m[2]);
            }
            const jwt = yield models.userTokenRefresh(obj["refreshToken"]);
            return setRefreshHeaders(jwt, { req, res });
        }),
        userVerify: (_obj, { email, token }, { models }) => __awaiter(void 0, void 0, void 0, function* () {
            const user = yield models.userByEmail(email);
            if (!user) {
                throw new Errors_1.InvalidEmailError();
            }
            if (user.verifyHash !== token) {
                throw new Errors_1.InvalidVerificationError();
            }
            return yield models.userEdit(user.id, { status: User_1.STATUS.VERIFIED });
        }),
        userCreate: (_obj, args, { models, user }) => __awaiter(void 0, void 0, void 0, function* () {
            Functions_1.checkUserHasRole(yield user(), User_1.roles.ADMIN);
            return models.userCreate(args.userCreateInput);
        }),
        userUpdatePassword: (_obj, args, { user, models, notify }) => __awaiter(void 0, void 0, void 0, function* () {
            const currentUser = yield user();
            const updatePassword = yield models.userUpdatePassword(currentUser, args.passwordInput);
            if (updatePassword === true) {
                yield notify.sendMail(new PasswordResetConfirmed_1.default(currentUser));
            }
            return updatePassword;
        }),
        /**
         * To prevent bots from scanning we dont indicate wether the account exists
         * or not but instead just return success.
         */
        userRequestPasswordReset: (_obj, { email }, { models, notify }) => __awaiter(void 0, void 0, void 0, function* () {
            const user = yield models.userByEmail(email);
            if (user) {
                const originalToken = crypto.randomBytes(20).toString("hex");
                const token = Buffer.from(JSON.stringify({
                    email,
                    token: originalToken,
                })).toString("base64");
                yield notify.sendMail(new PasswordResetRequest_1.default(user, token));
                yield models.userEdit(user.id, {
                    authReset: {
                        expires: "",
                        hash: crypto.createHash("sha512").update(originalToken).digest("hex"),
                    },
                });
            }
            return true;
        }),
        userResetPassword: (_obj, { token, resetInput }, { models, notify }) => __awaiter(void 0, void 0, void 0, function* () {
            var _b;
            const tokenResult = JSON.parse(Buffer.from(token, "base64").toString("ascii"));
            const user = yield models.userByEmail(tokenResult.email);
            if (user) {
                Functions_1.validatePasswordCreate(resetInput);
                //Compare the hash
                const hash = crypto.createHash("sha512").update(tokenResult.token).digest("hex");
                if (((_b = user.authReset) === null || _b === void 0 ? void 0 : _b.hash) !== hash) {
                    throw new Errors_1.InvalidVerificationError();
                }
                yield models.userUpdatePassword(user, resetInput);
                yield notify.sendMail(new PasswordResetConfirmed_1.default(user));
            }
            return true;
        }),
        me: (_obj, _args, { user }) => __awaiter(void 0, void 0, void 0, function* () {
            return yield user();
        }),
    },
};
