"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.authRouter = void 0;
const express_1 = require("express");
const zod_1 = require("zod");
const bcryptjs_1 = __importDefault(require("bcryptjs"));
const client_1 = require("@prisma/client");
const auth_service_1 = require("./auth.service");
const auth_schemas_1 = require("./auth.schemas");
const validate_1 = require("../../middleware/validate");
const auth_middleware_1 = require("../../middleware/auth.middleware");
const rate_limit_1 = require("../../middleware/rate-limit");
const database_1 = require("../../config/database");
const verification_token_service_1 = require("../../services/verification-token.service");
const password_reset_token_service_1 = require("../../services/password-reset-token.service");
const email_service_1 = require("../../services/email.service");
const settings_service_1 = require("../settings/settings.service");
const env_1 = require("../../config/env");
const logger_1 = require("../../utils/logger");
const auth_rate_limits_1 = require("./auth.rate-limits");
const profile_service_1 = require("../people/profile.service");
const router = (0, express_1.Router)();
exports.authRouter = router;
// POST /api/auth/login
router.post('/login', rate_limit_1.authRateLimit, (0, validate_1.validate)(auth_schemas_1.loginSchema), async (req, res, next) => {
    try {
        const result = await auth_service_1.authService.login(req.body.email, req.body.password);
        res.json(result);
    }
    catch (err) {
        next(err);
    }
});
// POST /api/auth/register
router.post('/register', rate_limit_1.authRateLimit, (0, validate_1.validate)(auth_schemas_1.registerSchema), async (req, res, next) => {
    try {
        const result = await auth_service_1.authService.register(req.body);
        res.status(201).json(result);
    }
    catch (err) {
        next(err);
    }
});
// POST /api/auth/verify-email
const verifyEmailSchema = zod_1.z.object({ token: zod_1.z.string().min(1) });
router.post('/verify-email', rate_limit_1.authRateLimit, (0, validate_1.validate)(verifyEmailSchema), async (req, res, next) => {
    try {
        const { token } = req.body;
        const result = await verification_token_service_1.verificationTokenService.verifyToken(token);
        if (!result.valid || !result.userId) {
            res.status(400).json({
                error: { message: result.error || 'Invalid token', code: 'INVALID_TOKEN' },
            });
            return;
        }
        const settings = await settings_service_1.siteSettingsService.get();
        const autoApprove = settings.autoApproveVerifiedUsers;
        const newStatus = autoApprove ? client_1.UserStatus.ACTIVE : client_1.UserStatus.PENDING_APPROVAL;
        await database_1.prisma.user.update({
            where: { id: result.userId },
            data: { emailVerified: true, status: newStatus },
        });
        // If not auto-approved, notify admins
        if (!autoApprove) {
            const user = await database_1.prisma.user.findUnique({ where: { id: result.userId } });
            if (user) {
                const admins = await database_1.prisma.user.findMany({
                    where: { role: client_1.UserRole.SUPER_ADMIN, status: client_1.UserStatus.ACTIVE },
                    select: { email: true },
                });
                if (admins.length > 0) {
                    await email_service_1.emailService.sendPendingApprovalNotification({
                        adminEmails: admins.map(a => a.email),
                        newUserEmail: user.email,
                        newUserName: user.name || '',
                    }).catch(err => logger_1.logger.error('Failed to send approval notification:', err));
                }
            }
        }
        res.json({
            verified: true,
            approved: autoApprove,
            message: autoApprove
                ? 'Email verified. You can now log in.'
                : 'Email verified. Your account is pending admin approval.',
        });
    }
    catch (err) {
        next(err);
    }
});
// POST /api/auth/resend-verification
const resendVerificationSchema = zod_1.z.object({ email: zod_1.z.string().email() });
router.post('/resend-verification', (0, auth_rate_limits_1.createVerificationRateLimit)(), (0, validate_1.validate)(resendVerificationSchema), async (req, res, next) => {
    try {
        // Always return success to prevent user enumeration
        res.json({ message: 'If your email is registered and pending verification, a new verification link has been sent.' });
        // Send asynchronously (don't block response)
        const user = await database_1.prisma.user.findUnique({ where: { email: req.body.email } });
        if (user && user.status === client_1.UserStatus.PENDING_VERIFICATION) {
            const token = await verification_token_service_1.verificationTokenService.createToken(user.id);
            const adminUrl = env_1.env.ADMIN_URL || 'http://localhost:3000';
            const verificationUrl = `${adminUrl}/verify-email?token=${token}`;
            await email_service_1.emailService.sendVerificationEmail({
                recipientEmail: user.email,
                recipientName: user.name || 'there',
                verificationUrl,
            }).catch(err => logger_1.logger.error('Failed to resend verification email:', err));
        }
    }
    catch (err) {
        next(err);
    }
});
// POST /api/auth/forgot-password
const forgotPasswordSchema = zod_1.z.object({ email: zod_1.z.string().email() });
router.post('/forgot-password', (0, auth_rate_limits_1.createResetRateLimit)(), (0, validate_1.validate)(forgotPasswordSchema), async (req, res, next) => {
    try {
        // Always return success to prevent user enumeration
        res.json({ message: 'If your email is registered, a password reset link has been sent.' });
        // Send asynchronously
        const user = await database_1.prisma.user.findUnique({ where: { email: req.body.email } });
        if (user && user.status === client_1.UserStatus.ACTIVE) {
            const token = await password_reset_token_service_1.passwordResetTokenService.createToken(user.id);
            const adminUrl = env_1.env.ADMIN_URL || 'http://localhost:3000';
            const resetUrl = `${adminUrl}/reset-password?token=${token}`;
            await email_service_1.emailService.sendPasswordResetEmail({
                recipientEmail: user.email,
                recipientName: user.name || 'there',
                resetUrl,
            }).catch(err => logger_1.logger.error('Failed to send password reset email:', err));
        }
    }
    catch (err) {
        next(err);
    }
});
// POST /api/auth/reset-password
const resetPasswordSchema = zod_1.z.object({
    token: zod_1.z.string().min(1),
    password: zod_1.z.string()
        .min(12, 'Password must be at least 12 characters')
        .regex(/[A-Z]/, 'Password must contain at least one uppercase letter')
        .regex(/[a-z]/, 'Password must contain at least one lowercase letter')
        .regex(/[0-9]/, 'Password must contain at least one digit'),
});
router.post('/reset-password', rate_limit_1.authRateLimit, (0, validate_1.validate)(resetPasswordSchema), async (req, res, next) => {
    try {
        const { token, password } = req.body;
        const result = await password_reset_token_service_1.passwordResetTokenService.validateToken(token);
        if (!result.valid || !result.userId) {
            res.status(400).json({
                error: { message: result.error || 'Invalid token', code: 'INVALID_TOKEN' },
            });
            return;
        }
        const hashedPassword = await bcryptjs_1.default.hash(password, 12);
        // Update password, mark token used, invalidate all refresh tokens — all in one transaction
        await database_1.prisma.$transaction(async (tx) => {
            await tx.user.update({
                where: { id: result.userId },
                data: { password: hashedPassword },
            });
            await tx.refreshToken.deleteMany({ where: { userId: result.userId } });
            await tx.passwordResetToken.update({
                where: { token },
                data: { usedAt: new Date() },
            });
        });
        res.json({ message: 'Password has been reset. You can now log in with your new password.' });
    }
    catch (err) {
        next(err);
    }
});
// POST /api/auth/refresh
router.post('/refresh', rate_limit_1.authRateLimit, (0, validate_1.validate)(auth_schemas_1.refreshSchema), async (req, res, next) => {
    try {
        const result = await auth_service_1.authService.refreshTokens(req.body.refreshToken);
        res.json(result);
    }
    catch (err) {
        next(err);
    }
});
// POST /api/auth/logout
router.post('/logout', rate_limit_1.authRateLimit, (0, validate_1.validate)(auth_schemas_1.refreshSchema), async (req, res, next) => {
    try {
        await auth_service_1.authService.logout(req.body.refreshToken);
        res.json({ message: 'Logged out' });
    }
    catch (err) {
        next(err);
    }
});
// GET /api/auth/me/profile-token
router.get('/me/profile-token', auth_middleware_1.authenticate, async (req, res, next) => {
    try {
        const userId = req.user.id;
        // Look up existing Contact linked to this user
        let contact = await database_1.prisma.contact.findUnique({ where: { userId } });
        // Auto-create Contact if none exists
        if (!contact) {
            const user = await database_1.prisma.user.findUnique({
                where: { id: userId },
                select: { name: true, email: true },
            });
            if (!user) {
                res.status(401).json({ error: { message: 'Invalid token', code: 'INVALID_TOKEN' } });
                return;
            }
            try {
                contact = await database_1.prisma.contact.create({
                    data: {
                        displayName: user.name || user.email,
                        firstName: user.name?.split(' ')[0] || null,
                        lastName: user.name?.split(' ').slice(1).join(' ') || null,
                        email: user.email,
                        userId,
                        primarySource: 'USER',
                    },
                });
            }
            catch (err) {
                // Race condition: another request created the Contact — retry lookup
                if (err.code === 'P2002') {
                    contact = await database_1.prisma.contact.findUnique({ where: { userId } });
                }
                if (!contact)
                    throw err;
            }
        }
        // Generate profile token if Contact doesn't have one
        if (!contact.profileToken) {
            const result = await profile_service_1.profileService.generateProfileToken(contact.id);
            res.json({ token: result.token });
            return;
        }
        res.json({ token: contact.profileToken });
    }
    catch (err) {
        next(err);
    }
});
// GET /api/auth/me
router.get('/me', auth_middleware_1.authenticate, async (req, res, next) => {
    try {
        const user = await database_1.prisma.user.findUnique({
            where: { id: req.user.id },
            select: {
                id: true,
                email: true,
                name: true,
                phone: true,
                role: true,
                roles: true,
                status: true,
                permissions: true,
                createdVia: true,
                emailVerified: true,
                lastLoginAt: true,
                createdAt: true,
                updatedAt: true,
            },
        });
        if (!user) {
            res.status(401).json({ error: { message: 'Invalid token', code: 'INVALID_TOKEN' } });
            return;
        }
        res.json(user);
    }
    catch (err) {
        next(err);
    }
});
//# sourceMappingURL=auth.routes.js.map