"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.updateProfile = exports.logoutAllDevices = exports.logout = exports.getActiveSessions = exports.refreshTokenHandler = exports.googleLogin = exports.resetPassword = exports.forgotPassword = exports.verifyOtp = exports.sendVerificationOtp = exports.adminLogin = exports.login = exports.register = void 0;
const database_1 = __importDefault(require("../config/database"));
const hash_utils_1 = require("../utils/hash.utils");
const jwt_utils_1 = require("../utils/jwt.utils");
const response_utils_1 = require("../utils/response.utils");
const crypto_1 = __importDefault(require("crypto"));
// Utility to generate 6-digit numeric OTP
const generateNumericOtp = () => {
    return Math.floor(100000 + Math.random() * 900000).toString();
};
// 1. Customer Registration
const register = async (req, res) => {
    try {
        const { email, password, firstName, lastName, phone } = req.body;
        const existingUser = await database_1.default.user.findUnique({ where: { email } });
        if (existingUser) {
            return (0, response_utils_1.sendError)(res, 'Email is already registered', null, 400);
        }
        const hashedPassword = await (0, hash_utils_1.hashPassword)(password);
        const user = await database_1.default.user.create({
            data: {
                email,
                password: hashedPassword,
                firstName,
                lastName,
                phone,
                status: 'ACTIVE',
                isEmailVerified: false,
            },
        });
        // Create Email Verification OTP
        const otpCode = generateNumericOtp();
        await database_1.default.otpToken.create({
            data: {
                email,
                code: otpCode,
                purpose: 'EMAIL_VERIFICATION',
                expiresAt: new Date(Date.now() + 15 * 60 * 1000), // 15 mins
            },
        });
        // Create Active Session
        const session = await database_1.default.session.create({
            data: {
                userId: user.id,
                ipAddress: req.ip,
                userAgent: req.get('user-agent'),
            },
        });
        const tokenPayload = { userId: user.id, email: user.email, role: 'CUSTOMER', sessionId: session.id };
        const token = (0, jwt_utils_1.signAccessToken)(tokenPayload, false);
        const refreshToken = (0, jwt_utils_1.signRefreshToken)(tokenPayload, false);
        await database_1.default.refreshToken.create({
            data: {
                token: refreshToken,
                userId: user.id,
                ipAddress: req.ip,
                userAgent: req.get('user-agent'),
                expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
            },
        });
        return (0, response_utils_1.sendSuccess)(res, 'Registration successful. Please verify your email with the OTP sent.', {
            user: {
                id: user.id,
                email: user.email,
                firstName: user.firstName,
                lastName: user.lastName,
                role: 'CUSTOMER',
                isEmailVerified: user.isEmailVerified,
            },
            token,
            refreshToken,
            otpCode, // Returned for dev testing
        }, 201);
    }
    catch (error) {
        return (0, response_utils_1.sendError)(res, 'Error registering user', error.message, 500);
    }
};
exports.register = register;
// 2. Customer & General Login (with Remember Me)
const login = async (req, res) => {
    try {
        const { email, password, rememberMe } = req.body;
        const user = await database_1.default.user.findUnique({ where: { email } });
        if (!user) {
            return (0, response_utils_1.sendError)(res, 'Invalid email or password', null, 401);
        }
        if (user.status !== 'ACTIVE') {
            return (0, response_utils_1.sendError)(res, `Account is currently ${user.status.toLowerCase()}`, null, 403);
        }
        const isMatch = await (0, hash_utils_1.comparePassword)(password, user.password);
        if (!isMatch) {
            return (0, response_utils_1.sendError)(res, 'Invalid email or password', null, 401);
        }
        // Create session
        const session = await database_1.default.session.create({
            data: {
                userId: user.id,
                ipAddress: req.ip,
                userAgent: req.get('user-agent'),
            },
        });
        const tokenPayload = { userId: user.id, email: user.email, role: 'CUSTOMER', sessionId: session.id };
        const token = (0, jwt_utils_1.signAccessToken)(tokenPayload, rememberMe);
        const refreshToken = (0, jwt_utils_1.signRefreshToken)(tokenPayload, rememberMe);
        await database_1.default.refreshToken.create({
            data: {
                token: refreshToken,
                userId: user.id,
                ipAddress: req.ip,
                userAgent: req.get('user-agent'),
                expiresAt: new Date(Date.now() + (rememberMe ? 30 : 7) * 24 * 60 * 60 * 1000),
            },
        });
        return (0, response_utils_1.sendSuccess)(res, 'Login successful', {
            user: {
                id: user.id,
                email: user.email,
                firstName: user.firstName,
                lastName: user.lastName,
                role: 'CUSTOMER',
                isEmailVerified: user.isEmailVerified,
            },
            token,
            refreshToken,
        });
    }
    catch (error) {
        return (0, response_utils_1.sendError)(res, 'Error logging in', error.message, 500);
    }
};
exports.login = login;
// 3. Admin Login
const adminLogin = async (req, res) => {
    try {
        const { email, password } = req.body;
        const admin = await database_1.default.admin.findUnique({ where: { email } });
        if (!admin) {
            return (0, response_utils_1.sendError)(res, 'Invalid admin credentials', null, 401);
        }
        const isMatch = await (0, hash_utils_1.comparePassword)(password, admin.password);
        if (!isMatch) {
            return (0, response_utils_1.sendError)(res, 'Invalid admin credentials', null, 401);
        }
        const session = await database_1.default.session.create({
            data: {
                adminId: admin.id,
                ipAddress: req.ip,
                userAgent: req.get('user-agent'),
            },
        });
        const tokenPayload = { adminId: admin.id, email: admin.email, role: 'ADMIN', sessionId: session.id };
        const token = (0, jwt_utils_1.signAccessToken)(tokenPayload, false);
        const refreshToken = (0, jwt_utils_1.signRefreshToken)(tokenPayload, false);
        await database_1.default.refreshToken.create({
            data: {
                token: refreshToken,
                adminId: admin.id,
                ipAddress: req.ip,
                userAgent: req.get('user-agent'),
                expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
            },
        });
        return (0, response_utils_1.sendSuccess)(res, 'Admin authentication successful', {
            admin: {
                id: admin.id,
                email: admin.email,
                name: admin.name,
                role: admin.role,
            },
            token,
            refreshToken,
        });
    }
    catch (error) {
        return (0, response_utils_1.sendError)(res, 'Admin login failed', error.message, 500);
    }
};
exports.adminLogin = adminLogin;
// 4. Send Email Verification OTP
const sendVerificationOtp = async (req, res) => {
    try {
        const { email } = req.body;
        const user = await database_1.default.user.findUnique({ where: { email } });
        if (!user) {
            return (0, response_utils_1.sendError)(res, 'User email not found', null, 404);
        }
        if (user.isEmailVerified) {
            return (0, response_utils_1.sendError)(res, 'Email is already verified', null, 400);
        }
        const otpCode = generateNumericOtp();
        await database_1.default.otpToken.create({
            data: {
                email,
                code: otpCode,
                purpose: 'EMAIL_VERIFICATION',
                expiresAt: new Date(Date.now() + 15 * 60 * 1000),
            },
        });
        return (0, response_utils_1.sendSuccess)(res, 'Verification OTP sent successfully', { otpCode });
    }
    catch (error) {
        return (0, response_utils_1.sendError)(res, 'Error sending OTP', error.message, 500);
    }
};
exports.sendVerificationOtp = sendVerificationOtp;
// 5. Verify OTP (Email Verification or Password Reset)
const verifyOtp = async (req, res) => {
    try {
        const { email, code, purpose } = req.body;
        const otpRecord = await database_1.default.otpToken.findFirst({
            where: {
                email,
                code,
                purpose,
                isUsed: false,
                expiresAt: { gt: new Date() },
            },
            orderBy: { createdAt: 'desc' },
        });
        if (!otpRecord) {
            return (0, response_utils_1.sendError)(res, 'Invalid or expired OTP code', null, 400);
        }
        await database_1.default.otpToken.update({
            where: { id: otpRecord.id },
            data: { isUsed: true },
        });
        if (purpose === 'EMAIL_VERIFICATION') {
            await database_1.default.user.update({
                where: { email },
                data: { isEmailVerified: true },
            });
        }
        return (0, response_utils_1.sendSuccess)(res, 'OTP verified successfully', { email, purpose, verified: true });
    }
    catch (error) {
        return (0, response_utils_1.sendError)(res, 'OTP verification failed', error.message, 500);
    }
};
exports.verifyOtp = verifyOtp;
// 6. Forgot Password (Request Password Reset)
const forgotPassword = async (req, res) => {
    try {
        const { email } = req.body;
        const user = await database_1.default.user.findUnique({ where: { email } });
        if (!user) {
            // Return success for security (prevent email enumeration)
            return (0, response_utils_1.sendSuccess)(res, 'If account exists, password reset instructions have been generated.');
        }
        const otpCode = generateNumericOtp();
        const resetToken = crypto_1.default.randomBytes(32).toString('hex');
        await Promise.all([
            database_1.default.otpToken.create({
                data: {
                    email,
                    code: otpCode,
                    purpose: 'PASSWORD_RESET',
                    expiresAt: new Date(Date.now() + 15 * 60 * 1000),
                },
            }),
            database_1.default.passwordResetToken.create({
                data: {
                    email,
                    token: resetToken,
                    expiresAt: new Date(Date.now() + 60 * 60 * 1000), // 1 hour
                },
            }),
        ]);
        return (0, response_utils_1.sendSuccess)(res, 'Password reset OTP and token generated.', {
            otpCode,
            resetToken,
        });
    }
    catch (error) {
        return (0, response_utils_1.sendError)(res, 'Error processing forgot password', error.message, 500);
    }
};
exports.forgotPassword = forgotPassword;
// 7. Reset Password
const resetPassword = async (req, res) => {
    try {
        const { email, token, newPassword } = req.body;
        // Check if token matches reset token or OTP code
        const resetTokenRecord = await database_1.default.passwordResetToken.findFirst({
            where: { email, token, isUsed: false, expiresAt: { gt: new Date() } },
        });
        const otpRecord = !resetTokenRecord
            ? await database_1.default.otpToken.findFirst({
                where: { email, code: token, purpose: 'PASSWORD_RESET', isUsed: false, expiresAt: { gt: new Date() } },
            })
            : null;
        if (!resetTokenRecord && !otpRecord) {
            return (0, response_utils_1.sendError)(res, 'Invalid or expired password reset token / OTP', null, 400);
        }
        if (resetTokenRecord) {
            await database_1.default.passwordResetToken.update({
                where: { id: resetTokenRecord.id },
                data: { isUsed: true },
            });
        }
        if (otpRecord) {
            await database_1.default.otpToken.update({
                where: { id: otpRecord.id },
                data: { isUsed: true },
            });
        }
        const hashedPassword = await (0, hash_utils_1.hashPassword)(newPassword);
        await database_1.default.user.update({
            where: { email },
            data: { password: hashedPassword },
        });
        return (0, response_utils_1.sendSuccess)(res, 'Password reset successful. You can now login with your new password.');
    }
    catch (error) {
        return (0, response_utils_1.sendError)(res, 'Failed to reset password', error.message, 500);
    }
};
exports.resetPassword = resetPassword;
// 8. Google OAuth Login
const googleLogin = async (req, res) => {
    try {
        const { idToken } = req.body;
        // Simulated Google OAuth profile verification
        // In production, user info is retrieved via OAuth2Client.verifyIdToken(idToken)
        const googleUser = {
            email: `google_${Date.now()}@example.com`,
            firstName: 'Google',
            lastName: 'User',
            avatarUrl: 'https://lh3.googleusercontent.com/a/default-user',
        };
        let user = await database_1.default.user.findUnique({ where: { email: googleUser.email } });
        if (!user) {
            const dummyPassword = await (0, hash_utils_1.hashPassword)(crypto_1.default.randomBytes(16).toString('hex'));
            user = await database_1.default.user.create({
                data: {
                    email: googleUser.email,
                    password: dummyPassword,
                    firstName: googleUser.firstName,
                    lastName: googleUser.lastName,
                    avatarUrl: googleUser.avatarUrl,
                    isEmailVerified: true,
                },
            });
        }
        const session = await database_1.default.session.create({
            data: {
                userId: user.id,
                ipAddress: req.ip,
                userAgent: req.get('user-agent'),
            },
        });
        const tokenPayload = { userId: user.id, email: user.email, role: 'CUSTOMER', sessionId: session.id };
        const token = (0, jwt_utils_1.signAccessToken)(tokenPayload, true);
        const refreshToken = (0, jwt_utils_1.signRefreshToken)(tokenPayload, true);
        await database_1.default.refreshToken.create({
            data: {
                token: refreshToken,
                userId: user.id,
                ipAddress: req.ip,
                userAgent: req.get('user-agent'),
                expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
            },
        });
        return (0, response_utils_1.sendSuccess)(res, 'Google authentication successful', {
            user: {
                id: user.id,
                email: user.email,
                firstName: user.firstName,
                lastName: user.lastName,
                avatarUrl: user.avatarUrl,
                role: 'CUSTOMER',
            },
            token,
            refreshToken,
        });
    }
    catch (error) {
        return (0, response_utils_1.sendError)(res, 'Google login failed', error.message, 500);
    }
};
exports.googleLogin = googleLogin;
// 9. Session Refresh Token Handler
const refreshTokenHandler = async (req, res) => {
    try {
        const { refreshToken } = req.body;
        if (!refreshToken) {
            return (0, response_utils_1.sendError)(res, 'Refresh token is required', null, 400);
        }
        const storedToken = await database_1.default.refreshToken.findUnique({
            where: { token: refreshToken },
        });
        if (!storedToken || storedToken.revoked || storedToken.expiresAt < new Date()) {
            return (0, response_utils_1.sendError)(res, 'Invalid or expired refresh token', null, 401);
        }
        const payload = (0, jwt_utils_1.verifyRefreshToken)(refreshToken);
        const newToken = (0, jwt_utils_1.signAccessToken)({
            userId: payload.userId,
            adminId: payload.adminId,
            email: payload.email,
            role: payload.role,
            sessionId: payload.sessionId,
        });
        return (0, response_utils_1.sendSuccess)(res, 'Token refreshed successfully', { token: newToken });
    }
    catch (error) {
        return (0, response_utils_1.sendError)(res, 'Token refresh failed', error.message, 401);
    }
};
exports.refreshTokenHandler = refreshTokenHandler;
// 10. Get Active Sessions
const getActiveSessions = async (req, res) => {
    try {
        const userId = req.user?.userId;
        const adminId = req.user?.adminId;
        const sessions = await database_1.default.session.findMany({
            where: {
                ...(userId && { userId }),
                ...(adminId && { adminId }),
                isActive: true,
            },
            orderBy: { lastSeen: 'desc' },
        });
        return (0, response_utils_1.sendSuccess)(res, 'Active sessions retrieved', sessions);
    }
    catch (error) {
        return (0, response_utils_1.sendError)(res, 'Error fetching sessions', error.message, 500);
    }
};
exports.getActiveSessions = getActiveSessions;
// 11. Logout Single Session
const logout = async (req, res) => {
    try {
        const { refreshToken } = req.body;
        if (refreshToken) {
            await database_1.default.refreshToken.updateMany({
                where: { token: refreshToken },
                data: { revoked: true },
            });
        }
        if (req.user?.sessionId) {
            await database_1.default.session.update({
                where: { id: req.user.sessionId },
                data: { isActive: false },
            });
        }
        return (0, response_utils_1.sendSuccess)(res, 'Logged out successfully');
    }
    catch (error) {
        return (0, response_utils_1.sendError)(res, 'Error logging out', error.message, 500);
    }
};
exports.logout = logout;
// 12. Logout All Devices
const logoutAllDevices = async (req, res) => {
    try {
        const userId = req.user?.userId;
        const adminId = req.user?.adminId;
        await Promise.all([
            database_1.default.refreshToken.updateMany({
                where: {
                    ...(userId && { userId }),
                    ...(adminId && { adminId }),
                },
                data: { revoked: true },
            }),
            database_1.default.session.updateMany({
                where: {
                    ...(userId && { userId }),
                    ...(adminId && { adminId }),
                },
                data: { isActive: false },
            }),
        ]);
        return (0, response_utils_1.sendSuccess)(res, 'Successfully logged out from all devices');
    }
    catch (error) {
        return (0, response_utils_1.sendError)(res, 'Failed to logout all devices', error.message, 500);
    }
};
exports.logoutAllDevices = logoutAllDevices;
// 13. Update Profile
const updateProfile = async (req, res) => {
    try {
        const userId = req.user?.userId;
        if (!userId) {
            return (0, response_utils_1.sendError)(res, 'User ID not found in session', null, 401);
        }
        const { firstName, lastName, email, phone } = req.body;
        const user = await database_1.default.user.findUnique({ where: { id: userId } });
        if (!user) {
            return (0, response_utils_1.sendError)(res, 'User not found', null, 404);
        }
        if (email && email !== user.email) {
            const emailExists = await database_1.default.user.findUnique({ where: { email } });
            if (emailExists) {
                return (0, response_utils_1.sendError)(res, 'Email address is already in use by another account', null, 400);
            }
        }
        const updatedUser = await database_1.default.user.update({
            where: { id: userId },
            data: {
                ...(firstName !== undefined && { firstName }),
                ...(lastName !== undefined && { lastName }),
                ...(email !== undefined && { email }),
                ...(phone !== undefined && { phone }),
            },
        });
        return (0, response_utils_1.sendSuccess)(res, 'Profile updated successfully', {
            user: {
                id: updatedUser.id,
                email: updatedUser.email,
                firstName: updatedUser.firstName,
                lastName: updatedUser.lastName,
                phone: updatedUser.phone,
                role: 'CUSTOMER',
                avatarUrl: updatedUser.avatarUrl,
                isEmailVerified: updatedUser.isEmailVerified,
            },
        });
    }
    catch (error) {
        return (0, response_utils_1.sendError)(res, 'Failed to update profile', error.message, 500);
    }
};
exports.updateProfile = updateProfile;
//# sourceMappingURL=auth.controller.js.map