import nullLogger from "../utils/nullLogger.js";
import CachedValue from "../utils/CachedValue.js";
/**
 * Default TTL for health check cache in lamport ticks.
 * Invalidates after 50 patches have been observed.
 */
const DEFAULT_CACHE_TTL_TICKS = 50;
/**
 * Health status constants.
 */
export const HealthStatus = {
    HEALTHY: 'healthy',
    DEGRADED: 'degraded',
    UNHEALTHY: 'unhealthy',
};
/**
 * Service for performing health checks on the graph system.
 *
 * Follows hexagonal architecture by depending on ports, not adapters.
 * Provides K8s-style probes (liveness vs readiness) and detailed component health.
 *
 * @example
 * const healthService = new HealthCheckService({
 *   persistence,
 *   cacheTtlTicks: 100,
 *   logger,
 * });
 *
 * // K8s liveness probe - am I running?
 * const alive = await healthService.isAlive(currentLamport);
 *
 * // K8s readiness probe - can I serve requests?
 * const ready = await healthService.isReady(currentLamport);
 *
 * // Detailed health breakdown
 * const health = await healthService.getHealth(currentLamport);
 * console.log(health.status); // 'healthy' | 'degraded' | 'unhealthy'
 */
export default class HealthCheckService {
    _persistence;
    _logger;
    _indexReader; // nosemgrep: ts-no-like-types -- 0025C
    _healthCache;
    /**
     * Creates a HealthCheckService instance.
     */
    constructor({ persistence, cacheTtlTicks = DEFAULT_CACHE_TTL_TICKS, logger = nullLogger, }) {
        this._persistence = persistence;
        this._logger = logger;
        this._indexReader = null;
        this._healthCache = new CachedValue({
            ttlTicks: cacheTtlTicks,
            compute: () => this._computeHealth(),
        });
    }
    /**
     * Sets the index reader for index health checks.
     * Call this when an index is loaded.
     *
     * @param reader - The index reader, or null to clear
     */
    setIndexReader(reader) {
        this._indexReader = reader;
        this._healthCache.invalidate();
    }
    /**
     * K8s-style liveness probe: Is the service running?
     *
     * Returns true if the repository is accessible.
     * A failed liveness check typically triggers a container restart.
     */
    async isAlive(currentTick) {
        const health = await this.getHealth(currentTick);
        // Alive if repository is reachable (even if degraded)
        return health.components.repository.status !== HealthStatus.UNHEALTHY;
    }
    /**
     * K8s-style readiness probe: Can the service serve requests?
     *
     * Returns true if all critical components are healthy.
     * A failed readiness check removes the pod from load balancer.
     */
    async isReady(currentTick) {
        const health = await this.getHealth(currentTick);
        return health.status === HealthStatus.HEALTHY;
    }
    /**
     * Gets detailed health information for all components.
     *
     * Results are cached for the configured tick TTL to prevent
     * excessive health check calls under load.
     */
    async getHealth(currentTick) {
        const { value, cachedAtTick, fromCache } = await this._healthCache.getWithMetadata(currentTick);
        const result = value;
        if (fromCache && cachedAtTick > 0) {
            return { ...result, cachedAtTick };
        }
        // Log only for fresh computations
        if (!fromCache) {
            this._logger.debug('Health check completed', {
                operation: 'getHealth',
                status: result.status,
                repositoryStatus: result.components.repository.status,
                indexStatus: result.components.index.status,
            });
        }
        return result;
    }
    /**
     * Computes health by checking all components.
     * This is called by CachedValue when the cache is stale.
     */
    async _computeHealth() {
        const repositoryHealth = await this._checkRepository();
        const indexHealth = this._checkIndex();
        const status = this._computeOverallStatus(repositoryHealth, indexHealth);
        return {
            status,
            components: {
                repository: repositoryHealth,
                index: indexHealth,
            },
        };
    }
    /**
     * Checks repository health by pinging the persistence layer.
     */
    async _checkRepository() {
        try {
            const pingResult = await this._persistence.ping();
            return {
                status: pingResult.ok ? HealthStatus.HEALTHY : HealthStatus.UNHEALTHY,
                latencyMs: Math.round(pingResult.latencyMs * 100) / 100,
            };
        }
        catch (err) {
            this._logger.warn('Repository ping failed', {
                operation: 'checkRepository',
                error: err instanceof Error ? err.message : String(err),
            });
            return {
                status: HealthStatus.UNHEALTHY,
                latencyMs: 0,
            };
        }
    }
    /**
     * Checks index health based on loaded state and shard count.
     */
    _checkIndex() {
        if (!this._indexReader) {
            return {
                status: HealthStatus.DEGRADED,
                loaded: false,
            };
        }
        const shardCount = this._indexReader.shardOids?.size ?? 0;
        return {
            status: HealthStatus.HEALTHY,
            loaded: true,
            shardCount,
        };
    }
    /**
     * Computes overall health status from component health.
     */
    _computeOverallStatus(repositoryHealth, indexHealth) {
        if (repositoryHealth.status === HealthStatus.UNHEALTHY) {
            return HealthStatus.UNHEALTHY;
        }
        if (indexHealth.status === HealthStatus.DEGRADED) {
            return HealthStatus.DEGRADED;
        }
        return HealthStatus.HEALTHY;
    }
}
