import { Resource } from "../resource.js";
import { importPeer } from "../util/peer.js";
import { retry } from "./retry.js";
/**
 * AWS IAM Policy Resource
 *
 * Creates and manages IAM policies that define permissions for AWS services and resources.
 * Supports automatic versioning and updates when policy content changes.
 *
 * @example
 * // Create a basic S3 bucket access policy
 * const s3Policy = await Policy("bucket-access", {
 *   policyName: "s3-bucket-access",
 *   document: {
 *     Version: "2012-10-17",
 *     Statement: [{
 *       Effect: "Allow",
 *       Action: [
 *         "s3:GetObject",
 *         "s3:PutObject"
 *       ],
 *       Resource: `${bucket.arn}/*`
 *     }]
 *   }
 * });
 *
 * @example
 * // Create a policy with multiple statements and conditions
 * const apiPolicy = await Policy("api-access", {
 *   policyName: "api-gateway-access",
 *   document: {
 *     Version: "2012-10-17",
 *     Statement: [
 *       {
 *         Sid: "InvokeAPI",
 *         Effect: "Allow",
 *         Action: "execute-api:Invoke",
 *         Resource: `${api.executionArn}/*`,
 *         Condition: {
 *           StringEquals: {
 *             "aws:SourceVpc": vpc.id
 *           }
 *         }
 *       },
 *       {
 *         Sid: "ReadLogs",
 *         Effect: "Allow",
 *         Action: [
 *           "logs:GetLogEvents",
 *           "logs:FilterLogEvents"
 *         ],
 *         Resource: `${api.logGroupArn}:*`
 *       }
 *     ]
 *   },
 *   description: "Allows invoking API Gateway endpoints and reading logs",
 *   tags: {
 *     Service: "API Gateway",
 *     Environment: "production"
 *   }
 * });
 *
 * @example
 * // Create a policy that denies access based on tags
 * const denyPolicy = await Policy("deny-production", {
 *   policyName: "deny-production-access",
 *   document: {
 *     Version: "2012-10-17",
 *     Statement: [{
 *       Effect: "Deny",
 *       Action: "*",
 *       Resource: "*",
 *       Condition: {
 *         StringEquals: {
 *           "aws:ResourceTag/Environment": "production"
 *         }
 *       }
 *     }]
 *   }
 * });
 */
export const Policy = Resource("iam::Policy", async function (id, props) {
    const { CreatePolicyCommand, CreatePolicyVersionCommand, DeletePolicyCommand, DeletePolicyVersionCommand, GetPolicyCommand, GetPolicyVersionCommand, IAMClient, ListPolicyVersionsCommand, NoSuchEntityException, } = await importPeer(import("@aws-sdk/client-iam"), "iam::Policy");
    const client = new IAMClient({});
    const policyName = props.policyName ??
        this.output?.policyName ??
        this.scope.createPhysicalName(id);
    const policyArn = `arn:aws:iam::${process.env.AWS_ACCOUNT_ID}:policy${props.path || "/"}${policyName}`;
    if (this.phase === "update" && this.output.policyName !== policyName) {
        this.replace();
    }
    if (this.phase === "delete") {
        try {
            // List and delete all non-default versions first
            const versions = await retry(() => client.send(new ListPolicyVersionsCommand({
                PolicyArn: policyArn,
            })));
            for (const version of versions.Versions || []) {
                if (!version.IsDefaultVersion) {
                    await retry(() => client.send(new DeletePolicyVersionCommand({
                        PolicyArn: policyArn,
                        VersionId: version.VersionId,
                    })));
                }
            }
            // Delete the policy
            await retry(() => client.send(new DeletePolicyCommand({
                PolicyArn: policyArn,
            })));
        }
        catch (error) {
            if (error.name !== NoSuchEntityException.name) {
                throw error;
            }
        }
        return this.destroy();
    }
    try {
        // Check if policy exists
        const existingPolicy = await retry(() => client.send(new GetPolicyCommand({
            PolicyArn: policyArn,
        })));
        // Get current policy version
        const currentVersion = await retry(() => client.send(new GetPolicyVersionCommand({
            PolicyArn: policyArn,
            VersionId: existingPolicy.Policy.DefaultVersionId,
        })));
        const currentDocument = JSON.parse(decodeURIComponent(currentVersion.PolicyVersion.Document));
        // If policy document changed, create new version
        if (JSON.stringify(currentDocument) !== JSON.stringify(props.document)) {
            // List versions to check if we need to delete old ones
            const versions = await retry(() => client.send(new ListPolicyVersionsCommand({
                PolicyArn: policyArn,
            })));
            // Delete oldest version if we have 5 versions (maximum allowed)
            if (versions.Versions?.length === 5) {
                const oldestVersion = versions.Versions.sort((a, b) => a.CreateDate.getTime() - b.CreateDate.getTime())[0];
                if (!oldestVersion.IsDefaultVersion) {
                    await retry(() => client.send(new DeletePolicyVersionCommand({
                        PolicyArn: policyArn,
                        VersionId: oldestVersion.VersionId,
                    })));
                }
            }
            // Create new version
            await retry(() => client.send(new CreatePolicyVersionCommand({
                PolicyArn: policyArn,
                PolicyDocument: JSON.stringify(props.document),
                SetAsDefault: true,
            })));
        }
        const policy = await retry(() => client.send(new GetPolicyCommand({
            PolicyArn: policyArn,
        })));
        return {
            ...props,
            arn: policy.Policy.Arn,
            policyName,
            defaultVersionId: policy.Policy.DefaultVersionId,
            attachmentCount: policy.Policy.AttachmentCount,
            createDate: policy.Policy.CreateDate,
            updateDate: policy.Policy.UpdateDate,
            isAttachable: policy.Policy.IsAttachable,
        };
    }
    catch (error) {
        if (error.name === "NoSuchEntity") {
            // Create new policy
            const newPolicy = await retry(() => client.send(new CreatePolicyCommand({
                PolicyName: policyName,
                PolicyDocument: JSON.stringify(props.document),
                Description: props.description,
                Path: props.path,
                Tags: props.tags
                    ? Object.entries(props.tags).map(([Key, Value]) => ({
                        Key,
                        Value,
                    }))
                    : undefined,
            })));
            return {
                ...props,
                arn: newPolicy.Policy.Arn,
                policyName,
                defaultVersionId: newPolicy.Policy.DefaultVersionId,
                attachmentCount: newPolicy.Policy.AttachmentCount,
                createDate: newPolicy.Policy.CreateDate,
                updateDate: newPolicy.Policy.UpdateDate,
                isAttachable: newPolicy.Policy.IsAttachable,
            };
        }
        throw error;
    }
});
//# sourceMappingURL=policy.js.map