const ADMIN_HREF = '/settings?tab=users';
const API_KEYS_HREF = '/settings?tab=api-keys';
const FAILED_LOGIN_WARN_THRESHOLD = 20;
const LEVEL_COPY = {
    secure: {
        label: 'Secure',
        description: 'Core protections are configured. Keep MFA coverage and API keys under review.',
    },
    needs_attention: {
        label: 'Needs attention',
        description: 'Some recommended controls are missing. Review the warnings below.',
    },
    at_risk: {
        label: 'At risk',
        description: 'Critical protections are missing or misconfigured. Resolve these before running in production.',
    },
    unknown: {
        label: 'Unknown',
        description: 'Security state could not be fully determined. Some services may be unavailable.',
    },
};
export function computeSecurityStatus(input) {
    const checks = [];
    const warnings = [];
    // ── MFA coverage ──────────────────────────────────────────────────────────
    if (input.users.totalAdmins === 0) {
        checks.push({ key: 'mfa', label: 'MFA coverage', status: 'unknown', value: 'No admins' });
    }
    else {
        const allCovered = input.users.adminsWithoutMfa === 0;
        const mfaStatus = allCovered ? 'pass' : input.settings.mfa.required ? 'fail' : 'warning';
        checks.push({
            key: 'mfa',
            label: 'MFA coverage',
            status: mfaStatus,
            value: `${input.users.withMfa} of ${input.users.totalAdmins} admins`,
            actionHref: ADMIN_HREF,
        });
        if (input.settings.mfa.required && input.users.adminsWithoutMfa > 0) {
            warnings.push({
                id: 'mfa-required-gap',
                severity: 'critical',
                category: 'auth',
                title: 'Admins without MFA while MFA is required',
                description: `${input.users.adminsWithoutMfa} admin account(s) have not set up MFA. They must enroll within the grace period.`,
                actionLabel: 'Review users',
                actionHref: ADMIN_HREF,
            });
        }
        else if (!input.settings.mfa.required && input.isProduction) {
            warnings.push({
                id: 'mfa-not-required',
                severity: 'warning',
                category: 'auth',
                title: 'MFA is not required in production',
                description: 'Requiring MFA for admins significantly reduces account-takeover risk.',
            });
        }
    }
    // ── Active sessions (informational) ─────────────────────────────────────────
    checks.push({
        key: 'sessions',
        label: 'Active admin sessions',
        status: 'pass',
        value: String(input.activeSessions),
        actionHref: '/settings?tab=security&section=sessions',
    });
    // ── API keys ────────────────────────────────────────────────────────────────
    const keyRisk = input.apiKeys.broadScope > 0
        ? 'fail'
        : input.apiKeys.withoutExpiration > 0 || input.apiKeys.staleUnused90d > 0
            ? 'warning'
            : 'pass';
    checks.push({
        key: 'api_keys',
        label: 'High-risk API keys',
        status: keyRisk === 'pass' ? 'pass' : keyRisk,
        value: keyRisk === 'pass' ? `${input.apiKeys.active} active` : describeKeyRisk(input.apiKeys),
        actionHref: API_KEYS_HREF,
    });
    if (input.apiKeys.broadScope > 0) {
        warnings.push({
            id: 'api-keys-broad',
            severity: 'warning',
            category: 'api_keys',
            title: 'API keys with broad scopes',
            description: `${input.apiKeys.broadScope} key(s) hold admin or full read/write scope. Scope keys to the minimum required.`,
            actionLabel: 'Review API keys',
            actionHref: API_KEYS_HREF,
        });
    }
    if (input.apiKeys.withoutExpiration > 0) {
        warnings.push({
            id: 'api-keys-no-expiry',
            severity: 'info',
            category: 'api_keys',
            title: 'API keys without expiration',
            description: `${input.apiKeys.withoutExpiration} key(s) never expire. Consider setting an expiry.`,
            actionLabel: 'Review API keys',
            actionHref: API_KEYS_HREF,
        });
    }
    // ── Failed logins (24h) ──────────────────────────────────────────────────────
    const loginStatus = input.failedLogins24h >= FAILED_LOGIN_WARN_THRESHOLD ? 'warning' : 'pass';
    checks.push({
        key: 'failed_logins',
        label: 'Failed logins (24h)',
        status: loginStatus,
        value: String(input.failedLogins24h),
    });
    if (loginStatus === 'warning') {
        warnings.push({
            id: 'failed-logins-spike',
            severity: 'warning',
            category: 'auth',
            title: 'Elevated failed login attempts',
            description: `${input.failedLogins24h} failed logins in the last 24h. This may indicate a brute-force attempt.`,
        });
    }
    // ── CSRF ──────────────────────────────────────────────────────────────────────
    checks.push({
        key: 'csrf',
        label: 'CSRF protection',
        status: input.csrfEnabled ? 'pass' : 'fail',
        value: input.csrfEnabled ? 'Enabled' : 'Disabled',
    });
    if (!input.csrfEnabled) {
        warnings.push({
            id: 'csrf-disabled',
            severity: 'critical',
            category: 'access',
            title: 'CSRF protection disabled',
            description: 'State-changing requests are not CSRF-protected. This exposes the admin to cross-site request forgery.',
        });
    }
    // ── Rate limiting ──────────────────────────────────────────────────────────────
    const distributed = input.secrets.rateLimitBackend === 'distributed';
    checks.push({
        key: 'rate_limit',
        label: 'Rate limiting',
        status: distributed ? 'pass' : 'warning',
        value: distributed ? 'Distributed (Upstash)' : 'In-memory',
    });
    if (!distributed && input.isProduction) {
        warnings.push({
            id: 'rate-limit-in-memory',
            severity: 'warning',
            category: 'rate_limit',
            title: 'Rate limiting is in-memory',
            description: 'Without Upstash/Redis, rate limits are per-process and ineffective across serverless invocations. Configure UPSTASH_REDIS_REST_URL.',
        });
    }
    // ── Upload validation ───────────────────────────────────────────────────────
    checks.push({
        key: 'uploads',
        label: 'Upload validation',
        status: input.uploadValidation ? 'pass' : 'warning',
        value: input.uploadValidation ? 'MIME + magic bytes + SVG sanitization' : 'Unknown',
    });
    // ── Secrets / encryption ─────────────────────────────────────────────────────
    checks.push({
        key: 'cms_secret',
        label: 'CMS secret',
        status: input.secrets.cmsSecret,
        value: secretValueLabel(input.secrets.cmsSecret),
    });
    if (input.secrets.cmsSecret === 'fail') {
        warnings.push({
            id: 'cms-secret-missing',
            severity: 'critical',
            category: 'secrets',
            title: 'CMS_SECRET is missing or weak',
            description: 'Session JWTs cannot be signed securely. Set a 32+ character CMS_SECRET.',
        });
    }
    checks.push({
        key: 'encryption_key',
        label: 'Encryption key',
        status: input.secrets.encryptionKey,
        value: secretValueLabel(input.secrets.encryptionKey),
    });
    if (input.secrets.encryptionKey === 'fail') {
        warnings.push({
            id: 'encryption-key',
            severity: input.isProduction ? 'critical' : 'warning',
            category: 'secrets',
            title: 'Encryption key not configured',
            description: 'CMS_ENCRYPTION_KEY (64 hex chars) is required to encrypt secrets at rest (webhook secrets, OAuth tokens, TOTP secrets).',
        });
    }
    // ── Audit logging ──────────────────────────────────────────────────────────────
    checks.push({
        key: 'audit',
        label: 'Audit logging',
        status: input.auditEnabled ? 'pass' : 'warning',
        value: input.auditEnabled ? 'Enabled' : 'Disabled',
        actionHref: '/settings?tab=security&section=audit',
    });
    // ── Roll up level + score ────────────────────────────────────────────────────
    const hasCritical = warnings.some((w) => w.severity === 'critical');
    const hasWarning = warnings.some((w) => w.severity === 'warning');
    const unknown = input.secrets.cmsSecret === 'unknown';
    let level;
    if (unknown)
        level = 'unknown';
    else if (hasCritical)
        level = 'at_risk';
    else if (hasWarning)
        level = 'needs_attention';
    else
        level = 'secure';
    let score = 100;
    for (const w of warnings) {
        score -= w.severity === 'critical' ? 30 : w.severity === 'warning' ? 12 : 3;
    }
    score = Math.max(0, Math.min(100, score));
    return {
        status: level,
        score,
        label: LEVEL_COPY[level].label,
        description: LEVEL_COPY[level].description,
        checks,
        warnings,
        lastCheckedAt: new Date().toISOString(),
    };
}
function describeKeyRisk(k) {
    const parts = [];
    if (k.broadScope > 0)
        parts.push(`${k.broadScope} broad-scope`);
    if (k.withoutExpiration > 0)
        parts.push(`${k.withoutExpiration} no expiry`);
    if (k.staleUnused90d > 0)
        parts.push(`${k.staleUnused90d} stale`);
    return parts.join(', ') || 'OK';
}
function secretValueLabel(status) {
    switch (status) {
        case 'pass':
            return 'Configured';
        case 'warning':
            return 'Not configured';
        case 'fail':
            return 'Missing / invalid';
        default:
            return 'Unknown';
    }
}
//# sourceMappingURL=status.js.map