/**
 * Auth Controller
 * Minimal, real auth endpoints backing the example API routes.
 */
import { Auth } from '../../src/auth/Auth.js';
import { isUndefinedOrNull } from '../../src/helper/index.js';
import { User } from '../Models/User.js';
import { getString } from '../../src/common/utility.js';
import DefaultLogger, { Logger as NamedLogger } from '../../src/config/logger.js';
import { getValidatedBody } from '../../src/http/ValidationHelper.js';
import { JwtManager } from '../../src/security/JwtManager.js';
const noopLoggerMethod = (_message, _data) => undefined;
const Logger = NamedLogger ??
    DefaultLogger ??
    Object.freeze({
        debug: noopLoggerMethod,
        info: noopLoggerMethod,
        warn: noopLoggerMethod,
        error: noopLoggerMethod,
        fatal: noopLoggerMethod,
    });
const pickPublicUser = (row) => {
    return {
        id: row.id,
        name: getString(row.name),
        email: getString(row.email),
    };
};
/**
 * Authenticates a user by email and password.
 * Validates credentials against the database and returns a JWT access token on success.
 * Logs all authentication attempts for security auditing.
 * @param req - HTTP request containing email and password
 * @param res - HTTP response to send authentication result
 * @returns Promise that resolves after sending the response
 */
async function login(req, res) {
    const body = getValidatedBody(req);
    if (!body) {
        Logger.error('AuthController.login: validation middleware did not populate req.validated.body');
        return res.setStatus(500).json({ error: 'Internal server error' });
    }
    const email = getString(body['email']);
    const password = getString(body['password']);
    const ipAddress = req.getRaw().socket.remoteAddress ?? 'unknown';
    try {
        const existing = await User.where('email', '=', email).limit(1).first();
        if (existing === null) {
            Logger.warn('AuthController.login: failed login attempt', {
                email,
                ip: ipAddress,
                reason: 'user_not_found',
                timestamp: new Date().toISOString(),
            });
            res.setStatus(401).json({ error: 'Invalid credentials' });
            return;
        }
        const passwordHash = getString(existing.password);
        const ok = await Auth.compare(password, passwordHash);
        if (!ok) {
            Logger.warn('AuthController.login: failed login attempt', {
                email,
                ip: ipAddress,
                reason: 'invalid_password',
                timestamp: new Date().toISOString(),
            });
            res.setStatus(401).json({ error: 'Invalid credentials' });
            return;
        }
        const user = pickPublicUser(existing);
        const subject = (() => {
            const id = user.id;
            if (typeof id === 'string' && id.length > 0)
                return id;
            if (typeof id === 'number' && Number.isFinite(id))
                return String(id);
            return undefined;
        })();
        // Bulletproof Auth (device binding) expects a device id header to match a JWT claim.
        // For the example app, we mint a stable device id derived from the subject.
        // Production apps should issue a per-device id and manage a per-device signing secret.
        const deviceId = isUndefinedOrNull(subject) ? undefined : `dev-${subject}`;
        const token = await JwtManager.signAccessToken({
            sub: subject,
            email,
            ...(isUndefinedOrNull(deviceId) ? {} : { deviceId }),
        });
        Logger.info('AuthController.login: successful login', {
            userId: subject,
            email,
            ip: ipAddress,
            timestamp: new Date().toISOString(),
        });
        res.json({
            token,
            token_type: 'Bearer',
            ...(isUndefinedOrNull(deviceId) ? {} : { deviceId }),
            user,
        });
    }
    catch (error) {
        Logger.error('AuthController.login: unexpected error', {
            email,
            ip: ipAddress,
            error: error instanceof Error ? error.message : String(error),
            timestamp: new Date().toISOString(),
        });
        res.setStatus(500).json({ error: 'Login failed' });
    }
}
/**
 * Registers a new user with name, email, and password.
 * Validates email uniqueness, hashes password, and stores user in database.
 * Returns 201 on success, 409 if email already exists.
 * @param req - HTTP request containing name, email, and password
 * @param res - HTTP response to send registration result
 * @returns Promise that resolves after sending the response
 */
async function register(req, res) {
    const body = getValidatedBody(req);
    if (!body) {
        Logger.error('AuthController.register: validation middleware did not populate req.validated.body');
        res.setStatus(500).json({ error: 'Internal server error' });
        return;
    }
    const name = getString(body['name']);
    const email = getString(body['email']);
    const password = getString(body['password']);
    const ipAddress = req.getRaw().socket.remoteAddress ?? 'unknown';
    try {
        const existing = await User.where('email', '=', email).limit(1).first();
        if (existing !== null) {
            Logger.warn('AuthController.register: duplicate email attempt', {
                email,
                ip: ipAddress,
                timestamp: new Date().toISOString(),
            });
            res.setStatus(409).json({ error: 'Email already registered' });
            return;
        }
        const passwordHash = await Auth.hash(password);
        const result = await User.query().insert({
            name,
            email,
            password: passwordHash,
        });
        let insertedUserId = result.id;
        if (insertedUserId === null || insertedUserId === undefined) {
            const inserted = await User.where('email', '=', email).limit(1).first();
            if (inserted?.id !== null && inserted?.id !== undefined) {
                insertedUserId = inserted.id;
            }
        }
        if (insertedUserId !== null && insertedUserId !== undefined) {
            Logger.info('AuthController.register: successful registration', {
                user_id: insertedUserId,
                email,
                ip: ipAddress,
                timestamp: new Date().toISOString(),
            });
            res.setStatus(201).json({ message: 'Registered' });
        }
        else {
            Logger.error('Failed to retrieve inserted user ID', {
                email,
                ip: ipAddress,
            });
            res.setStatus(500).json({ error: 'Registration failed' });
        }
        return;
    }
    catch (error) {
        Logger.error('AuthController.register failed', error);
        res.setStatus(500).json({ error: 'Registration failed' });
    }
}
/**
 * Logs out the current user by revoking their JWT token.
 * Extracts authorization header and marks token as revoked.
 * Requires persistent token revocation store for stateless JWT validation.
 * @param req - HTTP request containing authorization header with JWT token
 * @param res - HTTP response to send logout confirmation
 * @returns Promise that resolves after sending the response
 */
async function logout(req, res) {
    const authHeader = typeof req.getHeader === 'function' ? req.getHeader('authorization') : undefined;
    await JwtManager.logout(authHeader);
    res.json({ message: 'Logged out' });
}
/**
 * Logs out the current user from all devices by removing all active sessions for their subject.
 *
 * With session allowlist enforcement, deleting a user's session records causes any previously issued
 * tokens to become unauthorized (401) immediately.
 */
async function logoutAll(req, res) {
    const sub = typeof req.user?.sub === 'string' ? req.user.sub.trim() : '';
    if (sub === '') {
        res.setStatus(401).json({ error: 'Unauthorized' });
        return;
    }
    await JwtManager.logoutAll(sub);
    res.json({ message: 'Logged out everywhere' });
}
/**
 * Refreshes the user's JWT access token.
 * Generates a new token with the same claims as the current user.
 * Returns 401 if user is not authenticated.
 * @param req - HTTP request with user populated by authentication middleware
 * @param res - HTTP response to send refreshed token
 * @returns Promise that resolves after sending the response
 */
async function refresh(req, res) {
    const user = req.user;
    if (user === undefined) {
        res.setStatus(401).json({ error: 'Unauthorized' });
        return;
    }
    const token = await JwtManager.signAccessToken(user);
    res.json({ token, token_type: 'Bearer' });
}
export const AuthController = Object.freeze({
    create() {
        return {
            login,
            register,
            logout,
            logoutAll,
            refresh,
        };
    },
});
export default AuthController;
