import { Resource } from "../resource.js";
import { isSecret } from "../secret.js";
import { ignore } from "../util/ignore.js";
import { logger } from "../util/logger.js";
import { importPeer } from "../util/peer.js";
/**
 * AWS SSM Parameter Store Parameter Resource
 *
 * Creates and manages SSM parameters with support for different parameter types,
 * encryption, and automatic tag management. Uses discriminated union types to
 * ensure SecureString parameters always use Secret values and StringList uses arrays.
 *
 * @example
 * // Create a basic string parameter
 * const basicParam = await SSMParameter("app-config", {
 *   name: "/myapp/config/database-url",
 *   value: "postgresql://localhost:5432/myapp",
 *   description: "Database connection URL",
 *   tags: {
 *     Environment: "production",
 *     Application: "myapp"
 *   }
 * });
 *
 * @example
 * // Create a secure string parameter for secrets
 * const secretParam = await SSMParameter("app-secret", {
 *   name: "/myapp/secrets/api-key",
 *   value: alchemy.secret("super-secret-api-key"),
 *   type: "SecureString",
 *   description: "Third-party API key",
 *   tags: {
 *     Environment: "production",
 *     Secret: "true"
 *   }
 * });
 *
 * @example
 * // Create a parameter with custom KMS key
 * const encryptedParam = await SSMParameter("encrypted-config", {
 *   name: "/myapp/config/encrypted",
 *   value: alchemy.secret("sensitive-configuration-data"),
 *   type: "SecureString",
 *   keyId: "alias/myapp-kms-key",
 *   description: "Encrypted configuration data",
 *   tier: "Advanced",
 *   tags: {
 *     Environment: "production",
 *     Encrypted: "true"
 *   }
 * });
 *
 * @example
 * // Create a string list parameter
 * const listParam = await SSMParameter("server-list", {
 *   name: "/myapp/config/servers",
 *   value: ["server1.example.com", "server2.example.com", "server3.example.com"],
 *   type: "StringList",
 *   description: "List of application servers",
 *   tags: {
 *     Environment: "production",
 *     Type: "configuration"
 *   }
 * });
 */
export const SSMParameter = Resource("ssm::Parameter", async function (id, props) {
    const { AddTagsToResourceCommand, DeleteParameterCommand, GetParameterCommand, ParameterAlreadyExists, ParameterNotFound, PutParameterCommand, SSMClient, } = await importPeer(import("@aws-sdk/client-ssm"), "ssm::Parameter");
    const client = new SSMClient({});
    const parameterName = props.name ?? this.output?.name ?? this.scope.createPhysicalName(id);
    if (this.phase === "update" && this.output.name !== parameterName) {
        this.replace();
    }
    if (this.phase === "delete") {
        try {
            await ignore(ParameterNotFound.name, () => client.send(new DeleteParameterCommand({
                Name: parameterName,
            })));
        }
        catch (error) {
            if (!(error instanceof ParameterNotFound)) {
                throw error;
            }
        }
        return this.destroy();
    }
    const parameterType = props.type || "String";
    // Extract the actual value and handle type-specific conversions
    let parameterValue;
    if (isSecret(props.value)) {
        parameterValue = props.value.unencrypted;
    }
    else if (Array.isArray(props.value)) {
        // Convert string array to comma-separated string for StringList
        parameterValue = props.value.join(",");
    }
    else {
        parameterValue = props.value;
    }
    try {
        // First, try to create the parameter without overwrite to include tags
        try {
            await client.send(new PutParameterCommand({
                Name: parameterName,
                Value: parameterValue,
                Type: parameterType,
                Description: props.description,
                KeyId: props.keyId,
                Tier: props.tier,
                Policies: props.policies,
                DataType: props.dataType,
                Overwrite: false, // Don't overwrite, include tags
                Tags: [
                    ...Object.entries(props.tags || {}).map(([Key, Value]) => ({
                        Key,
                        Value,
                    })),
                    {
                        Key: "alchemy_stage",
                        Value: this.stage,
                    },
                    {
                        Key: "alchemy_resource",
                        Value: this.id,
                    },
                ],
            }));
        }
        catch (error) {
            // If parameter already exists, update it with overwrite (no tags in this call)
            if (error instanceof ParameterAlreadyExists) {
                await client.send(new PutParameterCommand({
                    Name: parameterName,
                    Value: parameterValue,
                    Type: parameterType,
                    Description: props.description,
                    KeyId: props.keyId,
                    Tier: props.tier,
                    Policies: props.policies,
                    DataType: props.dataType,
                    Overwrite: true, // Overwrite existing, no tags
                }));
                // Update tags separately for existing parameters
                const tags = [
                    ...Object.entries(props.tags || {}).map(([Key, Value]) => ({
                        Key,
                        Value,
                    })),
                    {
                        Key: "alchemy_stage",
                        Value: this.stage,
                    },
                    {
                        Key: "alchemy_resource",
                        Value: this.id,
                    },
                ];
                await client.send(new AddTagsToResourceCommand({
                    ResourceType: "Parameter",
                    ResourceId: parameterName,
                    Tags: tags,
                }));
            }
            else {
                throw error;
            }
        }
        // Get the updated parameter
        const parameter = await client.send(new GetParameterCommand({
            Name: parameterName,
            WithDecryption: true,
        }));
        if (!parameter?.Parameter) {
            throw new Error(`Failed to create or update parameter ${parameterName}`);
        }
        return {
            ...props,
            arn: parameter.Parameter.ARN,
            version: parameter.Parameter.Version,
            lastModifiedDate: parameter.Parameter.LastModifiedDate,
            name: parameter.Parameter.Name ?? parameterName,
            value: props.value,
            type: parameter.Parameter.Type ?? parameterType,
        };
    }
    catch (error) {
        logger.error(`Error creating/updating parameter ${parameterName}:`, error);
        throw error;
    }
});
//# sourceMappingURL=ssm-parameter.js.map