import { Resource } from "../resource.js";
import { Secret as SecretClass } from "../secret.js";
import { logger } from "../util/logger.js";
import { CloudflareApiError, handleApiError } from "./api-error.js";
import { extractCloudflareResult, } from "./api-response.js";
import { createCloudflareApi, } from "./api.js";
/**
 * Delete a health check from Cloudflare
 * @internal
 */
export async function deleteHealthCheck(api, zoneId, healthCheckId, id) {
    try {
        const deleteResponse = await api.delete(`/zones/${zoneId}/healthchecks/${healthCheckId}`);
        if (!deleteResponse.ok && deleteResponse.status !== 404) {
            await handleApiError(deleteResponse, "delete", "health check", id);
        }
    }
    catch (error) {
        logger.error(`Error deleting Health Check ${id}:`, error);
        throw error;
    }
}
/**
 * Represents a Cloudflare Health Check for monitoring origin server availability.
 *
 * Health Checks monitor the availability of your origin servers and can be used
 * with Load Balancers to automatically route traffic away from unhealthy origins.
 *
 * @example
 * // Create a basic HTTP health check
 * const basicHealthCheck = await HealthCheck("api-healthcheck", {
 *   zone: "023e105f4ecef8ad9ca31a8372d0c353",
 *   address: "api.example.com",
 *   name: "api-server-check"
 * });
 *
 * @example
 * // Create an HTTPS health check with custom path and expected response
 * const httpsHealthCheck = await HealthCheck("secure-api-check", {
 *   zone: "023e105f4ecef8ad9ca31a8372d0c353",
 *   address: "secure-api.example.com",
 *   name: "secure-api-check",
 *   type: "HTTPS",
 *   httpConfig: {
 *     path: "/health",
 *     expectedCodes: ["200", "201"],
 *     expectedBody: "OK",
 *     method: "GET"
 *   }
 * });
 *
 * @example
 * // Create a health check with custom intervals and retry logic
 * const customHealthCheck = await HealthCheck("custom-check", {
 *   zone: "023e105f4ecef8ad9ca31a8372d0c353",
 *   address: "backend.example.com",
 *   name: "backend-check",
 *   interval: 30,
 *   timeout: 10,
 *   retries: 3,
 *   consecutiveFails: 2,
 *   consecutiveSuccesses: 2,
 *   description: "Backend server health monitoring"
 * });
 *
 * @example
 * // Create a TCP health check
 * const tcpHealthCheck = await HealthCheck("tcp-check", {
 *   zone: "023e105f4ecef8ad9ca31a8372d0c353",
 *   address: "database.example.com",
 *   name: "database-check",
 *   type: "TCP",
 *   tcpConfig: {
 *     port: 5432,
 *     method: "connection_established"
 *   }
 * });
 *
 * @example
 * // Create a health check with specific regions and custom headers
 * const regionalHealthCheck = await HealthCheck("regional-check", {
 *   zone: "023e105f4ecef8ad9ca31a8372d0c353",
 *   address: "api.example.com",
 *   name: "regional-api-check",
 *   checkRegions: ["WNAM", "ENAM", "WEU"],
 *   httpConfig: {
 *     path: "/api/health",
 *     header: {
 *       "Host": ["api.example.com"],
 *       "X-Health-Check": ["true"]
 *     },
 *     followRedirects: true
 *   }
 * });
 */
export const HealthCheck = Resource("cloudflare::HealthCheck", async function (id, props) {
    const zoneId = typeof props.zone === "string" ? props.zone : props.zone.id;
    const adopt = props.adopt ?? this.scope.adopt;
    const name = props.name ?? this.output?.name ?? this.scope.createPhysicalName(id);
    const api = await createCloudflareApi(props);
    if (this.phase === "delete") {
        const healthCheckId = this.output?.healthCheckId;
        if (!healthCheckId) {
            logger.warn(`No healthCheckId found for ${id}, skipping delete`);
            return this.destroy();
        }
        await deleteHealthCheck(api, zoneId, healthCheckId, id);
        return this.destroy();
    }
    const requestBody = prepareRequestBody(props, name);
    let result;
    if (this.output?.healthCheckId) {
        // Update existing health check
        result = await extractCloudflareResult(`update health check "${name}"`, api.patch(`/zones/${zoneId}/healthchecks/${this.output.healthCheckId}`, requestBody));
    }
    else {
        // Create new health check
        try {
            result = await extractCloudflareResult(`create health check "${name}"`, api.post(`/zones/${zoneId}/healthchecks`, requestBody));
        }
        catch (error) {
            if (error instanceof CloudflareApiError &&
                error.errorData.some((e) => e.code === 1003 ||
                    e.code === 1004 ||
                    e.message?.includes("already exists") ||
                    e.message?.includes("value not unique"))) {
                if (!adopt) {
                    throw new Error(`Health check "${name}" already exists. Use adopt: true to adopt it.`, { cause: error });
                }
                const existing = await findHealthCheckByName(api, zoneId, name);
                if (!existing) {
                    throw new Error(`Health check "${name}" failed to create due to name conflict and could not be found for adoption.`, { cause: error });
                }
                result = await extractCloudflareResult(`adopt health check "${name}"`, api.patch(`/zones/${zoneId}/healthchecks/${existing.id}`, requestBody));
            }
            else {
                throw error;
            }
        }
    }
    return {
        id,
        healthCheckId: result.id,
        zoneId,
        address: result.address,
        name: result.name,
        checkRegions: result.check_regions,
        consecutiveFails: result.consecutive_fails,
        consecutiveSuccesses: result.consecutive_successes,
        description: result.description,
        httpConfig: result.http_config
            ? mapHttpConfigFromApi(result.http_config)
            : undefined,
        interval: result.interval,
        retries: result.retries,
        suspended: result.suspended,
        tcpConfig: result.tcp_config
            ? mapTcpConfigFromApi(result.tcp_config)
            : undefined,
        timeout: result.timeout,
        type: result.type,
        createdOn: result.created_on,
        modifiedOn: result.modified_on,
        status: result.status,
        failureReason: result.failure_reason,
        accountId: props.accountId,
        apiToken: props.apiToken,
    };
});
/**
 * Find a health check by name
 * @internal
 */
async function findHealthCheckByName(api, zoneId, name, page = 1) {
    const response = await api.get(`/zones/${zoneId}/healthchecks?page=${page}&per_page=50`);
    const data = await response.json();
    const found = data.result.find((check) => check.name === name);
    if (found) {
        return found;
    }
    if (data.result_info?.total_pages && page < data.result_info.total_pages) {
        return await findHealthCheckByName(api, zoneId, name, page + 1);
    }
    return undefined;
}
/**
 * Prepare the request body by converting to snake_case
 * @internal
 */
function prepareRequestBody(props, name) {
    const body = {
        address: props.address,
        name: name,
    };
    if (props.checkRegions !== undefined) {
        body.check_regions = props.checkRegions;
    }
    if (props.consecutiveFails !== undefined) {
        body.consecutive_fails = props.consecutiveFails;
    }
    if (props.consecutiveSuccesses !== undefined) {
        body.consecutive_successes = props.consecutiveSuccesses;
    }
    if (props.description !== undefined) {
        body.description = props.description;
    }
    if (props.httpConfig !== undefined) {
        body.http_config = props.httpConfig
            ? {
                allow_insecure: props.httpConfig.allowInsecure,
                expected_body: props.httpConfig.expectedBody,
                expected_codes: props.httpConfig.expectedCodes,
                follow_redirects: props.httpConfig.followRedirects,
                header: props.httpConfig.header
                    ? Object.fromEntries(Object.entries(props.httpConfig.header).map(([key, values]) => [
                        key,
                        values.map((v) => SecretClass.unwrap(v)),
                    ]))
                    : undefined,
                method: props.httpConfig.method,
                path: props.httpConfig.path,
                port: props.httpConfig.port,
            }
            : undefined;
    }
    if (props.interval !== undefined) {
        body.interval = props.interval;
    }
    if (props.retries !== undefined) {
        body.retries = props.retries;
    }
    if (props.suspended !== undefined) {
        body.suspended = props.suspended;
    }
    if (props.tcpConfig !== undefined) {
        body.tcp_config = props.tcpConfig
            ? {
                method: props.tcpConfig.method,
                port: props.tcpConfig.port,
            }
            : undefined;
    }
    if (props.timeout !== undefined) {
        body.timeout = props.timeout;
    }
    if (props.type !== undefined) {
        body.type = props.type;
    }
    return body;
}
/**
 * Map HTTP config from API response (snake_case) to camelCase
 * @internal
 */
function mapHttpConfigFromApi(apiConfig) {
    return {
        allowInsecure: apiConfig.allow_insecure,
        expectedBody: apiConfig.expected_body,
        expectedCodes: apiConfig.expected_codes,
        followRedirects: apiConfig.follow_redirects,
        header: apiConfig.header,
        method: apiConfig.method,
        path: apiConfig.path,
        port: apiConfig.port,
    };
}
/**
 * Map TCP config from API response (snake_case) to camelCase
 * @internal
 */
function mapTcpConfigFromApi(apiConfig) {
    return {
        method: apiConfig.method,
        port: apiConfig.port,
    };
}
//# sourceMappingURL=health-check.js.map