import { AuthError } from '../auth/authService.js';
import { clearAuthCookies, cookieNames, setSessionCookie, setDeviceCookie } from '../auth/cookies.js';
import { isAllowedOrigin } from '../config.js';
function cookies(request) {
    return request.cookies;
}
function authContext(request, rememberNetwork) {
    const ua = request.headers['user-agent'];
    return {
        ip: request.ip,
        userAgent: Array.isArray(ua) ? ua[0] : ua,
        rememberNetwork,
        deviceToken: cookies(request)[cookieNames.device],
        clientKey: request.ip
    };
}
export async function requireAllowedOrigin(config, request, reply) {
    if (!request.headers.origin) {
        await reply.code(403).send({ error: 'origin_required' });
        return;
    }
    if (!isAllowedOrigin(config, request.headers.origin)) {
        await reply.code(403).send({ error: 'origin_not_allowed' });
    }
}
async function requireAuthOrigin(config, request, reply) {
    await requireAllowedOrigin(config, request, reply);
}
function sendAuthError(reply, error) {
    if (error instanceof AuthError) {
        return reply.code(error.statusCode).send({ error: error.code.toLowerCase() });
    }
    throw error;
}
export async function registerAuthRoutes(app, config, services) {
    app.get('/api/auth/session', async (request) => {
        const requestCookies = cookies(request);
        const status = await services.authService.getSessionStatus(requestCookies[cookieNames.session], {
            ip: request.ip,
            deviceToken: requestCookies[cookieNames.device]
        });
        const invalidationSource = sanitizeDiagnosticValue(request.query.invalidationSource);
        if (invalidationSource || (requestCookies[cookieNames.session] && !status.authenticated)) {
            const diagnostic = await services.authService.diagnoseSession(requestCookies[cookieNames.session], request.ip);
            request.log.warn({
                event: 'auth.session_status_checked',
                invalidationSource: invalidationSource || null,
                authenticated: status.authenticated,
                deviceTrusted: status.deviceTrusted,
                currentIpWhitelisted: status.currentIpWhitelisted,
                deviceCookiePresent: Boolean(requestCookies[cookieNames.device]),
                ...diagnostic
            }, 'auth session status checked');
        }
        return status;
    });
    app.post('/api/auth/password', { preHandler: (request, reply) => requireAuthOrigin(config, request, reply) }, async (request, reply) => {
        try {
            if (typeof request.body?.password !== 'string') {
                return reply.code(400).send({ error: 'password_required' });
            }
            const result = await services.authService.setupPassword(request.body.password, authContext(request, request.body.rememberNetwork));
            setSessionCookie(reply, config, result.session.sessionId);
            setDeviceCookie(reply, config, result.deviceToken);
            return { passwordSet: true, authenticated: true, totpEnabled: false, deviceTrusted: true, currentIpWhitelisted: Boolean(request.body.rememberNetwork), expiresAt: result.session.expiresAt };
        }
        catch (error) {
            return sendAuthError(reply, error);
        }
    });
    app.post('/api/auth/login', { preHandler: (request, reply) => requireAuthOrigin(config, request, reply) }, async (request, reply) => {
        try {
            if (typeof request.body?.password !== 'string') {
                return reply.code(400).send({ error: 'password_required' });
            }
            const otp = typeof request.body.otp === 'string' ? request.body.otp : undefined;
            const result = await services.authService.loginWithPassword(request.body.password, otp, authContext(request, request.body.rememberNetwork));
            setSessionCookie(reply, config, result.session.sessionId);
            setDeviceCookie(reply, config, result.deviceToken);
            return services.authService.getSessionStatus(result.session.sessionId, { ip: request.ip, deviceToken: result.deviceToken });
        }
        catch (error) {
            return sendAuthError(reply, error);
        }
    });
    app.post('/api/auth/reauth', { preHandler: (request, reply) => requireAuthOrigin(config, request, reply) }, async (request, reply) => {
        try {
            const otp = typeof request.body?.otp === 'string' ? request.body.otp : undefined;
            const session = await services.authService.reauthWithOtp(otp, authContext(request, request.body?.rememberNetwork));
            setSessionCookie(reply, config, session.sessionId);
            const deviceToken = cookies(request)[cookieNames.device];
            return services.authService.getSessionStatus(session.sessionId, { ip: request.ip, deviceToken });
        }
        catch (error) {
            return sendAuthError(reply, error);
        }
    });
    app.post('/api/auth/logout', { preHandler: (request, reply) => requireAuthOrigin(config, request, reply) }, async (request, reply) => {
        const requestCookies = cookies(request);
        const session = await services.authService.requireSession(request);
        if (session) {
            await services.authService.logout(requestCookies[cookieNames.session], requestCookies[cookieNames.device]);
            if (request.body?.closeTerminals === true) {
                services.terminalManager.closeAll();
            }
        }
        clearAuthCookies(reply, config);
        return services.authService.getSessionStatus(undefined);
    });
}
function sanitizeDiagnosticValue(value) {
    return typeof value === 'string' ? value.replace(/[^a-zA-Z0-9_.:-]/g, '_').slice(0, 96) : '';
}
//# sourceMappingURL=authRoutes.js.map