"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.requireAdmin = exports.requireAuth = exports.JwtAuthAdapter = void 0;
exports.createAuthMiddleware = createAuthMiddleware;
exports.setAuthAdapter = setAuthAdapter;
exports.signToken = signToken;
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
const config_1 = require("./config");
const user_1 = require("../services/user");
const bot_api_key_1 = require("../services/auth/bot-api-key");
const principal_1 = require("../services/iam/principal");
/**
 * Reference JWT auth adapter using `jsonwebtoken`.
 *
 * Reads a Bearer token from the Authorization header and verifies it
 * with the configured secret. Returns the decoded payload or null.
 */
class JwtAuthAdapter {
    constructor(secret) {
        this.explicitSecret = secret;
    }
    authenticate(req) {
        const header = req.headers.authorization;
        if (!header?.startsWith('Bearer '))
            return null;
        const token = header.slice(7);
        // Bot API key authentication (async path)
        if (token.startsWith('lt_bot_')) {
            return this.authenticateBotApiKey(token);
        }
        // JWT authentication (sync path)
        const secret = this.explicitSecret ?? config_1.config.JWT_SECRET;
        if (!secret)
            return null;
        try {
            return jsonwebtoken_1.default.verify(token, secret);
        }
        catch (err) {
            if (err instanceof jsonwebtoken_1.default.TokenExpiredError) {
                // Tag the request so the middleware can return a specific message
                req._authError = 'expired';
            }
            return null;
        }
    }
    async authenticateBotApiKey(rawKey) {
        try {
            const keyRecord = await (0, bot_api_key_1.validateBotApiKey)(rawKey);
            if (!keyRecord)
                return null;
            // Resolve bot's actual roles (single JOIN query) instead of hardcoding 'member'
            const principal = await (0, principal_1.resolvePrincipal)(keyRecord.user_id);
            const role = principal?.roleType ?? 'member';
            return {
                userId: keyRecord.user_id,
                role,
                scopes: keyRecord.scopes ?? [],
                principalType: 'bot',
            };
        }
        catch {
            return null;
        }
    }
}
exports.JwtAuthAdapter = JwtAuthAdapter;
/**
 * Create Express middleware from any auth adapter.
 *
 * The adapter handles token extraction and verification.
 * This middleware handles the HTTP response (401) and ensures
 * the payload contains a `userId` claim before setting `req.auth`.
 *
 * Usage:
 * ```typescript
 * import { createAuthMiddleware, JwtAuthAdapter } from '@hotmeshio/long-tail';
 *
 * // Use the reference JWT adapter
 * app.use(createAuthMiddleware(new JwtAuthAdapter('my-secret')));
 *
 * // Or plug in your own adapter
 * app.use(createAuthMiddleware(myClerkAdapter));
 * ```
 */
function createAuthMiddleware(adapter) {
    return async (req, res, next) => {
        try {
            const payload = await adapter.authenticate(req);
            if (!payload) {
                const isExpired = req._authError === 'expired';
                res.status(401).json({ error: isExpired ? 'Token expired' : 'Unauthorized' });
                return;
            }
            if (!payload.userId) {
                res.status(401).json({ error: 'Token missing required userId claim' });
                return;
            }
            req.auth = payload;
            next();
        }
        catch {
            res.status(401).json({ error: 'Unauthorized' });
        }
    };
}
/**
 * Default auth middleware using JWT with `config.JWT_SECRET`.
 * Drop-in replacement for custom middleware — just import and use.
 *
 * When `setAuthAdapter()` is called (e.g., from `start()`), this
 * middleware delegates to the custom adapter instead.
 */
let _authMiddleware = null;
const requireAuth = (req, res, next) => {
    const mw = _authMiddleware || createAuthMiddleware(new JwtAuthAdapter());
    mw(req, res, next);
};
exports.requireAuth = requireAuth;
/**
 * Replace the auth adapter used by `requireAuth`.
 * Call before starting the server.
 */
function setAuthAdapter(adapter) {
    _authMiddleware = createAuthMiddleware(adapter);
}
/**
 * Middleware that requires admin access. Must be placed AFTER requireAuth.
 *
 * Checks isSuperAdmin() via the database first, then falls back to the
 * JWT `role` claim for stateless admin checks. Returns 403 otherwise.
 */
const requireAdmin = async (req, res, next) => {
    try {
        if (!req.auth?.userId) {
            res.status(403).json({ error: 'Forbidden' });
            return;
        }
        // Fast path: trust the JWT role claim for admin/superadmin
        if (req.auth.role === 'admin' || req.auth.role === 'superadmin') {
            next();
            return;
        }
        // Slow path: check database for superadmin role type
        if (await (0, user_1.isSuperAdmin)(req.auth.userId)) {
            next();
            return;
        }
        res.status(403).json({ error: 'Forbidden: admin access required' });
    }
    catch {
        res.status(403).json({ error: 'Forbidden' });
    }
};
exports.requireAdmin = requireAdmin;
/**
 * Generate a JWT token. Utility for tests and token provisioning.
 */
function signToken(payload, expiresIn = '24h') {
    return jsonwebtoken_1.default.sign(payload, config_1.config.JWT_SECRET, { expiresIn });
}
