const UnitOfWork = require('../../database/UnitOfWork'); const ReferralService = require('../../services/referral/ReferralService'); // <-- use unified service const { logger } = require('../../middleware/logger'); // Helper to get frontend URL without trailing slash, no localhost fallback function getFrontendUrl() { let url = process.env.FRONTEND_URL || ''; if (url.endsWith('/')) { url = url.slice(0, -1); } return url; } class ReferralTokenController { static async create(req, res) { const userId = req.user.userId; const userRole = req.user.role; const { expiresInDays, maxUses } = req.body; // Normalize unlimited expiry from frontend (0 or -1) to -1 sentinel let normalizedExpiresInDays = (expiresInDays === 0) ? -1 : expiresInDays; logger.info('ReferralTokenController:createReferralToken:start', { userId, normalizedExpiresInDays, maxUses }); const unitOfWork = new UnitOfWork(); await unitOfWork.start(); try { let maxActiveLinks = 5; if (userRole === 'admin' || userRole === 'super_admin') { maxActiveLinks = 15; } const activeCount = await ReferralService.countActiveReferralTokens(userId, unitOfWork); if (activeCount >= maxActiveLinks) { await unitOfWork.rollback(); return res.status(403).json({ success: false, message: `Maximum active referral links reached (${maxActiveLinks}) for your role (${userRole}).` }); } const token = await ReferralService.createReferralToken({ userId, expiresInDays: normalizedExpiresInDays, maxUses, unitOfWork }); if (normalizedExpiresInDays === -1) { logger.info('ReferralTokenController:createReferralToken:unlimited_expiry', { userId }); } await unitOfWork.commit(); logger.info('ReferralTokenController:createReferralToken:success', { token }); const link = `${getFrontendUrl()}/register?ref=${token.token}`; res.json({ success: true, token: token.token, link, expiresAt: token.expiresAt, maxUses: token.maxUses }); } catch (error) { await unitOfWork.rollback(error); logger.error('ReferralTokenController:createReferralToken:error', { userId, error }); res.status(400).json({ success: false, message: error.message }); } } static async list(req, res) { const userId = req.user.userId; logger.info('ReferralTokenController:getUserReferralTokens:start', { userId }); const unitOfWork = new UnitOfWork(); await unitOfWork.start(); try { const tokens = await ReferralService.getUserReferralTokens(userId, unitOfWork); await unitOfWork.commit(); logger.info('ReferralTokenController:getUserReferralTokens:success', { count: tokens.length }); const frontendUrl = getFrontendUrl(); // FIX: tokens already transformed by service; use provided fields instead of raw DB column names const result = tokens.map(t => { const isUnlimitedExpiry = t.isUnlimited || !t.expires; const usage = t.usage || (t.isUnlimited ? `${t.used}/∞` : `${t.used}/${t.maxUses}`); return { id: t.id, status: t.status, created: t.created, created_at: t.created, // legacy alias expires: isUnlimitedExpiry ? '∞' : t.expires, expires_at: t.expires, // legacy alias link: t.link || `${frontendUrl}/register?ref=${t.token}`, token: t.token, // normalized usage fields used: t.used, maxUses: t.maxUses, isUnlimited: t.isUnlimited, usage, usageDisplay: usage, // backward compatibility (old FE expected these) max_uses: t.isUnlimited ? -1 : t.maxUses, usage_count: t.used }; }); return res.json({ success: true, tokens: result }); } catch (error) { await unitOfWork.rollback(error); logger.error('ReferralTokenController:getUserReferralTokens:error', { userId, error }); res.status(500).json({ success: false, message: error.message }); } } static async stats(req, res) { const userId = req.user.userId; logger.info('ReferralTokenController:getUserReferralStats:start', { userId }); const unitOfWork = new UnitOfWork(); await unitOfWork.start(); try { const stats = await ReferralService.getUserReferralStats(userId, unitOfWork); await unitOfWork.commit(); logger.info('ReferralTokenController:getUserReferralStats:success', { stats }); // Response now includes companyUsersReferred and personalUsersReferred res.json({ success: true, stats }); } catch (error) { await unitOfWork.rollback(error); logger.error('ReferralTokenController:getUserReferralStats:error', { userId, error }); res.status(500).json({ success: false, message: error.message }); } } static async deactivate(req, res) { const userId = req.user.userId; const { tokenId } = req.body; logger.info('ReferralTokenController:deactivateReferralToken:start', { userId, tokenId }); if (!tokenId) { return res.status(400).json({ success: false, message: 'tokenId is required' }); } const unitOfWork = new UnitOfWork(); await unitOfWork.start(); try { await ReferralService.deactivateReferralToken(userId, tokenId, unitOfWork); await unitOfWork.commit(); logger.info('ReferralTokenController:deactivateReferralToken:success', { userId, tokenId }); res.json({ success: true, message: 'Referral link deactivated' }); } catch (error) { await unitOfWork.rollback(error); logger.error('ReferralTokenController:deactivateReferralToken:error', { userId, tokenId, error }); res.status(500).json({ success: false, message: error.message }); } } } module.exports = ReferralTokenController;