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());
    });
};
import * as crypto from "crypto";
import { roles, STATUS } from "../../shared/types/User";
import { AccountExistsError, InvalidEmailError, InvalidVerificationError } from "../Errors";
import { checkUserHasRole, generateVerifyHash, validateAuthInput, validatePasswordCreate } from "../Functions";
import PasswordResetConfirmed from "../mail/PasswordResetConfirmed";
import PasswordResetRequest from "../mail/PasswordResetRequest";
import RegisterVerifyMail from "../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
 */
export default {
    Query: {
        userById: (_obj, args, { models, user }) => __awaiter(void 0, void 0, void 0, function* () {
            const currentUser = yield user();
            if (currentUser.id === args.id || checkUserHasRole(currentUser, roles.ADMIN)) {
                return yield models.userById(args.id);
            }
            return null;
        }),
        paginateUsers: (_obj, { size, page }, { models, user }) => __awaiter(void 0, void 0, void 0, function* () {
            checkUserHasRole(yield user(), 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)
                checkUserHasRole(yield user(), 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* () {
            validateAuthInput(args.data.email);
            validatePasswordCreate(args.data);
            if (yield models.userByEmail(args.data.email)) {
                throw new AccountExistsError();
            }
            const user = yield models.userRegister(args.data);
            user.verifyHash = yield generateVerifyHash(user, models.userEdit);
            yield notify.sendMail(new RegisterVerifyMail(user));
            return user;
        }),
        userAuthenticate: (_obj, args, { models, eventEmitter, res, req }) => __awaiter(void 0, void 0, void 0, function* () {
            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 InvalidEmailError();
            }
            if (user.verifyHash !== token) {
                throw new InvalidVerificationError();
            }
            return yield models.userEdit(user.id, { status: STATUS.VERIFIED });
        }),
        userCreate: (_obj, args, { models, user }) => __awaiter(void 0, void 0, void 0, function* () {
            checkUserHasRole(yield user(), 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(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(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) {
                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 InvalidVerificationError();
                }
                yield models.userUpdatePassword(user, resetInput);
                yield notify.sendMail(new PasswordResetConfirmed(user));
            }
            return true;
        }),
        me: (_obj, _args, { user }) => __awaiter(void 0, void 0, void 0, function* () {
            return yield user();
        }),
    },
};
