/**
 * Security Middleware
 * Implements standard security headers and CORS protection
 * Zero-dependency implementation replacing helmet/cors
 */
import { securityConfig } from '../config/security.js';
const normalizeCorsList = (values, fallback) => {
    const normalized = (values ?? [])
        .map((value) => (typeof value === 'string' ? value.trim() : ''))
        .filter((value) => value !== '');
    return normalized.length > 0 ? normalized : fallback;
};
const getSecurityCorsConfig = () => {
    const config = securityConfig;
    return config?.cors ?? {};
};
const resolveCorsOrigin = () => {
    const origins = normalizeCorsList(getSecurityCorsConfig().origins, ['*']);
    return origins.includes('*') ? '*' : origins;
};
const DEFAULT_OPTIONS = {
    hsts: {
        maxAge: 15552000, // 180 days
        includeSubDomains: true,
        preload: false,
    },
    frameguard: {
        action: 'SAMEORIGIN',
    },
    cors: {
        origin: resolveCorsOrigin(),
        methods: normalizeCorsList(getSecurityCorsConfig().methods, [
            'GET',
            'HEAD',
            'PUT',
            'PATCH',
            'POST',
            'DELETE',
        ]),
        allowedHeaders: normalizeCorsList(getSecurityCorsConfig().allowedHeaders, [
            'Content-Type',
            'Authorization',
            'X-Requested-With',
            'X-CSRF-Token',
        ]),
        credentials: getSecurityCorsConfig().credentials,
        maxAge: getSecurityCorsConfig().maxAge,
    },
};
const mergeSecurityOptions = (options) => {
    return {
        hsts: { ...DEFAULT_OPTIONS.hsts, ...options.hsts },
        frameguard: { ...DEFAULT_OPTIONS.frameguard, ...options.frameguard },
        cors: { ...DEFAULT_OPTIONS.cors, ...options.cors },
        csp: options.csp ?? DEFAULT_OPTIONS.csp,
    };
};
function applyHsts(res, hsts) {
    if (!hsts)
        return;
    let headerValue = `max-age=${hsts.maxAge}`;
    if (hsts.includeSubDomains ?? false)
        headerValue += '; includeSubDomains';
    if (hsts.preload ?? false)
        headerValue += '; preload';
    res.setHeader('Strict-Transport-Security', headerValue);
}
function applyFrameguard(res, frameguard) {
    if (!frameguard)
        return;
    res.setHeader('X-Frame-Options', frameguard.action ?? 'SAMEORIGIN');
}
function applyNoSniff(res) {
    res.setHeader('X-Content-Type-Options', 'nosniff');
}
function applyReferrerPolicy(res) {
    res.setHeader('Referrer-Policy', 'no-referrer');
}
function applyCsp(res, csp) {
    if (!csp?.directives)
        return;
    const headerValue = Object.entries(csp.directives)
        .map(([key, values]) => `${key} ${values.join(' ')}`)
        .join('; ');
    res.setHeader('Content-Security-Policy', headerValue);
}
function applyCors(req, res, cors) {
    if (!cors)
        return false;
    const originHeader = req.getHeader('origin');
    const origin = typeof originHeader === 'string' ? originHeader : undefined;
    let allowedOrigin = cors.origin;
    if (Array.isArray(cors.origin)) {
        if (origin === undefined || !cors.origin.includes(origin)) {
            allowedOrigin = undefined;
        }
        else {
            allowedOrigin = origin;
        }
    }
    if (allowedOrigin !== null && allowedOrigin !== undefined) {
        res.setHeader('Access-Control-Allow-Origin', allowedOrigin ?? '*');
    }
    if (cors.methods) {
        res.setHeader('Access-Control-Allow-Methods', cors.methods.join(', '));
    }
    if (cors.allowedHeaders) {
        res.setHeader('Access-Control-Allow-Headers', cors.allowedHeaders.join(', '));
    }
    if (cors.credentials !== undefined) {
        res.setHeader('Access-Control-Allow-Credentials', cors.credentials ? 'true' : 'false');
    }
    if (cors.maxAge !== undefined) {
        res.setHeader('Access-Control-Max-Age', cors.maxAge.toString());
    }
    // Handle Preflight
    if (req.getMethod() === 'OPTIONS') {
        res.setStatus(204);
        return true;
    }
    return false;
}
export const SecurityMiddleware = Object.freeze({
    /**
     * Create security middleware with options
     */
    create(options = {}) {
        const config = mergeSecurityOptions(options);
        return async (req, res, next) => {
            applyHsts(res, config.hsts);
            applyFrameguard(res, config.frameguard);
            applyNoSniff(res);
            applyReferrerPolicy(res);
            applyCsp(res, config.csp);
            const preflightHandled = applyCors(req, res, config.cors);
            if (preflightHandled)
                return;
            await next();
        };
    },
});
