import User from '#models/user';
import crypto from 'node:crypto';
import { loginValidator } from '#validators/user';
import UserTransformer from '#transformers/user_transformer';
import { logUserLogin } from '#services/login_logger_service';
import { verify } from 'otplib';
import hash from '@adonisjs/core/services/hash';
import { DateTime } from 'luxon';
import logger from '@adonisjs/core/services/logger';
const DUMMY_HASH = '$scrypt$n=16384,r=8,p=1$c2FsdHNhbHRzYWx0c2FsdA$ZHVtbXlkdW1teWR1bW15ZHVtbXlkdW1teWR1bW15ZHVtbXk';
const MAX_LOGIN_ATTEMPTS = 3;
const MAX_TWO_FACTOR_ATTEMPTS = 5;
const TWO_FACTOR_TOKEN_TTL_MINUTES = 10;
export default class AccessTokensController {
    async store({ request, response, serialize }) {
        const { email, password } = await request.validateUsing(loginValidator);
        const user = await User.findBy('email', email);
        if (!user) {
            try {
                await hash.verify(DUMMY_HASH, password);
            }
            catch {
            }
            return response.badRequest({ errors: [{ message: 'Invalid user credentials' }] });
        }
        if (user.blockedUntil && user.blockedUntil > DateTime.now()) {
            return response.forbidden({
                errors: [
                    {
                        message: `This account is temporarily locked due to multiple failed login attempts. Please try again after ${user.blockedUntil.toFormat('HH:mm')} or contact support.`,
                    },
                ],
            });
        }
        const isPasswordValid = await hash.verify(user.password, password);
        if (!isPasswordValid) {
            user.otpTokenAttempts += 1;
            if (user.otpTokenAttempts >= MAX_LOGIN_ATTEMPTS) {
                user.status = 'blocked';
                await user.save();
                return response.forbidden({
                    errors: [{ message: 'This account has been blocked due to multiple failed login attempts. Please contact support.' }]
                });
            }
            await user.save();
            const remainingTrials = MAX_LOGIN_ATTEMPTS - user.otpTokenAttempts;
            return response.badRequest({
                errors: [{
                        message: `Invalid credentials. You have ${remainingTrials} ${remainingTrials === 1 ? 'attempt' : 'attempts'} remaining before your account is blocked.`
                    }]
            });
        }
        if (user.status !== 'active') {
            return response.forbidden({
                errors: [{ message: 'This account is not active. Please contact support.' }],
            });
        }
        if (!user.isEmailVerified) {
            return response.badRequest({ errors: [{ message: 'Please verify your email first.' }] });
        }
        if (user.otpTokenAttempts > 0 || user.blockedUntil) {
            user.otpTokenAttempts = 0;
            user.blockedUntil = null;
            await user.save();
        }
        if (user.isTwoFactorEnabled) {
            const tempToken = crypto.randomBytes(32).toString('hex');
            user.otpToken = tempToken;
            user.otpTokenExpiresAt = DateTime.now().plus({ minutes: TWO_FACTOR_TOKEN_TTL_MINUTES });
            user.otpTokenAttempts = 0;
            await user.save();
            return response.ok({
                requiresTwoFactor: true,
                message: 'Two-factor authentication code required to complete login.',
                tempToken,
            });
        }
        const ipAddress = request.ip();
        const userAgent = request.header('user-agent') || '';
        try {
            await logUserLogin({
                userId: user.id,
                ipAddress,
                userAgentString: userAgent,
                mfaVerified: false
            });
        }
        catch (error) {
            logger.error(error);
        }
        const token = await User.accessTokens.create(user);
        return serialize({
            user: UserTransformer.transform(user),
            token: token.value.release(),
        });
    }
    async verifyTwoFactor({ request, response, serialize }) {
        const { tempToken, code } = request.only(['tempToken', 'code']);
        if (!tempToken || !code) {
            return response.badRequest({ message: 'The temporary token and verification code are required.' });
        }
        const cleanCode = String(code).trim();
        if (cleanCode.length !== 6 || !/^\d+$/.test(cleanCode)) {
            return response.badRequest({ message: 'The verification code must be exactly 6 numeric digits.' });
        }
        const user = await User.findBy('otp_token', tempToken);
        if (!user || !user.isTwoFactorEnabled || !user.twoFactorSecret) {
            return response.badRequest({ message: 'Invalid or expired login attempt.' });
        }
        if (!user.otpTokenExpiresAt || user.otpTokenExpiresAt < DateTime.now()) {
            user.otpToken = null;
            user.otpTokenExpiresAt = null;
            await user.save();
            return response.badRequest({ message: 'Invalid or expired login attempt.' });
        }
        if (user.otpTokenAttempts >= MAX_TWO_FACTOR_ATTEMPTS) {
            user.otpToken = null;
            user.otpTokenExpiresAt = null;
            user.otpTokenAttempts = 0;
            await user.save();
            return response.badRequest({ message: 'Too many attempts. Please log in again.' });
        }
        if (user.status !== 'active') {
            return response.forbidden({ message: 'This account is not active. Please contact support.' });
        }
        const isValid = await verify({
            token: cleanCode,
            secret: user.twoFactorSecret
        });
        console.log(`[2FA DEBUG] Cryptographic validation result:`, isValid);
        if (!isValid || isValid.valid != true) {
            user.otpTokenAttempts += 1;
            await user.save();
            return response.badRequest({ message: 'Invalid verification code. Please try again.' });
        }
        user.otpToken = null;
        user.otpTokenExpiresAt = null;
        user.otpTokenAttempts = 0;
        await user.save();
        const ipAddress = request.ip();
        const userAgent = request.header('user-agent') || '';
        try {
            await logUserLogin({
                userId: user.id,
                ipAddress,
                userAgentString: userAgent,
                mfaVerified: true
            });
        }
        catch (error) {
            logger.error(error);
        }
        const token = await User.accessTokens.create(user);
        return serialize({
            user: UserTransformer.transform(user),
            token: token.value.release(),
        });
    }
    async destroy({ auth, response }) {
        await auth.authenticate();
        const user = auth.user;
        const token = user.currentAccessToken;
        if (token) {
            await User.accessTokens.delete(user, token.identifier);
        }
        return response.ok({
            message: 'Logged out successfully',
        });
    }
}
//# sourceMappingURL=access_tokens_controller.js.map