export const DEFAULT_SECURITY_SETTINGS = {
    mfa: { required: false, gracePeriodDays: 7 },
    session: { maxConcurrentSessions: 5 },
};
export const MFA_GRACE_PERIOD_OPTIONS = [0, 1, 7, 14, 30];
export const MAX_CONCURRENT_SESSION_OPTIONS = [0, 3, 5, 10];
function toInt(value, fallback) {
    const n = typeof value === 'number' ? value : Number(value);
    return Number.isFinite(n) ? Math.trunc(n) : fallback;
}
/** Read the `security` blob off a settings document, coercing to a safe shape. */
export function parseSecuritySettings(blob) {
    const root = (blob && typeof blob === 'object' ? blob : {}) ?? {};
    const sec = (root.security && typeof root.security === 'object'
        ? root.security
        : {});
    const mfa = (sec.mfa && typeof sec.mfa === 'object' ? sec.mfa : {});
    const session = (sec.session && typeof sec.session === 'object' ? sec.session : {});
    return {
        mfa: {
            required: Boolean(mfa.required),
            gracePeriodDays: clampGrace(toInt(mfa.gracePeriodDays, DEFAULT_SECURITY_SETTINGS.mfa.gracePeriodDays)),
            requiredAt: typeof mfa.requiredAt === 'string' ? mfa.requiredAt : undefined,
        },
        session: {
            maxConcurrentSessions: clampSessions(toInt(session.maxConcurrentSessions, DEFAULT_SECURITY_SETTINGS.session.maxConcurrentSessions)),
        },
        updatedAt: typeof sec.updatedAt === 'string' ? sec.updatedAt : undefined,
        updatedBy: typeof sec.updatedBy === 'string' ? sec.updatedBy : undefined,
    };
}
function clampGrace(n) {
    if (n < 0)
        return 0;
    if (n > 365)
        return 365;
    return n;
}
function clampSessions(n) {
    if (n < 0)
        return 0;
    if (n > 100)
        return 100;
    return n;
}
/** Validate an incoming settings payload. Pure — no DB / lockout checks here. */
export function validateSecuritySettings(settings) {
    const issues = [];
    const grace = settings.mfa.gracePeriodDays;
    if (!Number.isInteger(grace) || grace < 0 || grace > 365) {
        issues.push({
            field: 'mfaGracePeriodDays',
            message: 'MFA grace period must be a whole number of days between 0 and 365.',
            severity: 'error',
        });
    }
    const max = settings.session.maxConcurrentSessions;
    if (!Number.isInteger(max) || max < 0 || max > 100) {
        issues.push({
            field: 'maxConcurrentSessions',
            message: 'Maximum concurrent sessions must be a whole number between 0 (unlimited) and 100.',
            severity: 'error',
        });
    }
    return issues;
}
/**
 * Lockout-safe MFA enable check. Enabling org-wide MFA while the acting admin
 * has no TOTP configured would lock them (and possibly everyone) out. Callers
 * MUST run this server-side before persisting `mfa.required = true`.
 */
export function mfaEnableLockoutError(input) {
    if (!input.enabling)
        return null;
    if (!input.viewerHasMfa) {
        return 'Set up MFA for your own account before requiring it for everyone — otherwise you would lock yourself out.';
    }
    return null;
}
const IPV4_OCTET = '(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)';
const IPV4_RE = new RegExp(`^${IPV4_OCTET}(\\.${IPV4_OCTET}){3}$`);
/**
 * Validate a single allowlist entry: an IPv4/IPv6 literal or a CIDR range.
 * Standalone (does not import the matcher's private helpers) so it can run in
 * the client bundle for inline form validation.
 */
export function validateIpRange(entry) {
    const value = entry.trim();
    if (!value)
        return false;
    const [addr, maskStr, ...rest] = value.split('/');
    if (rest.length > 0 || addr === undefined)
        return false;
    const isV6 = addr.includes(':');
    if (maskStr !== undefined) {
        if (!/^\d{1,3}$/.test(maskStr))
            return false;
        const mask = Number(maskStr);
        if (isV6 ? mask > 128 : mask > 32)
            return false;
    }
    return isV6 ? isValidIpv6(addr) : IPV4_RE.test(addr);
}
function isValidIpv6(addr) {
    // Reject IPv4-mapped + multiple `::`; otherwise validate 1–8 hextet groups.
    if (addr.includes('.'))
        return false;
    const halves = addr.split('::');
    if (halves.length > 2)
        return false;
    const groups = addr
        .replace('::', ':')
        .split(':')
        .filter((g) => g.length > 0);
    if (groups.length === 0 && halves.length !== 2)
        return false;
    if (groups.length > 8)
        return false;
    return groups.every((g) => /^[0-9a-fA-F]{1,4}$/.test(g));
}
//# sourceMappingURL=settings.js.map