import { XMLParser } from "fast-xml-parser";
import { Resource } from "../../resource.js";
import { ignore } from "../../util/ignore.js";
import { mergeTimeoutConfig, waitForResourceState, } from "../../util/timeout.js";
import { callEC2Api, createEC2Client } from "./utils.js";
/**
 * Security Group timeout constants
 */
export const SECURITY_GROUP_TIMEOUT = {
    maxAttempts: 30,
    delayMs: 1000, // 1 second - Security Groups are fast resources
};
/**
 * AWS Security Group Resource
 *
 * Creates and manages security groups. Acts as a virtual firewall for EC2
 * instances and other AWS resources. Rules are managed separately using the
 * `SecurityGroupRule` resource.
 *
 * Supports AWS credential overrides at the resource level, allowing you to deploy Security Groups
 * to different AWS accounts or regions than the default scope configuration.
 *
 * @example
 * ```typescript
 * // Create a basic web server security group
 * const webSecurityGroup = await SecurityGroup("web-sg", {
 *   vpc: mainVpc,
 *   groupName: "web-server-sg",
 *   description: "Security group for web servers",
 *   tags: {
 *     Name: "web-server-security-group",
 *     Environment: "production"
 *   }
 * });
 * ```
 *
 * @example
 * ```typescript
 * // Create Security Group with AWS credential overrides
 * const crossAccountSg = await SecurityGroup("cross-account-sg", {
 *   vpc: mainVpc,
 *   groupName: "cross-account-security-group",
 *   description: "Security group in different account",
 *   // Override AWS credentials for this specific resource
 *   region: "us-east-1",
 *   profile: "production-account",
 *   tags: {
 *     Name: "cross-account-sg",
 *     Environment: "production"
 *   }
 * });
 * ```
 *
 * @example
 * ```typescript
 * // Create Security Group in different region with role assumption
 * const multiRegionSg = await SecurityGroup("multi-region-sg", {
 *   vpc: euVpc,
 *   groupName: "eu-security-group",
 *   description: "Security group in EU region",
 *   region: "eu-west-1",
 *   roleArn: "arn:aws:iam::123456789012:role/CrossRegionRole",
 *   roleSessionName: "security-group-deployment",
 *   tags: {
 *     Name: "eu-security-group",
 *     Region: "europe"
 *   }
 * });
 * ```
 *
 * @example
 * ```typescript
 * // Create Security Group with explicit credentials
 * const explicitCredsSg = await SecurityGroup("explicit-creds-sg", {
 *   vpc: testVpc,
 *   groupName: "test-security-group",
 *   description: "Security group with explicit credentials",
 *   accessKeyId: alchemy.secret("AKIAIOSFODNN7EXAMPLE"),
 *   secretAccessKey: alchemy.secret("wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"),
 *   region: "us-west-2",
 *   tags: {
 *     Name: "explicit-credentials-sg",
 *     Purpose: "testing"
 *   }
 * });
 * ```
 *
 * @example
 * ```typescript
 * // Create Security Group with rules (rules managed separately)
 * const webSg = await SecurityGroup("web-sg", {
 *   vpc: mainVpc,
 *   groupName: "web-server-sg",
 *   description: "Security group for web servers",
 *   tags: { Name: "web-server-security-group" }
 * });
 *
 * // Separately, define a rule to allow HTTP traffic
 * const httpRule = await SecurityGroupRule("web-sg-http-rule", {
 *   securityGroup: webSg,
 *   type: "ingress",
 *   protocol: "tcp",
 *   fromPort: 80,
 *   toPort: 80,
 *   cidrBlocks: ["0.0.0.0/0"],
 *   description: "Allow HTTP access from anywhere"
 * });
 * ```
 *
 * @example
 * ```typescript
 * // Multi-account deployment with scope-level and resource-level overrides
 * await alchemy.run("production", {
 *   aws: { region: "us-west-2", profile: "main-account" }
 * }, async () => {
 *   // This SG uses scope credentials (main-account, us-west-2)
 *   const mainSg = await SecurityGroup("main-sg", {
 *     vpc: mainVpc,
 *     groupName: "main-account-sg",
 *     description: "Security group in main account",
 *     tags: { Name: "main-account-sg" }
 *   });
 *
 *   // This SG overrides to use different account
 *   const crossAccountSg = await SecurityGroup("cross-account-sg", {
 *     vpc: crossVpc,
 *     groupName: "secondary-account-sg",
 *     description: "Security group in secondary account",
 *     profile: "secondary-account",
 *     region: "us-east-1", // Also override region
 *     tags: { Name: "secondary-account-sg" }
 *   });
 * });
 * ```
 */
export const SecurityGroup = Resource("aws::SecurityGroup", async function (id, props) {
    // Create EC2 client with credential resolution handled internally
    const client = await createEC2Client(props);
    const vpcId = typeof props.vpc === "string" ? props.vpc : props.vpc.vpcId;
    const timeoutConfig = mergeTimeoutConfig(SECURITY_GROUP_TIMEOUT, props.timeout);
    const groupName = props.groupName ?? this.scope.createPhysicalName(id);
    if (this.phase === "update" && this.output.groupName !== groupName) {
        this.replace();
    }
    if (this.phase === "delete") {
        if (this.output?.groupId) {
            console.log(`🗑️ Deleting Security Group: ${this.output.groupId}`);
            await ignore(["InvalidGroupId.NotFound", "DependencyViolation"], async () => {
                await callEC2Api(client, "DeleteSecurityGroup", parseSecurityGroupXmlResponse, {
                    GroupId: this.output.groupId,
                });
            });
            await waitForSecurityGroupDeleted(client, this.output.groupId, timeoutConfig);
            console.log(`  ✅ Security Group ${this.output.groupId} deletion completed`);
        }
        return this.destroy();
    }
    let securityGroup;
    if (this.phase === "update" && this.output?.groupId) {
        // Get existing security group
        const response = await callEC2Api(client, "DescribeSecurityGroups", parseSecurityGroupXmlResponse, {
            "GroupId.1": this.output.groupId,
        });
        if (!response.SecurityGroups?.[0]) {
            throw new Error(`Security Group ${this.output.groupId} not found`);
        }
        securityGroup = response.SecurityGroups[0];
    }
    else {
        // Create new security group
        const createParams = {
            GroupName: groupName,
            GroupDescription: props.description,
            VpcId: vpcId,
        };
        // Add tags if specified
        if (props.tags) {
            createParams.TagSpecifications = [
                {
                    ResourceType: "security-group",
                    Tags: Object.entries(props.tags).map(([Key, Value]) => ({
                        Key,
                        Value,
                    })),
                },
            ];
        }
        const response = await callEC2Api(client, "CreateSecurityGroup", parseSecurityGroupXmlResponse, convertCreateSecurityGroupParamsToAwsFormat(createParams));
        if (!response.GroupId) {
            throw new Error("Failed to create security group");
        }
        // Wait for the security group to be available
        await waitForSecurityGroupAvailable(client, response.GroupId, timeoutConfig);
        // Get the full security group details
        const sgResponse = await callEC2Api(client, "DescribeSecurityGroups", parseSecurityGroupXmlResponse, {
            "GroupId.1": response.GroupId,
        });
        if (!sgResponse.SecurityGroups?.[0]) {
            throw new Error("Failed to retrieve created security group");
        }
        securityGroup = sgResponse.SecurityGroups[0];
    }
    return {
        groupId: securityGroup.GroupId,
        groupName,
        vpcId: securityGroup.VpcId,
        ownerId: securityGroup.OwnerId,
        ...props,
        vpc: vpcId,
    };
});
/**
 * Parse XML responses specifically for Security Group operations
 */
function parseSecurityGroupXmlResponse(xmlText) {
    const parser = new XMLParser({
        ignoreAttributes: true,
        parseAttributeValue: true,
        parseTagValue: true,
        trimValues: true,
    });
    const parsed = parser.parse(xmlText);
    const result = {};
    if (parsed.CreateSecurityGroupResponse) {
        result.GroupId = parsed.CreateSecurityGroupResponse.groupId;
    }
    if (parsed.DescribeSecurityGroupsResponse) {
        const securityGroupSet = parsed.DescribeSecurityGroupsResponse.securityGroupInfo;
        if (securityGroupSet?.item) {
            const securityGroups = Array.isArray(securityGroupSet.item)
                ? securityGroupSet.item
                : [securityGroupSet.item];
            result.SecurityGroups = securityGroups.map((sg) => ({
                GroupId: sg.groupId,
                GroupName: sg.groupName,
                Description: sg.groupDescription,
                VpcId: sg.vpcId,
                OwnerId: sg.ownerId,
            }));
        }
        else {
            result.SecurityGroups = [];
        }
    }
    if (parsed.DeleteSecurityGroupResponse) {
        result.success = true;
    }
    return result;
}
/**
 * Convert CreateSecurityGroupParams to the format required by the AWS API.
 */
function convertCreateSecurityGroupParamsToAwsFormat(params) {
    const awsParams = {
        GroupName: params.GroupName,
        GroupDescription: params.GroupDescription,
        VpcId: params.VpcId,
    };
    if (params.DryRun !== undefined) {
        awsParams.DryRun = params.DryRun.toString();
    }
    if (params.TagSpecifications) {
        params.TagSpecifications.forEach((tagSpec, specIndex) => {
            const specNum = specIndex + 1;
            awsParams[`TagSpecification.${specNum}.ResourceType`] =
                tagSpec.ResourceType;
            tagSpec.Tags.forEach((tag, tagIndex) => {
                const tagNum = tagIndex + 1;
                awsParams[`TagSpecification.${specNum}.Tag.${tagNum}.Key`] = tag.Key;
                awsParams[`TagSpecification.${specNum}.Tag.${tagNum}.Value`] =
                    tag.Value;
            });
        });
    }
    return awsParams;
}
/**
 * Wait for a security group to become available.
 */
async function waitForSecurityGroupAvailable(client, groupId, timeoutConfig) {
    const checkFunction = async () => {
        const response = await callEC2Api(client, "DescribeSecurityGroups", parseSecurityGroupXmlResponse, { "GroupId.1": groupId });
        return response.SecurityGroups?.[0];
    };
    await waitForResourceState(checkFunction, (sg) => !!sg, timeoutConfig, groupId, "Security Group", "to be available");
}
/**
 * Wait for a security group to be deleted.
 */
async function waitForSecurityGroupDeleted(client, groupId, timeoutConfig) {
    const checkFunction = async () => {
        try {
            const response = await callEC2Api(client, "DescribeSecurityGroups", parseSecurityGroupXmlResponse, { "GroupId.1": groupId });
            return response.SecurityGroups?.[0];
        }
        catch (error) {
            if (error.code === "InvalidGroup.NotFound") {
                return undefined;
            }
            throw error;
        }
    };
    await waitForResourceState(checkFunction, (sg) => !sg, timeoutConfig, groupId, "Security Group", "to be deleted");
}
//# sourceMappingURL=security-group.js.map