"use strict";
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 jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
const models_1 = require("../api/users/models");
const models_2 = require("../api/admin/models");
const helpers_1 = __importDefault(require("./helpers"));
function authorizeUser(req, res, next) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const { authorization } = req.headers;
            if (!authorization) {
                return res.status(401).json({ message: 'authorization failed' });
            }
            if (!authorization.startsWith('Bearer ')) {
                return res.status(401).json({ message: 'authorization failed' });
            }
            const token = authorization.substring('Bearer '.length);
            let payload;
            try {
                payload = jsonwebtoken_1.default.verify(token, process.env.JWT_SECRET);
            }
            catch (error) {
                // check if tokenHash exist in users devices, if yes then remove it
                try {
                    const payload = jsonwebtoken_1.default.decode(token);
                    if (typeof payload !== 'object' || !(payload === null || payload === void 0 ? void 0 : payload._id)) {
                        return;
                    }
                    const user = yield models_1.User.findById(payload._id);
                    if (!user) {
                        return;
                    }
                    const tokenHash = helpers_1.default.getHash(token);
                    const deviceIndex = user.auth.loggedinDevices.findIndex(e => e.tokenHash === tokenHash);
                    if (deviceIndex !== -1) {
                        return;
                    }
                    user.auth.loggedinDevices.splice(deviceIndex, 1);
                    yield user.save();
                }
                catch (error) {
                    console.log(error);
                }
                return res.status(401).json({ message: 'authorization failed' });
            }
            const { _id, hash } = payload;
            const user = yield models_1.User.findById(_id);
            if (!user) {
                return res.status(401).json({ message: 'authorization failed' });
            }
            if (user.deactivatedAt) {
                return res.status(403).json({ message: 'Account is currently scheduled for deletion, It will be automatically deleted within 7 days' });
            }
            if (user.auth.password.slice(-10) !== hash) {
                return res.status(401).json({ message: 'authorization failed' });
            }
            if (!user.auth.emailVerified) {
                return res.status(403).json({ message: 'Please verify your email first', verificationRequired: true });
            }
            if (user.terminated) {
                return res.status(403).json({ message: 'Your account has been terminated due to a violation of our Terms and Conditions and in accordance with our Privacy Policy.', terminated: true });
            }
            const tokenHash = helpers_1.default.getHash(token);
            const device = user.auth.loggedinDevices.find(e => e.tokenHash === tokenHash);
            if (!device) {
                return res.status(401).json({ message: 'authorization failed' });
            }
            // update device lastAccessedAt
            try {
                device.lastAccessedAt = new Date();
                yield user.save();
            }
            catch (error) {
                console.log(error);
            }
            res.locals.device = device;
            res.locals.user = user;
            return next();
        }
        catch (error) {
            console.log(error);
            return res.status(500).json({ message: 'server error' });
        }
    });
}
function optionalAuthorizeUser(req, res, next) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const { authorization } = req.headers;
            if (!authorization) {
                return next();
            }
            if (!authorization.startsWith('Bearer ')) {
                return next();
            }
            const token = authorization.substring('Bearer '.length);
            let payload;
            try {
                payload = jsonwebtoken_1.default.verify(token, process.env.JWT_SECRET);
            }
            catch (error) {
                // check if tokenHash exist in users devices, if yes then remove it
                try {
                    const payload = jsonwebtoken_1.default.decode(token);
                    if (typeof payload !== 'object' || !(payload === null || payload === void 0 ? void 0 : payload._id)) {
                        return next();
                    }
                    const user = yield models_1.User.findById(payload._id);
                    if (!user) {
                        return next();
                    }
                    const tokenHash = helpers_1.default.getHash(token);
                    const deviceIndex = user.auth.loggedinDevices.findIndex(e => e.tokenHash === tokenHash);
                    if (deviceIndex !== -1) {
                        return next();
                    }
                    user.auth.loggedinDevices.splice(deviceIndex, 1);
                    yield user.save();
                }
                catch (error) {
                    console.log(error);
                }
                return next();
            }
            const { _id, hash } = payload;
            const user = yield models_1.User.findById(_id);
            if (!user) {
                return next();
            }
            if (user.deactivatedAt) {
                return next();
            }
            if (user.auth.password.slice(-10) !== hash) {
                return next();
            }
            if (!user.auth.emailVerified) {
                return next();
            }
            if (user.terminated) {
                return next();
            }
            const tokenHash = helpers_1.default.getHash(token);
            const device = user.auth.loggedinDevices.find(e => e.tokenHash === tokenHash);
            if (!device) {
                return next();
            }
            // update device lastAccessedAt
            try {
                device.lastAccessedAt = new Date();
                yield user.save();
            }
            catch (error) {
                console.log(error);
            }
            res.locals.device = device;
            res.locals.user = user;
            return next();
        }
        catch (error) {
            console.log(error);
            return res.status(500).json({ message: 'server error' });
        }
    });
}
function authorizeAdmin(req, res, next) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const { authorization } = req.headers;
            if (!authorization) {
                return res.status(401).json({ message: 'authorization failed' });
            }
            if (!authorization.startsWith('Bearer ')) {
                return res.status(401).json({ message: 'authorization failed' });
            }
            const token = authorization.substring('Bearer '.length);
            const { _id, hash } = jsonwebtoken_1.default.verify(token, process.env.JWT_SECRET);
            const admin = yield models_2.Admin.findById(_id);
            if (!admin) {
                return res.status(401).json({ message: 'authorization failed' });
            }
            if (admin.auth.password.slice(-10) !== hash) {
                return res.status(401).json({ message: 'authorization failed' });
            }
            res.locals.admin = admin;
            return next();
        }
        catch (error) {
            console.log(error);
            return res.status(401).json({ message: 'authorization failed' });
        }
    });
}
const auth = {
    authorizeUser,
    optionalAuthorizeUser,
    authorizeAdmin
};
exports.default = auth;
