// tools/suggest-tooling/resilience-catalog.ts — SPEC-034
// Generates resilience library and health check recommendations
// based on project language, stack, and detected patterns.
import { getJvmVersion, getRustVersion } from '../../config/version-defaults.js';
const RESILIENCE_LIBRARY_BY_LANG = {
    nodejs: {
        name: 'cockatiel',
        install: 'npm install cockatiel',
        note: 'Inspired by Polly .NET. Composable policies: circuit breaker, retry, timeout, bulkhead.',
    },
    typescript: {
        name: 'cockatiel',
        install: 'npm install cockatiel',
        note: 'Inspired by Polly .NET. Composable policies: circuit breaker, retry, timeout, bulkhead.',
    },
    javascript: {
        name: 'cockatiel',
        install: 'npm install cockatiel',
        note: 'Inspired by Polly .NET. Composable policies: circuit breaker, retry, timeout, bulkhead.',
    },
    python: {
        name: 'stamina',
        install: 'pip install stamina',
        note: 'Modern retry with exponential backoff and jitter. Integrates with structlog for observability.',
    },
    java: {
        name: 'resilience4j',
        install: `implementation("io.github.resilience4j:resilience4j-spring-boot3:${getJvmVersion('resilience4j-spring-boot3')}")`,
        note: 'Circuit breaker, retry, rate limiter, bulkhead with Spring Boot auto-configuration.',
    },
    kotlin: {
        name: 'resilience4j',
        install: `implementation("io.github.resilience4j:resilience4j-kotlin:${getJvmVersion('resilience4j-kotlin')}")`,
        note: 'Kotlin-friendly coroutine extension functions for all Resilience4j patterns.',
    },
    dotnet: {
        name: 'Polly',
        install: 'dotnet add package Polly.Extensions.Http',
        note: 'The de-facto standard for .NET. Fluent API for all resilience patterns.',
    },
    go: {
        name: 'failsafe-go',
        install: 'go get github.com/failsafe-go/failsafe-go',
        note: 'Composable policy-based resilience. Circuit breaker, retry, timeout, bulkhead.',
    },
    rust: {
        name: 'tokio-retry',
        install: `tokio-retry = "${getRustVersion('tokio-retry')}"`,
        note: 'Retry strategies: Exponential, Fixed, Fibonacci. Pairs with tokio::time::timeout for timeouts.',
    },
};
const EXTERNAL_CALL_INDICATORS = [
    'fetch',
    'axios',
    'httpclient',
    'httprequestmessage',
    'requests',
    'httpx',
    'http.get',
    'net/http',
    'reqwest',
    'surf',
    'resttemplate',
    'webclient',
    'grpc',
    'sdk',
    'client',
];
const RESILIENCE_LIBRARY_INDICATORS = [
    'resilience4j',
    'polly',
    'opossum',
    'cockatiel',
    'p-retry',
    'stamina',
    'tenacity',
    'pybreaker',
    'failsafe-go',
    'gobreaker',
    'tokio-retry',
    'hystrix',
];
/**
 * Returns true when the project makes external HTTP/gRPC calls but has no resilience library.
 */
export function needsResilienceRecommendation(knowledge) {
    const stackLower = knowledge.stack.map((s) => s.toLowerCase());
    const conventionsStr = JSON.stringify(knowledge.conventions).toLowerCase();
    const combined = [...stackLower, conventionsStr].join(' ');
    const hasExternalCalls = EXTERNAL_CALL_INDICATORS.some((indicator) => combined.includes(indicator));
    const hasResilienceLib = RESILIENCE_LIBRARY_INDICATORS.some((lib) => combined.includes(lib.toLowerCase()));
    return hasExternalCalls && !hasResilienceLib;
}
/**
 * Returns true when the project appears to be a microservice without health checks.
 */
export function needsHealthCheckRecommendation(knowledge) {
    const stackLower = knowledge.stack.map((s) => s.toLowerCase());
    const hasMicroserviceSignals = stackLower.some((s) => ['kubernetes', 'k8s', 'docker', 'helm', 'istio', 'grpc'].some((kw) => s.includes(kw)));
    if (!hasMicroserviceSignals) {
        return false;
    }
    // Check if health check is already mentioned
    const conventionsStr = JSON.stringify(knowledge.conventions).toLowerCase();
    const combined = [...stackLower, conventionsStr].join(' ');
    const hasHealthCheck = ['/health', '/healthz', '/ready', '/readyz', 'liveness', 'readiness'].some((kw) => combined.includes(kw));
    return !hasHealthCheck;
}
/**
 * Returns true when project has non-distributed rate limiting in a multi-instance deployment.
 */
export function hasDistributedRateLimitRisk(knowledge) {
    const stackLower = knowledge.stack.map((s) => s.toLowerCase());
    const hasLocalRateLimit = stackLower.some((s) => ['express-rate-limit', 'slowapi', 'golang.org/x/time/rate'].some((lib) => s.includes(lib)));
    const hasMultiInstance = stackLower.some((s) => ['kubernetes', 'k8s', 'ecs', 'replicas', 'docker-compose'].some((kw) => s.includes(kw)));
    return hasLocalRateLimit && hasMultiInstance;
}
/**
 * Build resilience tooling recommendation lines.
 */
export function buildResilienceToolingLines(knowledge) {
    const lines = [];
    if (needsResilienceRecommendation(knowledge)) {
        const rec = RESILIENCE_LIBRARY_BY_LANG[knowledge.language];
        if (rec) {
            lines.push('## Resilience Library Recommendation', '', '**No resilience library detected.** External service calls without circuit breaker/retry are a single point of failure.', '', `**Recommended:** \`${rec.name}\``, `**Why:** ${rec.note}`, '', `**Install:** \`${rec.install}\``, '', '**Patterns to implement:**', '- **Circuit breaker**: open after 50% failures in 10 calls, stay open for 60s', '- **Retry with jitter**: max 3 attempts, exponential backoff (500ms, 1s, 2s) + random jitter', '- **Timeout**: connect_timeout 2s, read_timeout 10s — never rely on OS timeout (30s+)', '- **Fallback**: define what to return when circuit is open (cached value, degraded mode, or 503)', '');
        }
    }
    if (hasDistributedRateLimitRisk(knowledge)) {
        lines.push('## Rate Limiting Warning: Local-Only (Not Distributed)', '', '**Risk**: In-memory rate limiting is not effective in multi-instance deployments.', 'With N instances, each user can effectively make N times the configured per-IP limit.', '', '**Fix:** Switch to a Redis-backed distributed rate limiter:', '- Node.js: `rate-limiter-flexible` with Redis store', '- Python: `flask-limiter` with Redis backend', '- Go: `github.com/go-redis/redis_rate` (Lua-based sliding window)', '');
    }
    if (needsHealthCheckRecommendation(knowledge)) {
        lines.push('## Health Check Endpoints Recommendation', '', '**Kubernetes deployment detected without health check endpoints.**', 'Without liveness/readiness probes, the load balancer cannot detect unhealthy pods.', '', '**Add two endpoints:**', '', '### Liveness: `GET /healthz`', '- Returns `200 OK` with `{"status":"ok"}` when process is alive', '- Must be ultra-lightweight: NO database queries, NO external calls', '- Response time must be < 100ms (Kubernetes default timeout: 1s)', '- Exclude from rate limiting and authentication', '', '### Readiness: `GET /ready`', '- Returns `200 OK` when all dependencies are available', '- Returns `503 Service Unavailable` when any dependency fails', '- Checks: database connection (`SELECT 1`), Redis (`PING`), critical upstream services', '- Kubernetes uses this to decide whether to send traffic to this pod', '', '**Kubernetes probe configuration example:**', '```yaml', 'livenessProbe:', '  httpGet: { path: /healthz, port: 8080 }', '  initialDelaySeconds: 10', '  periodSeconds: 15', 'readinessProbe:', '  httpGet: { path: /ready, port: 8080 }', '  initialDelaySeconds: 5', '  periodSeconds: 10', '```', '');
    }
    return lines;
}
//# sourceMappingURL=resilience-catalog.js.map