/**
 * Health Checker
 *
 * Performs health checks against deployed endpoints.
 *
 * @module scanners/deploy/health-checker
 */
import { logger } from "../../logger.js";
const DEFAULT_TIMEOUT = 10000;
const DEFAULT_HEALTH_ENDPOINTS = ["/", "/api/health", "/health", "/healthz"];
/**
 * Check a single health endpoint
 */
export async function checkHealth(baseUrl, endpoint, options = {}) {
    const url = endpoint.startsWith("http") ? endpoint : `${baseUrl}${endpoint}`;
    const startTime = Date.now();
    try {
        const response = await fetch(url, {
            method: "GET",
            signal: AbortSignal.timeout(options.timeout || DEFAULT_TIMEOUT),
            headers: {
                "User-Agent": "Vaspera-Health-Check/1.0",
            },
        });
        const responseTime = Date.now() - startTime;
        let status;
        if (response.ok) {
            status = responseTime < 500 ? "healthy" : "degraded";
        }
        else if (response.status >= 500) {
            status = "unhealthy";
        }
        else {
            status = "degraded";
        }
        return {
            endpoint,
            status,
            statusCode: response.status,
            responseTime,
            checkedAt: new Date().toISOString(),
        };
    }
    catch (error) {
        const responseTime = Date.now() - startTime;
        const errorMessage = error.message;
        return {
            endpoint,
            status: errorMessage.includes("timeout") ? "timeout" : "unhealthy",
            responseTime,
            error: errorMessage,
            checkedAt: new Date().toISOString(),
        };
    }
}
/**
 * Check multiple health endpoints
 */
export async function checkAllHealth(baseUrl, endpoints = DEFAULT_HEALTH_ENDPOINTS, options = {}) {
    logger.info("deploy.health_check_started", {
        baseUrl,
        endpoints: endpoints.length,
    });
    let results;
    if (options.parallel !== false) {
        results = await Promise.all(endpoints.map((endpoint) => checkHealth(baseUrl, endpoint, options)));
    }
    else {
        results = [];
        for (const endpoint of endpoints) {
            results.push(await checkHealth(baseUrl, endpoint, options));
        }
    }
    const healthy = results.filter((r) => r.status === "healthy").length;
    const degraded = results.filter((r) => r.status === "degraded").length;
    const unhealthy = results.filter((r) => r.status === "unhealthy" || r.status === "timeout").length;
    logger.info("deploy.health_check_completed", {
        healthy,
        degraded,
        unhealthy,
        total: results.length,
    });
    return results;
}
/**
 * Run a smoke test
 */
export async function runSmokeTest(baseUrl, test) {
    const url = test.endpoint.startsWith("http")
        ? test.endpoint
        : `${baseUrl}${test.endpoint}`;
    const startTime = Date.now();
    try {
        const response = await fetch(url, {
            method: test.method || "GET",
            headers: {
                "Content-Type": "application/json",
                "User-Agent": "Vaspera-Smoke-Test/1.0",
                ...test.headers,
            },
            body: test.body ? JSON.stringify(test.body) : undefined,
            signal: AbortSignal.timeout(test.timeout || DEFAULT_TIMEOUT),
        });
        const responseTime = Date.now() - startTime;
        const responseBody = await response.text();
        let responseJson;
        try {
            responseJson = JSON.parse(responseBody);
        }
        catch {
            responseJson = responseBody;
        }
        // Check assertions
        const assertionResults = [];
        let allPassed = response.status === (test.expectedStatus || 200);
        if (test.assertions) {
            for (const assertion of test.assertions) {
                let actual;
                let passed = false;
                switch (assertion.type) {
                    case "status":
                        actual = response.status;
                        passed = evaluateAssertion(actual, assertion.operator, assertion.value);
                        break;
                    case "latency":
                        actual = responseTime;
                        passed = evaluateAssertion(actual, assertion.operator, assertion.value);
                        break;
                    case "header":
                        actual = response.headers.get(assertion.path || "") || "";
                        passed = evaluateAssertion(actual, assertion.operator, assertion.value);
                        break;
                    case "body":
                        actual = getJsonPath(responseJson, assertion.path || "");
                        passed = evaluateAssertion(actual, assertion.operator, assertion.value);
                        break;
                }
                assertionResults.push({
                    type: assertion.type,
                    expected: assertion.value,
                    actual,
                    passed,
                });
                if (!passed)
                    allPassed = false;
            }
        }
        return {
            name: test.name,
            endpoint: test.endpoint,
            passed: allPassed,
            statusCode: response.status,
            responseTime,
            assertions: assertionResults,
        };
    }
    catch (error) {
        return {
            name: test.name,
            endpoint: test.endpoint,
            passed: false,
            statusCode: 0,
            responseTime: Date.now() - startTime,
            assertions: [],
            error: error.message,
        };
    }
}
/**
 * Run all smoke tests
 */
export async function runAllSmokeTests(baseUrl, tests) {
    logger.info("deploy.smoke_tests_started", {
        baseUrl,
        tests: tests.length,
    });
    const results = [];
    for (const test of tests) {
        const result = await runSmokeTest(baseUrl, test);
        results.push(result);
        logger.debug("deploy.smoke_test_result", {
            name: test.name,
            passed: result.passed,
            responseTime: result.responseTime,
        });
    }
    const passed = results.filter((r) => r.passed).length;
    logger.info("deploy.smoke_tests_completed", {
        passed,
        failed: results.length - passed,
        total: results.length,
    });
    return results;
}
/**
 * Evaluate an assertion
 */
function evaluateAssertion(actual, operator, expected) {
    switch (operator) {
        case "eq":
            return actual === expected;
        case "ne":
            return actual !== expected;
        case "gt":
            return Number(actual) > Number(expected);
        case "lt":
            return Number(actual) < Number(expected);
        case "contains":
            return String(actual).includes(String(expected));
        case "matches":
            return new RegExp(String(expected)).test(String(actual));
        default:
            return false;
    }
}
/**
 * Get value from JSON path
 */
function getJsonPath(obj, path) {
    if (!path)
        return String(obj);
    const parts = path.split(".");
    let current = obj;
    for (const part of parts) {
        if (current === null || current === undefined)
            return "";
        if (typeof current === "object") {
            current = current[part];
        }
        else {
            return "";
        }
    }
    if (typeof current === "number")
        return current;
    return String(current ?? "");
}
/**
 * Calculate health score (0-100)
 */
export function calculateHealthScore(results) {
    if (results.length === 0)
        return 50; // Neutral if no endpoints
    let score = 0;
    for (const result of results) {
        switch (result.status) {
            case "healthy":
                score += 100;
                break;
            case "degraded":
                score += 60;
                break;
            case "unhealthy":
                score += 20;
                break;
            case "timeout":
                score += 0;
                break;
        }
    }
    return Math.round(score / results.length);
}
/**
 * Calculate smoke test score (0-100)
 */
export function calculateSmokeTestScore(results) {
    if (results.length === 0)
        return 50; // Neutral if no tests
    const passed = results.filter((r) => r.passed).length;
    return Math.round((passed / results.length) * 100);
}
//# sourceMappingURL=health-checker.js.map