import jwt from 'jsonwebtoken';
import { userDb, appConfigDb, apiKeysDb } from '../database/db.js';
import { IS_PLATFORM } from '../constants/config.js';
// Use env var if set, otherwise auto-generate a unique secret per installation
const JWT_SECRET = process.env.JWT_SECRET || appConfigDb.getOrCreateJwtSecret();
const isPixcodeApiKey = (token) => typeof token === 'string' && (token.startsWith('px_') || token.startsWith('ck_'));
// Optional API key middleware
const validateApiKey = (req, res, next) => {
    // Skip API key validation if not configured
    if (!process.env.API_KEY) {
        return next();
    }
    const apiKey = req.headers['x-api-key'];
    if (apiKey !== process.env.API_KEY) {
        return res.status(401).json({ error: 'Invalid API key' });
    }
    next();
};
// JWT authentication middleware
const authenticateToken = async (req, res, next) => {
    // Platform mode:  use single database user
    if (IS_PLATFORM) {
        try {
            const user = userDb.getFirstUser();
            if (!user) {
                return res.status(500).json({ error: 'Platform mode: No user found in database' });
            }
            req.user = user;
            return next();
        }
        catch (error) {
            console.error('Platform mode error:', error);
            return res.status(500).json({ error: 'Platform mode: Failed to fetch user' });
        }
    }
    // Pull credentials from any of the supported transports.
    //  - Authorization: Bearer <jwt-or-apikey>
    //  - X-API-Key: <apikey>            (legacy, kept for /api/agent compatibility)
    //  - ?token=<jwt>                    (EventSource workaround — can't set headers)
    //  - ?apiKey=<apikey>                (EventSource workaround)
    // Auth-token mode is decided by the prefix: new keys generated by Pixcode
    // start with `px_`; older `ck_` keys remain valid for existing installs.
    const authHeader = req.headers['authorization'];
    const bearerToken = authHeader && authHeader.startsWith('Bearer ') ? authHeader.slice(7).trim() : null;
    const apiKeyHeader = req.headers['x-api-key'];
    const queryToken = typeof req.query.token === 'string' ? req.query.token : null;
    const queryApiKey = typeof req.query.apiKey === 'string' ? req.query.apiKey : null;
    // Try API-key paths first when the credential is unambiguously an API key.
    const explicitApiKey = apiKeyHeader || queryApiKey
        || (isPixcodeApiKey(bearerToken) ? bearerToken : null)
        || (isPixcodeApiKey(queryToken) ? queryToken : null);
    if (explicitApiKey) {
        try {
            const user = apiKeysDb.validateApiKey(explicitApiKey);
            if (!user) {
                return res.status(401).json({ error: 'Invalid or inactive API key' });
            }
            req.user = user;
            return next();
        }
        catch (error) {
            console.error('API key validation error:', error);
            return res.status(500).json({ error: 'Authentication backend error' });
        }
    }
    // Otherwise fall back to JWT.
    const jwtToken = bearerToken || queryToken;
    if (!jwtToken) {
        return res.status(401).json({ error: 'Access denied. No token provided.' });
    }
    try {
        const decoded = jwt.verify(jwtToken, JWT_SECRET);
        // Verify user still exists and is active
        const user = userDb.getUserById(decoded.userId);
        if (!user) {
            return res.status(401).json({ error: 'Invalid token. User not found.' });
        }
        // Auto-refresh: if token is past halfway through its lifetime, issue a new one
        if (decoded.exp && decoded.iat) {
            const now = Math.floor(Date.now() / 1000);
            const halfLife = (decoded.exp - decoded.iat) / 2;
            if (now > decoded.iat + halfLife) {
                const newToken = generateToken(user);
                res.setHeader('X-Refreshed-Token', newToken);
            }
        }
        req.user = user;
        next();
    }
    catch (error) {
        console.error('Token verification error:', error);
        return res.status(403).json({ error: 'Invalid token' });
    }
};
// Generate JWT token
const generateToken = (user) => {
    return jwt.sign({
        userId: user.id,
        username: user.username
    }, JWT_SECRET, { expiresIn: '7d' });
};
// WebSocket authentication function
const authenticateWebSocket = (token) => {
    // Platform mode: bypass token validation, return first user
    if (IS_PLATFORM) {
        try {
            const user = userDb.getFirstUser();
            if (user) {
                return { id: user.id, userId: user.id, username: user.username };
            }
            return null;
        }
        catch (error) {
            console.error('Platform mode WebSocket error:', error);
            return null;
        }
    }
    // Normal OSS validation — accept either an API key (`px_…` or legacy
    // `ck_…`) or a JWT.
    // Mirrors the REST `authenticateToken` middleware so any tool that has
    // a Pixcode API key (CI scripts, the api-tester subagent, the user's own
    // automation, ...) can also open a WebSocket without first exchanging
    // the key for a JWT.
    if (!token) {
        return null;
    }
    if (isPixcodeApiKey(token)) {
        try {
            const user = apiKeysDb.validateApiKey(token);
            if (!user)
                return null;
            return { userId: user.id, username: user.username };
        }
        catch (error) {
            console.error('WebSocket API key validation error:', error);
            return null;
        }
    }
    try {
        const decoded = jwt.verify(token, JWT_SECRET);
        // Verify user actually exists in database (matches REST authenticateToken behavior)
        const user = userDb.getUserById(decoded.userId);
        if (!user) {
            return null;
        }
        return { userId: user.id, username: user.username };
    }
    catch (error) {
        console.error('WebSocket token verification error:', error);
        return null;
    }
};
export { validateApiKey, authenticateToken, generateToken, authenticateWebSocket, JWT_SECRET };
//# sourceMappingURL=auth.js.map