import { Resource, ResourceKind } from "../resource.js";
import { Scope } from "../scope.js";
import { CloudflareApiError, handleApiError } from "./api-error.js";
import { createCloudflareApi, } from "./api.js";
export function isQueue(eventSource) {
    return (ResourceKind in eventSource &&
        eventSource[ResourceKind] === "cloudflare::Queue");
}
/**
 * Creates and manages Cloudflare Queues.
 *
 * Queues provide a managed queue system for reliable message delivery
 * between workers and other systems.
 *
 * @example
 * // Create a basic queue with default settings
 * const basicQueue = await Queue("my-app-queue", {
 *   name: "my-app-queue"
 * });
 *
 * @example
 * // Create a queue with custom settings
 * const customQueue = await Queue("delayed-queue", {
 *   name: "delayed-queue",
 *   settings: {
 *     deliveryDelay: 30, // 30 second delay before message delivery
 *     messageRetentionPeriod: 86400 // Store messages for 1 day
 *   }
 * });
 *
 * @example
 * // Create a paused queue for later activation
 * const pausedQueue = await Queue("paused-queue", {
 *   name: "paused-queue",
 *   settings: {
 *     deliveryPaused: true
 *   }
 * });
 *
 * @example
 * // Create a queue with a dead letter queue using string reference
 * const dlqQueue = await Queue("dlq-queue", {
 *   name: "dlq-queue"
 * });
 *
 * const mainQueue = await Queue("main-queue", {
 *   name: "main-queue",
 *   dlq: "dlq-queue"
 * });
 *
 * @example
 * // Create a queue with a dead letter queue using Queue object
 * const dlqQueue = await Queue("dlq-queue", {
 *   name: "dlq-queue"
 * });
 *
 * const mainQueue = await Queue("main-queue", {
 *   name: "main-queue",
 *   dlq: dlqQueue
 * });
 *
 * @example
 * // Create a queue and configure Worker consumer with custom settings
 * const processingQueue = await Queue("processing-queue", {
 *   name: "processing-queue"
 * });
 *
 * const processingWorker = await Worker("processor", {
 *   entrypoint: "./src/processor.ts",
 *   bindings: {
 *     QUEUE: processingQueue  // Producer: bind queue for sending messages
 *   },
 *   eventSources: [{  // Consumer: configure processing settings
 *     queue: processingQueue,
 *     settings: {
 *       batchSize: 25,           // Process 25 messages at once
 *       maxConcurrency: 5,       // Allow 5 concurrent invocations
 *       maxRetries: 3,           // Retry failed messages up to 3 times
 *       maxWaitTimeMs: 1500,     // Wait up to 1.5 seconds to fill a batch
 *       retryDelay: 45,          // Wait 45 seconds before retrying failed messages
 *       deadLetterQueue: "failed-processing" // Send failed messages to DLQ
 *     }
 *   }]
 * });
 *
 * @see https://developers.cloudflare.com/queues/
 */
export async function Queue(id, props = {}) {
    return await _Queue(id, {
        ...props,
        dev: {
            ...(props.dev ?? {}),
            force: Scope.current.local,
        },
    });
}
const _Queue = Resource("cloudflare::Queue", async function (id, props = {}) {
    const queueName = props.name ?? this.output?.name ?? this.scope.createPhysicalName(id);
    const dev = {
        id: this.output?.dev?.id ?? this.output?.id ?? id,
        remote: props.dev?.remote ?? false,
    };
    if (this.scope.local && !props.dev?.remote) {
        return {
            type: "queue",
            id: this.output?.id ?? "",
            name: queueName,
            dev,
            createdOn: this.output?.createdOn ?? new Date().toISOString(),
            modifiedOn: this.output?.modifiedOn ?? new Date().toISOString(),
            Body: undefined,
            Batch: undefined,
        };
    }
    const api = await createCloudflareApi(props);
    if (this.phase === "delete") {
        if (props.delete !== false && this.output?.id) {
            // Delete Queue
            await deleteQueue(api, this.output.id);
        }
        // Return void (a deleted queue has no content)
        return this.destroy();
    }
    let queueData;
    if (this.phase === "create" || !this.output?.id) {
        try {
            queueData = await createQueue(api, queueName, props);
        }
        catch (error) {
            if (error instanceof CloudflareApiError && error.status === 409) {
                if (!(props.adopt ?? this.scope.adopt)) {
                    throw error;
                }
                // Queue already exists, try to find it by name
                const existingQueue = await findQueueByName(api, queueName);
                if (!existingQueue) {
                    throw new Error(`Queue with name ${queueName} not found despite 409 conflict`);
                }
                queueData = existingQueue;
                queueData = await updateQueue(api, queueData.result.queue_id, {
                    ...props,
                    name: queueName,
                });
            }
            else {
                throw error;
            }
        }
    }
    else {
        // Update operation
        if (this.output?.id) {
            // Update the queue with new settings
            queueData = await updateQueue(api, this.output.id, {
                ...props,
                name: queueName,
            });
        }
        else {
            // If no ID exists, fall back to creating a new queue
            queueData = await createQueue(api, queueName, props);
        }
    }
    return {
        type: "queue",
        id: queueData.result.queue_id || "",
        name: queueName,
        settings: queueData.result.settings
            ? {
                deliveryDelay: queueData.result.settings.delivery_delay,
                deliveryPaused: queueData.result.settings.delivery_paused,
                messageRetentionPeriod: queueData.result.settings.message_retention_period,
            }
            : undefined,
        dlq: props.dlq,
        createdOn: queueData.result.created_on || new Date().toISOString(),
        modifiedOn: queueData.result.modified_on || new Date().toISOString(),
        accountId: api.accountId,
        dev,
        // phantom properties
        Body: undefined,
        Batch: undefined,
    };
});
/**
 * Create a new Cloudflare Queue
 */
export async function createQueue(api, queueName, props) {
    // Prepare the create payload
    const createPayload = {
        queue_name: queueName,
    };
    // Add settings if provided
    if (props.settings) {
        createPayload.settings = {};
        if (props.settings.deliveryDelay !== undefined) {
            createPayload.settings.delivery_delay = props.settings.deliveryDelay;
        }
        if (props.settings.deliveryPaused !== undefined) {
            createPayload.settings.delivery_paused = props.settings.deliveryPaused;
        }
        if (props.settings.messageRetentionPeriod !== undefined) {
            createPayload.settings.message_retention_period =
                props.settings.messageRetentionPeriod;
        }
    }
    const createResponse = await api.post(`/accounts/${api.accountId}/queues`, createPayload);
    if (!createResponse.ok) {
        return await handleApiError(createResponse, "creating", "Queue", queueName);
    }
    return (await createResponse.json());
}
/**
 * Get a Cloudflare Queue
 */
export async function getQueue(api, queueId) {
    const response = await api.get(`/accounts/${api.accountId}/queues/${queueId}`);
    if (!response.ok) {
        return await handleApiError(response, "getting", "Queue", queueId);
    }
    return (await response.json());
}
/**
 * Delete a Cloudflare Queue
 */
export async function deleteQueue(api, queueId) {
    // Delete Queue
    const deleteResponse = await api.delete(`/accounts/${api.accountId}/queues/${queueId}`);
    if (!deleteResponse.ok && deleteResponse.status !== 404) {
        const errorData = await deleteResponse.json().catch(() => ({
            errors: [{ message: deleteResponse.statusText }],
        }));
        throw new CloudflareApiError(`Error deleting Cloudflare Queue '${queueId}': ${errorData.errors?.[0]?.message || deleteResponse.statusText}`, deleteResponse);
    }
}
/**
 * Update a Cloudflare Queue
 *
 * Note: According to Cloudflare API, the queue name cannot be changed after creation.
 * Only the settings can be updated.
 */
export async function updateQueue(api, queueId, props) {
    // Prepare the update payload - only include settings
    const updatePayload = {
        queue_name: props.name,
    };
    // Add settings if provided
    if (props.settings) {
        updatePayload.settings = {};
        if (props.settings.deliveryDelay !== undefined) {
            updatePayload.settings.delivery_delay = props.settings.deliveryDelay;
        }
        if (props.settings.deliveryPaused !== undefined) {
            updatePayload.settings.delivery_paused = props.settings.deliveryPaused;
        }
        if (props.settings.messageRetentionPeriod !== undefined) {
            updatePayload.settings.message_retention_period =
                props.settings.messageRetentionPeriod;
        }
    }
    // Use PATCH for partial updates (only settings can be updated)
    const updateResponse = await api.patch(`/accounts/${api.accountId}/queues/${queueId}`, updatePayload);
    if (!updateResponse.ok) {
        return await handleApiError(updateResponse, "updating", "Queue", queueId);
    }
    return (await updateResponse.json());
}
const CLOUDFLARE_QUEUE_PAGE_SIZE = 100;
async function fetchQueuePage(api, page, perPage = CLOUDFLARE_QUEUE_PAGE_SIZE) {
    const searchParams = new URLSearchParams({
        page: page.toString(),
        per_page: perPage.toString(),
    });
    const response = await api.get(`/accounts/${api.accountId}/queues?${searchParams.toString()}`);
    if (!response.ok) {
        return await handleApiError(response, "listing", "Queues", "");
    }
    const data = (await response.json());
    if (!data.success) {
        const errorMessage = data.errors?.[0]?.message || "Unknown error";
        throw new Error(`Failed to list queues: ${errorMessage}`);
    }
    return data;
}
function hasMoreQueuePages(data, currentPage, perPage) {
    if (data.result_info?.total_pages !== undefined) {
        return currentPage < data.result_info.total_pages;
    }
    const pageSize = data.result_info?.per_page ?? perPage;
    const currentCount = data.result?.length ?? 0;
    return currentCount > 0 && currentCount >= pageSize;
}
export async function listQueues(api) {
    const queues = [];
    let page = 1;
    // Paginate through all queues to ensure nothing is missed on later pages
    while (true) {
        const data = await fetchQueuePage(api, page);
        queues.push(...(data.result ?? []));
        if (!hasMoreQueuePages(data, page, CLOUDFLARE_QUEUE_PAGE_SIZE)) {
            break;
        }
        page += 1;
    }
    // Transform API response
    return queues.map((queue) => ({
        name: queue.queue_name,
        id: queue.queue_id,
    }));
}
/**
 * Find a Cloudflare Queue by name
 */
export async function findQueueByName(api, queueName) {
    let page = 1;
    while (true) {
        const data = await fetchQueuePage(api, page);
        const queue = data.result?.find((q) => q.queue_name === queueName);
        if (queue) {
            return {
                result: {
                    queue_id: queue.queue_id,
                    queue_name: queue.queue_name,
                    created_on: queue.created_on,
                    modified_on: queue.modified_on,
                    settings: queue.settings,
                },
                success: true,
                errors: [],
                messages: [],
            };
        }
        if (!hasMoreQueuePages(data, page, CLOUDFLARE_QUEUE_PAGE_SIZE)) {
            return null;
        }
        page += 1;
    }
}
//# sourceMappingURL=queue.js.map