import { getCsrfTokenManager } from './csrf-manager.js';
function getHeaderValue(header) {
    if (!header)
        return null;
    if (Array.isArray(header))
        return header[0] ?? null;
    return header;
}
function parseCookieHeader(cookieHeader) {
    if (!cookieHeader)
        return {};
    const cookies = {};
    for (const part of cookieHeader.split(';')) {
        const [rawName, ...rawValueParts] = part.trim().split('=');
        if (!rawName)
            continue;
        const rawValue = rawValueParts.join('=');
        try {
            cookies[rawName] = decodeURIComponent(rawValue);
        }
        catch {
            cookies[rawName] = rawValue;
        }
    }
    return cookies;
}
function appendSetCookie(res, cookie) {
    const existing = res.getHeader('Set-Cookie');
    if (!existing) {
        res.setHeader('Set-Cookie', cookie);
        return;
    }
    if (Array.isArray(existing)) {
        res.setHeader('Set-Cookie', [...existing, cookie]);
        return;
    }
    res.setHeader('Set-Cookie', [String(existing), cookie]);
}
function setCsrfCookie(res, token, maxAgeSeconds) {
    const attributes = [
        `XSRF-TOKEN=${encodeURIComponent(token)}`,
        'Path=/',
        'HttpOnly',
        'SameSite=Strict',
        `Max-Age=${maxAgeSeconds}`,
    ];
    appendSetCookie(res, attributes.join('; '));
}
function envFlagEnabled(name) {
    const value = process.env[name];
    if (!value)
        return false;
    return ['1', 'true', 'yes', 'on'].includes(value.trim().toLowerCase());
}
async function readRawBody(req) {
    const withCache = req;
    if (typeof withCache.__ccwRawBody === 'string')
        return withCache.__ccwRawBody;
    return new Promise((resolve, reject) => {
        let body = '';
        req.on('data', (chunk) => {
            body += chunk.toString();
        });
        req.on('end', () => {
            withCache.__ccwRawBody = body;
            resolve(body);
        });
        req.on('error', reject);
    });
}
async function readJsonBody(req) {
    const withCache = req;
    if (withCache.body !== undefined)
        return withCache.body;
    const raw = await readRawBody(req);
    if (!raw)
        return undefined;
    try {
        const parsed = JSON.parse(raw);
        withCache.body = parsed;
        return parsed;
    }
    catch {
        return undefined;
    }
}
function extractCsrfTokenFromBody(body) {
    if (!body || typeof body !== 'object')
        return null;
    const record = body;
    const token = record.csrfToken;
    return typeof token === 'string' && token ? token : null;
}
function writeJson(res, status, body) {
    res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' });
    res.end(JSON.stringify(body));
}
export async function csrfValidation(ctx) {
    const { pathname, req, res } = ctx;
    if (!pathname.startsWith('/api/'))
        return true;
    // CSRF is disabled by default for local deployment scenarios.
    // Set CCW_ENABLE_CSRF=1 to enable CSRF protection.
    if (!envFlagEnabled('CCW_ENABLE_CSRF'))
        return true;
    const method = (req.method || 'GET').toUpperCase();
    if (!['POST', 'PUT', 'PATCH', 'DELETE'].includes(method))
        return true;
    // Always allow token acquisition routes and webhook endpoints.
    if (pathname === '/api/auth/token')
        return true;
    if (pathname === '/api/hook')
        return true;
    // Requests authenticated via Authorization header do not require CSRF protection.
    const authorization = getHeaderValue(req.headers.authorization);
    if (authorization && /^Bearer\s+.+$/i.test(authorization))
        return true;
    const headerToken = getHeaderValue(req.headers['x-csrf-token']);
    const cookies = parseCookieHeader(getHeaderValue(req.headers.cookie));
    const cookieToken = cookies['XSRF-TOKEN'];
    let bodyToken = null;
    if (!headerToken && !cookieToken) {
        const body = await readJsonBody(req);
        bodyToken = extractCsrfTokenFromBody(body);
    }
    const token = headerToken || bodyToken || cookieToken || null;
    const sessionId = cookies.ccw_session_id;
    if (!token || !sessionId) {
        writeJson(res, 403, { error: 'CSRF validation failed' });
        return false;
    }
    const tokenManager = getCsrfTokenManager();
    const ok = tokenManager.validateToken(token, sessionId);
    if (!ok) {
        writeJson(res, 403, { error: 'CSRF validation failed' });
        return false;
    }
    const nextToken = tokenManager.generateToken(sessionId);
    res.setHeader('X-CSRF-Token', nextToken);
    setCsrfCookie(res, nextToken, 15 * 60);
    return true;
}
//# sourceMappingURL=csrf-middleware.js.map