import { Resource } from "../resource.js";
import { isSecret } from "../secret.js";
import { logger } from "../util/logger.js";
import { createVercelApi } from "./api.js";
const decryptEnvironmentVariable = (envVar) => {
    let { type, value } = envVar;
    // Decrypt Secret & default `type`
    if (isSecret(value)) {
        value = value.unencrypted;
        type ??= "encrypted";
    }
    else {
        // Otherwise, default to `plain`
        type ??= "plain";
    }
    return { ...envVar, type, value };
};
/**
 * Create and manage Vercel projects.
 *
 * @example
 * // With accessToken
 * const project = await Project("my-app", {
 *   accessToken: alchemy.secret(process.env.VERCEL_ACCESS_TOKEN),
 *   name: "my-app",
 *   framework: "astro",
 * });
 *
 * @example
 * // With GitHub
 * const project = await Project("my-app", {
 *   name: "my-app",
 *   framework: "nextjs",
 *   gitRepository: {
 *     type: "github",
 *     repo: "username/my-app",
 *   },
 * });
 *
 * @example
 * // With plain text environment variable
 * const project = await Project("my-app", {
 *   name: "my-app",
 *   environmentVariables: [
 *     {
 *       key: "PUBLIC_URL",
 *       target: ["production", "preview", "development"],
 *       value: "https://example.com",
 *     },
 *   ],
 * });
 *
 * @example
 * // With encrypted environment variable
 * const project = await Project("my-app", {
 *   name: "my-app",
 *   environmentVariables: [
 *     {
 *       key: "DATABASE_URL",
 *       target: ["production", "preview"],
 *       value: alchemy.secret("DATABASE_URL"),
 *     },
 *   ],
 * });
 *
 * @example
 * // With sensitive environment variable
 * const project = await Project("my-app", {
 *   name: "my-app",
 *   environmentVariables: [
 *     {
 *       key: "DATABASE_URL",
 *       target: ["production", "preview"],
 *       type: "sensitive",
 *       value: alchemy.secret("DATABASE_URL"),
 *     },
 *   ],
 * });
 *
 * @example
 * // With custom build settings
 * const project = await Project("my-app", {
 *   name: "my-app",
 *   buildCommand: "npm run build",
 *   outputDirectory: "dist",
 *   installCommand: "npm install",
 *   devCommand: "npm run dev",
 * });
 */
export const Project = Resource("vercel::Project", async function (id, { accessToken, ...props }) {
    // it's possible that `this.output.name` is undefined because a previous version
    // of alchemy had a bug where it didn't set the name on the output
    // so, we try to find the project by ID and use the name from the API response
    const lookupName = async () => {
        if (!this.output) {
            return undefined;
        }
        else if (this.output.name) {
            return this.output.name;
        }
        const name = await getProjectName(await createVercelApi({
            baseUrl: "https://api.vercel.com/v9",
            accessToken,
        }), this.output.id);
        if (name) {
            this.output.name = name;
        }
        return name;
    };
    const projectName = props.name ?? (await lookupName()) ?? this.scope.createPhysicalName(id);
    switch (this.phase) {
        case "delete": {
            const api = await createVercelApi({
                baseUrl: "https://api.vercel.com/v9",
                accessToken,
            });
            try {
                if (this.output?.id) {
                    await api.delete(`/projects/${this.output.id}`);
                }
            }
            catch (error) {
                logger.error("Error deleting project:", error);
            }
            return this.destroy();
        }
        case "update": {
            const api = await createVercelApi({
                baseUrl: "https://api.vercel.com/v9",
                accessToken,
            });
            if (!this.output?.id) {
                throw new Error(`Cannot update project ${id}: missing previous output.id`);
            }
            const { environmentVariables, ...updateProps } = { ...props };
            // 409 Conflict: Can't update these properties, so remove them from the updateProps
            delete updateProps.name;
            delete updateProps.environmentVariables;
            delete updateProps.gitRepository;
            delete updateProps.resourceConfig;
            const response = await api.patch(`/projects/${this.output.id}`, updateProps);
            const data = (await response.json());
            if (environmentVariables) {
                const envApi = await createVercelApi({
                    baseUrl: "https://api.vercel.com/v10",
                    accessToken,
                });
                await updateEnvironmentVariables(envApi, this.output, props);
            }
            return {
                id: data.id,
                name: projectName,
                accountId: data.accountId,
                createdAt: data.createdAt,
                updatedAt: data.updatedAt,
                latestDeployment: data.latestDeployment,
                ...props,
            };
        }
        case "create": {
            const api = await createVercelApi({
                baseUrl: "https://api.vercel.com/v11",
                accessToken,
            });
            const project = await createProject(api, props);
            return this.create({ ...props, ...project });
        }
    }
});
export async function createProject(api, props) {
    const response = await api.post("/projects", {
        ...props,
        environmentVariables: props.environmentVariables?.map(decryptEnvironmentVariable),
    });
    const data = (await response.json());
    return data;
}
export async function updateEnvironmentVariables(api, output, props) {
    if (!props.environmentVariables) {
        return;
    }
    // Update explicitly set environment variables
    await api.post(`/projects/${output.id}/env?upsert=true`, props.environmentVariables.map(decryptEnvironmentVariable));
    // Find previous env vars that are not in the new list and delete them
    const { envs } = (await api
        .get(`/projects/${output.id}/env`)
        .then((res) => res.json()));
    for (const previousEnv of envs) {
        if (props.environmentVariables.some((envVar) => envVar.key === previousEnv.key)) {
            continue;
        }
        await api.delete(`/projects/${output.id}/env/${previousEnv.id}`);
    }
}
async function getProjectName(api, id) {
    const response = await api.get(`/v9/projects/${id}`);
    if (!response.ok) {
        throw new Error(`API error: ${response.statusText}`);
    }
    return (await response.json()).name;
}
//# sourceMappingURL=project.js.map