import { Resource } from "../resource.js";
import { createPlanetScaleClient } from "./api.js";
import { diffExtensions, updateExtensions, } from "./database-extensions.js";
import { ensureProductionBranchClusterSize, sanitizeClusterSize, waitForBranchReady, waitForDatabaseReady, } from "./utils.js";
/**
 * Create, manage and delete PlanetScale databases
 *
 * @example
 * // Create a basic database in a specific organization
 * const db = await Database("my-app-db", {
 *   name: "my-app-db",
 *   organization: "my-org",
 *   clusterSize: "PS_10"
 * });
 *
 * @example
 * // Create a database with specific region and settings
 * const db = await Database("my-app-db", {
 *   name: "my-app-db",
 *   organization: "my-org",
 *   region: {
 *     slug: "us-east"
 *   },
 *   clusterSize: "PS_10",
 *   requireApprovalForDeploy: true,
 *   allowDataBranching: true,
 *   automaticMigrations: true
 * });
 *
 * @example
 * // Create a database with custom API key
 * const db = await Database("my-app-db", {
 *   name: "my-app-db",
 *   organization: "my-org",
 *   apiKey: alchemy.secret(process.env.CUSTOM_PLANETSCALE_TOKEN),
 *   clusterSize: "PS_10"
 * });
 */
export const Database = Resource("planetscale::Database", async function (id, props) {
    const api = createPlanetScaleClient(props);
    const databaseName = props.name ?? this.output?.name ?? this.scope.createPhysicalName(id);
    const clusterSize = sanitizeClusterSize({
        size: props.clusterSize,
        kind: props.kind,
        ...(props.kind === "postgresql"
            ? {
                arch: props.arch,
            }
            : {}),
        region: props.region?.slug,
    });
    const organization = 
    // @ts-expect-error - organizationId is a legacy thing, we keep this so we can destroy
    this.output?.organizationId ??
        props.organization ??
        process.env.PLANETSCALE_ORGANIZATION ??
        process.env.PLANETSCALE_ORG_ID;
    if (!organization) {
        throw new Error("PlanetScale organization is required. Please set the `organization` property or the `PLANETSCALE_ORGANIZATION` environment variable.");
    }
    const adopt = props.adopt ?? this.scope.adopt;
    const shouldDelete = props.delete ?? false;
    if (this.phase === "update" && this.output.name !== databaseName) {
        await api.updateDatabaseSettings({
            path: {
                organization,
                database: this.output.name,
            },
            body: { new_name: databaseName },
        });
    }
    if (this.phase === "delete") {
        if (shouldDelete && this.output?.name) {
            const response = await api.deleteDatabase({
                path: {
                    organization,
                    database: this.output.name,
                },
                throwOnError: false,
            });
            if (response.error && response.response.status !== 404) {
                throw new Error(`Failed to delete database "${this.output.name}"`, {
                    cause: response.error,
                });
            }
        }
        return this.destroy();
    }
    // Check if database exists
    const getResponse = await api.getDatabase({
        path: {
            organization,
            database: databaseName,
        },
        throwOnError: false,
    });
    if (this.phase === "update" || (adopt && getResponse.data)) {
        if (!getResponse.data) {
            throw new Error(`Database "${databaseName}" not found`, {
                cause: getResponse.error,
            });
        }
        // Validate immutable properties match if specified
        const actualKind = getResponse.data.kind;
        if (props.kind && actualKind !== props.kind) {
            throw new Error(`Database "${databaseName}" has kind "${actualKind}" but expected "${props.kind}". ` +
                `Database kind cannot be changed after creation.`);
        }
        if (props.region) {
            const actualSlug = getResponse.data.region.slug;
            if (actualSlug !== props.region.slug) {
                throw new Error(`Database "${databaseName}" is in region "${actualSlug}" but expected "${props.region.slug}". ` +
                    `PlanetScale database regions cannot be changed after creation. ` +
                    `Either update the region in your configuration to match, or create a new database in the correct region.`);
            }
        }
        if (props.kind === "postgresql" && props.arch) {
            const defaultBranch = props.defaultBranch || "main";
            const branchInfo = await api.getBranch({
                path: {
                    organization,
                    database: databaseName,
                    branch: defaultBranch,
                },
                throwOnError: false,
            });
            if (branchInfo.data?.cluster_architecture) {
                const actualArch = branchInfo.data.cluster_architecture === "aarch64" ? "arm" : "x86";
                if (actualArch !== props.arch) {
                    throw new Error(`Database "${databaseName}" has architecture "${actualArch}" but expected "${props.arch}". ` +
                        `Database architecture cannot be changed after creation.`);
                }
            }
        }
        // Update database settings
        // If updating to a non-'main' default branch, create it first
        if (props.defaultBranch && props.defaultBranch !== "main") {
            const branchResponse = await api.getBranch({
                path: {
                    organization,
                    database: databaseName,
                    branch: props.defaultBranch,
                },
                throwOnError: false,
            });
            if (!branchResponse.data) {
                await waitForDatabaseReady(api, organization, databaseName);
            }
            if (branchResponse.error && branchResponse.response.status === 404) {
                // Create the branch
                await api.createBranch({
                    path: {
                        organization,
                        database: databaseName,
                    },
                    body: {
                        name: props.defaultBranch,
                        parent_branch: "main",
                    },
                });
            }
        }
        const { data } = await api.updateDatabaseSettings({
            path: {
                organization,
                database: databaseName,
            },
            body: {
                ...(props.kind !== "postgresql"
                    ? {
                        automatic_migrations: props.automaticMigrations,
                        migration_framework: props.migrationFramework,
                        migration_table_name: props.migrationTableName,
                        allow_foreign_key_constraints: props.allowForeignKeyConstraints,
                        allow_data_branching: props.allowDataBranching,
                    }
                    : {}),
                require_approval_for_deploy: props.requireApprovalForDeploy,
                restrict_branch_region: props.restrictBranchRegion,
                insights_raw_queries: props.insightsRawQueries,
                production_branch_web_console: props.productionBranchWebConsole,
                default_branch: props.defaultBranch,
            },
        });
        await ensureProductionBranchClusterSize(api, organization, databaseName, props.defaultBranch || "main", data.kind, clusterSize);
        // Sync PostgreSQL extensions on update
        if (props.kind === "postgresql") {
            const previousExtensions = this.output?.kind === "postgresql"
                ? this.output.extensions
                : undefined;
            const desiredExtensions = props.extensions;
            const diff = diffExtensions(previousExtensions, desiredExtensions);
            if (diff.hasChanges) {
                const branch = props.defaultBranch || "main";
                await waitForBranchReady(api, organization, databaseName, branch);
                await updateExtensions(desiredExtensions ?? {}, {
                    ...props,
                    organization,
                    database: databaseName,
                    branch,
                });
            }
        }
        return {
            ...props,
            id: data.id,
            name: databaseName,
            state: data.state,
            defaultBranch: data.default_branch,
            plan: data.plan,
            createdAt: data.created_at,
            updatedAt: data.updated_at,
            htmlUrl: data.html_url,
            organization,
            region: { slug: data.region.slug },
        };
    }
    if (getResponse.data) {
        throw new Error(`Database with name "${databaseName}" already exists`);
    }
    // Create new database
    await api.createDatabase({
        path: {
            organization,
        },
        body: {
            name: databaseName,
            region: props.region?.slug,
            kind: props.kind,
            cluster_size: clusterSize,
            replicas: props.replicas,
            ...(props.kind === "postgresql"
                ? {
                    major_version: props.majorVersion,
                }
                : {}),
        },
    });
    // These settings can't be set on creation, so we need to patch them after creation.
    const { data } = await api.updateDatabaseSettings({
        path: {
            organization,
            database: databaseName,
        },
        body: {
            ...(props.kind !== "postgresql"
                ? {
                    automatic_migrations: props.automaticMigrations,
                    migration_framework: props.migrationFramework,
                    migration_table_name: props.migrationTableName,
                    allow_foreign_key_constraints: props.allowForeignKeyConstraints,
                    allow_data_branching: props.allowDataBranching,
                }
                : {}),
            require_approval_for_deploy: props.requireApprovalForDeploy,
            restrict_branch_region: props.restrictBranchRegion,
            insights_raw_queries: props.insightsRawQueries,
            production_branch_web_console: props.productionBranchWebConsole,
        },
    });
    // If a non-'main' default branch is specified, create it
    if (props.defaultBranch && props.defaultBranch !== "main") {
        await waitForDatabaseReady(api, organization, databaseName);
        // Check if branch exists
        const branchResponse = await api.getBranch({
            path: {
                organization,
                database: databaseName,
                branch: props.defaultBranch,
            },
            throwOnError: false,
        });
        if (branchResponse.error && branchResponse.response.status === 404) {
            // Create the branch
            await api.createBranch({
                path: {
                    organization,
                    database: databaseName,
                },
                body: {
                    name: props.defaultBranch,
                    parent_branch: "main",
                },
            });
            await ensureProductionBranchClusterSize(api, organization, databaseName, props.defaultBranch || "main", data.kind, clusterSize);
            // Update database to use new branch as default
            const { data: updatedData } = await api.updateDatabaseSettings({
                path: {
                    organization,
                    database: databaseName,
                },
                body: {
                    default_branch: props.defaultBranch,
                },
            });
            // Enable PostgreSQL extensions on the new default branch
            if (props.kind === "postgresql" && props.extensions) {
                await waitForBranchReady(api, organization, databaseName, props.defaultBranch);
                await updateExtensions(props.extensions, {
                    ...props,
                    organization,
                    database: databaseName,
                    branch: props.defaultBranch,
                });
            }
            return {
                ...props,
                id: data.id,
                name: databaseName,
                state: updatedData.state,
                defaultBranch: updatedData.default_branch,
                plan: updatedData.plan,
                createdAt: updatedData.created_at,
                updatedAt: updatedData.updated_at,
                htmlUrl: updatedData.html_url,
                organization,
                region: { slug: updatedData.region.slug },
            };
        }
    }
    // Enable PostgreSQL extensions on the default branch (main)
    if (props.kind === "postgresql" && props.extensions) {
        const branch = props.defaultBranch || "main";
        await waitForBranchReady(api, organization, databaseName, branch);
        await updateExtensions(props.extensions, {
            ...props,
            organization,
            database: databaseName,
            branch,
        });
    }
    return {
        ...props,
        id: data.id,
        name: databaseName,
        state: data.state,
        defaultBranch: data.default_branch || "main",
        plan: data.plan,
        createdAt: data.created_at,
        updatedAt: data.updated_at,
        htmlUrl: data.html_url,
        organization,
        region: { slug: data.region.slug },
    };
});
//# sourceMappingURL=database.js.map