import { Resource } from "../resource.js";
import { isCloudflareApiError } from "./api-error.js";
import { extractCloudflareResult } from "./api-response.js";
import { createCloudflareApi, } from "./api.js";
/**
 * Create or update a VPC service that routes Worker traffic to a private host
 * (hostname or IP) through a Cloudflare Tunnel.
 *
 * [VPC Services](https://developers.cloudflare.com/workers-vpc/configuration/vpc-services/)
 * enable Workers to securely access private network resources through Cloudflare Tunnel.
 * Configure a host (hostname or IP) and optional ports, then bind the service to a Worker
 * to reach private backends.
 *
 * @example
 * // Minimal: hostname through a tunnel
 * ```ts
 * const tunnel = await Tunnel("my-tunnel", {
 *   ingress: [{ service: "http://localhost:3000" }],
 * });
 * const vpcService = await VpcService("my-service", {
 *   host: {
 *     hostname: "localhost",
 *     resolverNetwork: { tunnel, resolverIps: ["127.0.0.1"] },
 *   },
 * });
 * ```
 *
 * @example
 * // IPv4 address
 * ```ts
 * const vpcService = await VpcService("internal-api", {
 *   host: {
 *     ipv4: "192.168.1.100",
 *     network: { tunnel },
 *   },
 * });
 * ```
 *
 * @example
 * // IPv6 address
 * ```ts
 * const vpcService = await VpcService("ipv6-service", {
 *   host: { ipv6: "::1", network: { tunnel } },
 * });
 * ```
 *
 * @example
 * // Dual stack (IPv4 + IPv6)
 * ```ts
 * const vpcService = await VpcService("dual-stack-service", {
 *   host: {
 *     ipv4: "192.168.1.100",
 *     ipv6: "::1",
 *     network: { tunnel },
 *   },
 * });
 * ```
 *
 * @example
 * // Custom HTTP/HTTPS ports
 * ```ts
 * const vpcService = await VpcService("dev-server", {
 *   httpPort: 5173,
 *   httpsPort: 5174,
 *   host: {
 *     hostname: "localhost",
 *     resolverNetwork: { tunnel, resolverIps: ["127.0.0.1"] },
 *   },
 * });
 * ```
 *
 * @example
 * // Bind to a Worker
 * ```ts
 * const vpcService = await VpcService("private-api", {
 *   httpPort: 8080,
 *   host: {
 *     hostname: "internal-api",
 *     resolverNetwork: { tunnel, resolverIps: ["10.0.0.1"] },
 *   },
 * });
 * const worker = await Worker("api-gateway", {
 *   entrypoint: "./src/worker.ts",
 *   bindings: { PRIVATE_API: vpcService },
 * });
 * ```
 *
 * @example
 * // Existing tunnel by ID
 * ```ts
 * const vpcService = await VpcService("existing-tunnel-service", {
 *   host: {
 *     hostname: "internal.example.com",
 *     resolverNetwork: {
 *       tunnelId: "e6a0817c-79c5-40ca-9776-a1c019defe70",
 *       resolverIps: ["10.0.0.53"],
 *     },
 *   },
 * });
 * ```
 *
 * @example
 * // Adopt an existing VPC service
 * ```ts
 * const vpcService = await VpcService("adopted-service", {
 *   name: "existing-service-name",
 *   adopt: true,
 *   host: {
 *     hostname: "localhost",
 *     resolverNetwork: { tunnel },
 *   },
 * });
 * ```
 */
export const VpcService = Resource("cloudflare::VpcService", async function (id, props) {
    const api = await createCloudflareApi(props);
    if (this.phase === "delete") {
        if (this.output?.serviceId) {
            await deleteService(api, this.output.serviceId);
        }
        return this.destroy();
    }
    const input = {
        name: props.name ?? this.scope.createPhysicalName(id),
        type: props.serviceType ?? "http",
        tcp_port: props.tcpPort,
        app_protocol: props.appProtocol,
        http_port: props.httpPort,
        https_port: props.httpsPort,
        host: normalizeHost(props.host),
    };
    switch (this.phase) {
        case "create": {
            const adopt = props.adopt ?? this.scope.adopt;
            return await createService(api, input).catch(async (err) => {
                if (isCloudflareApiError(err, { code: 5101 }) && adopt) {
                    const service = await findVpcServiceByName(api, input.name);
                    if (service) {
                        return await updateService(api, service.serviceId, input);
                    }
                }
                throw err;
            });
        }
        case "update": {
            return await updateService(api, this.output.serviceId, input);
        }
    }
    function normalizeHost(host) {
        if ("hostname" in host) {
            return {
                hostname: host.hostname,
                resolver_network: normalizeNetwork(host.resolverNetwork),
            };
        }
        return {
            ...host,
            network: normalizeNetwork(host.network),
        };
    }
    function normalizeNetwork(network) {
        if ("tunnelId" in network) {
            const { tunnelId, ...rest } = network;
            return { tunnel_id: network.tunnelId, ...rest };
        }
        const { tunnel, ...rest } = network;
        return { tunnel_id: tunnel.tunnelId, ...rest };
    }
});
/**
 * Create a connectivity (VPC) service via the Cloudflare API.
 * @internal
 */
export async function createService(api, body) {
    const service = await extractCloudflareResult("create connectivity service", api.post(`/accounts/${api.accountId}/connectivity/directory/services`, body));
    return formatVpcService(service);
}
/**
 * Delete a connectivity (VPC) service. No-op if the service is already gone (404).
 * @internal
 */
export async function deleteService(api, serviceId) {
    await extractCloudflareResult(`delete connectivity service "${serviceId}"`, api.delete(`/accounts/${api.accountId}/connectivity/directory/services/${serviceId}`)).catch((err) => {
        if (!isCloudflareApiError(err, { status: 404 })) {
            throw err;
        }
    });
}
/**
 * Fetch a single connectivity (VPC) service by ID.
 * @internal
 */
export async function getService(api, serviceId) {
    const service = await extractCloudflareResult(`get connectivity service "${serviceId}"`, api.get(`/accounts/${api.accountId}/connectivity/directory/services/${serviceId}`));
    return formatVpcService(service);
}
/**
 * List connectivity (VPC) services for the account.
 * @internal
 */
export async function findVpcServiceByName(api, name) {
    const services = await extractCloudflareResult("list connectivity services", api.get(`/accounts/${api.accountId}/connectivity/directory/services?per_page=1000`));
    const service = services.find((s) => s.name === name);
    return service ? formatVpcService(service) : undefined;
}
/**
 * Update an existing connectivity (VPC) service.
 * @internal
 */
export async function updateService(api, serviceId, body) {
    const service = await extractCloudflareResult(`update connectivity service "${serviceId}"`, api.put(`/accounts/${api.accountId}/connectivity/directory/services/${serviceId}`, body));
    return formatVpcService(service);
}
/**
 * Format an API response into a VpcService resource.
 * @internal
 */
function formatVpcService(service) {
    return {
        name: service.name,
        serviceId: service.service_id,
        serviceType: service.type,
        tcpPort: service.tcp_port,
        appProtocol: service.app_protocol,
        httpPort: service.http_port,
        httpsPort: service.https_port,
        host: "hostname" in service.host
            ? {
                hostname: service.host.hostname,
                resolverNetwork: {
                    tunnelId: service.host.resolver_network.tunnel_id,
                    resolverIps: service.host.resolver_network.resolver_ips,
                },
            }
            : {
                ...service.host,
                network: { tunnelId: service.host.network.tunnel_id },
            },
        createdAt: new Date(service.created_at).getTime(),
        updatedAt: new Date(service.updated_at).getTime(),
        type: "vpc_service",
    };
}
//# sourceMappingURL=vpc-service.js.map