import { alchemy } from "../alchemy.js";
import { Resource } from "../resource.js";
import { logger } from "../util/logger.js";
import { sha256 } from "../util/sha256.js";
import { createCloudflareApi } from "./api.js";
import { PermissionGroups, } from "./permission-groups.js";
/**
 * Creates a Cloudflare Account API Token with specified permissions.
 *
 * Note: Requires a Cloudflare API Key or Token with admin-level account access.
 * The OAuth token from `wrangler login` is NOT sufficient for this operation.
 * You must use an API token with permission to manage account API tokens.
 *
 * @see https://developers.cloudflare.com/api/resources/accounts/subresources/tokens/methods/create/
 *
 * @example
 * // Create a token with read-only permissions for specific zones
 * const readOnlyToken = await AccountApiToken("readonly-token", {
 *   name: "Readonly Zone Token",
 *   policies: [
 *     {
 *       effect: "allow",
 *       permissionGroups: ["Zone Read", "Analytics Read"],
 *       resources: {
 *         "com.cloudflare.api.account.zone.22b1de5f1c0e4b3ea97bb1e963b06a43": "*",
 *         "com.cloudflare.api.account.zone.eb78d65290b24279ba6f44721b3ea3c4": "*"
 *       }
 *     }
 *   ],
 *   expiresOn: "2024-12-31T23:59:59Z"
 * });
 *
 * @example
 * // Create a token with time and IP restrictions
 * const restrictedToken = await AccountApiToken("restricted-token", {
 *   name: "Restricted Access Token",
 *   policies: [
 *     {
 *       effect: "allow",
 *       permissionGroups: ["Worker Routes Edit"],
 *       resources: {
 *         "com.cloudflare.api.account.worker.route.*": "*"
 *       }
 *     }
 *   ],
 *   notBefore: "2023-01-01T00:00:00Z",
 *   expiresOn: "2023-12-31T23:59:59Z",
 *   condition: {
 *     requestIp: {
 *       in: ["192.168.1.0/24", "10.0.0.0/8"],
 *       notIn: ["192.168.1.100/32"]
 *     }
 *   }
 * });
 *
 * @example
 * // Create a token with bucket access permissions
 * const storageToken = await AccountApiToken("account-access-token", {
 *   name: "alchemy-account-access-token",
 *   policies: [
 *     {
 *       effect: "allow",
 *       permissionGroups: ["Workers R2 Storage Write"],
 *       resources: {
 *         "com.cloudflare.api.account": "*",
 *       },
 *     },
 *   ],
 * });
 */
export const AccountApiToken = Resource("cloudflare::AccountApiToken", async function (id, props) {
    // Create Cloudflare API client with automatic account discovery
    const api = await createCloudflareApi(props);
    const tokenName = props.name ?? this.scope.createPhysicalName(id);
    if (this.phase === "delete") {
        // Delete token if we have an ID
        if (this.output?.id && props.delete !== false) {
            try {
                const deleteResponse = await api.delete(`/accounts/${api.accountId}/tokens/${this.output.id}`);
                if (!deleteResponse.ok && deleteResponse.status !== 404) {
                    const errorData = await deleteResponse.json().catch(() => ({
                        errors: [{ message: deleteResponse.statusText }],
                    }));
                    logger.error(`Error deleting token '${tokenName}':`, errorData);
                }
            }
            catch (error) {
                logger.error(`Error deleting token '${tokenName}':`, error);
            }
        }
        // Return destroyed state
        return this.destroy();
    }
    const permissionGroups = await PermissionGroups();
    // Transform our properties to API format
    const apiPayload = {
        name: tokenName,
        policies: props.policies.map((policy) => ({
            effect: policy.effect,
            permission_groups: policy.permissionGroups.map((pg) => typeof pg === "string"
                ? {
                    id: permissionGroups[pg].id,
                }
                : {
                    id: pg.id,
                    meta: pg.meta || {},
                }),
            resources: Object.fromEntries(Object.entries(policy.resources).map(([key, value]) => {
                if (key === "com.cloudflare.api.account") {
                    return [`com.cloudflare.api.account.${api.accountId}`, value];
                }
                return [key, value];
            })),
        })),
        // Format dates for Cloudflare API (removing milliseconds)
        ...(props.expiresOn
            ? { expires_on: formatCloudflareDate(props.expiresOn) }
            : {}),
        ...(props.notBefore
            ? { not_before: formatCloudflareDate(props.notBefore) }
            : {}),
        ...(props.condition
            ? {
                condition: {
                    request_ip: props.condition.requestIp
                        ? {
                            in: props.condition.requestIp.in || [],
                            not_in: props.condition.requestIp.notIn || [],
                        }
                        : undefined,
                },
            }
            : {}),
    };
    /**
     * Formats a date string for Cloudflare API by removing milliseconds
     * Converts from "2023-01-01T00:00:00.000Z" to "2023-01-01T00:00:00Z"
     */
    function formatCloudflareDate(dateStr) {
        return dateStr.replace(/\.\d{3}Z$/, "Z");
    }
    let response;
    let tokenValue;
    if (this.phase === "update" && this.output?.id) {
        // Update existing token
        response = await api.put(`/accounts/${api.accountId}/tokens/${this.output.id}`, apiPayload);
    }
    else {
        // Create new token
        response = await api.post(`/accounts/${api.accountId}/tokens`, apiPayload);
    }
    if (!response.ok) {
        const errorData = await response.json().catch(() => ({
            errors: [{ message: response.statusText }],
        }));
        throw new Error([
            `Error ${this.phase === "update" ? "updating" : "creating"} token '${tokenName}' (${response.status}): ${errorData.errors?.[0]?.message || response.statusText}`,
            ...(api.credentials.type !== "api-key"
                ? [
                    `\nTo create an Account API Token, you must use a Global API Key or an API Token with the "Account API Tokens Write" permission.`,
                ]
                : []),
            ...(api.credentials.type === "oauth"
                ? ["OAuth credentials are not supported."]
                : []),
            "See https://alchemy.run/guides/cloudflare/ for more details.",
        ].join("\n"));
    }
    const result = await response.json();
    const tokenData = result.result;
    if (tokenData.value) {
        tokenValue = alchemy.secret(tokenData.value);
    }
    else {
        if (!this.output?.value) {
            throw new Error(`Token '${tokenName}' was created but we have no record of its value. Try deleting and recreating the token.`);
        }
        tokenValue = this.output?.value;
    }
    // Transform API response to our format
    return {
        id: tokenData.id,
        name: tokenData.name,
        status: tokenData.status,
        ...(tokenData.expires_on ? { expiresOn: tokenData.expires_on } : {}),
        ...(tokenData.not_before ? { notBefore: tokenData.not_before } : {}),
        ...(tokenData.condition
            ? {
                condition: {
                    requestIp: tokenData.condition.request_ip
                        ? {
                            in: tokenData.condition.request_ip.in || [],
                            notIn: tokenData.condition.request_ip.not_in || [],
                        }
                        : undefined,
                },
            }
            : {}),
        value: tokenValue,
        accessKeyId: alchemy.secret(tokenData.id),
        secretAccessKey: alchemy.secret(sha256(tokenValue.unencrypted)),
    };
});
//# sourceMappingURL=account-api-token.js.map