import { XMLParser } from "fast-xml-parser";
import { Resource } from "../../resource.js";
import { ignore } from "../../util/ignore.js";
import { logger } from "../../util/logger.js";
import { mergeTimeoutConfig, waitForResourceState, } from "../../util/timeout.js";
import { callEC2Api, createEC2Client } from "./utils.js";
/**
 * AWS VPC (Virtual Private Cloud) Resource
 *
 * Creates and manages Amazon VPC instances with configurable CIDR blocks,
 * DNS settings, and instance tenancy options. Supports AWS credential overrides
 * for multi-account and cross-region deployments.
 *
 * @example
 * // Create a basic VPC with default settings
 * const vpc = await Vpc("main-vpc", {
 *   cidrBlock: "10.0.0.0/16",
 *   tags: {
 *     Name: "main-vpc",
 *     Environment: "production"
 *   }
 * });
 *
 * @example
 * // Create a VPC with custom DNS settings
 * const vpc = await Vpc("custom-vpc", {
 *   cidrBlock: "172.16.0.0/16",
 *   enableDnsSupport: true,
 *   enableDnsHostnames: true,
 *   instanceTenancy: "default",
 *   tags: {
 *     Name: "custom-vpc"
 *   }
 * });
 *
 * @example
 * // Create a VPC with dedicated tenancy and custom timeout
 * const dedicatedVpc = await Vpc("dedicated-vpc", {
 *   cidrBlock: "192.168.0.0/16",
 *   instanceTenancy: "dedicated",
 *   enableDnsSupport: false,
 *   enableDnsHostnames: false,
 *   timeout: {
 *     maxAttempts: 90,
 *     delayMs: 3000
 *   },
 *   tags: {
 *     Name: "dedicated-vpc",
 *     Type: "isolated"
 *   }
 * });
 *
 * @example
 * // Create a VPC with AWS credential overrides for cross-account deployment
 * const crossAccountVpc = await Vpc("cross-account-vpc", {
 *   cidrBlock: "10.1.0.0/16",
 *   region: "us-east-1",
 *   profile: "production-account",
 *   tags: {
 *     Name: "cross-account-vpc",
 *     Account: "production"
 *   }
 * });
 *
 * @example
 * // Create a VPC in a different region with explicit credentials
 * const multiRegionVpc = await Vpc("multi-region-vpc", {
 *   cidrBlock: "10.2.0.0/16",
 *   region: "eu-west-1",
 *   accessKeyId: alchemy.secret("AKIA..."),
 *   secretAccessKey: alchemy.secret("..."),
 *   tags: {
 *     Name: "eu-vpc",
 *     Region: "europe"
 *   }
 * });
 *
 * @example
 * // Using scope-level credentials with resource-level overrides
 * await alchemy.run("multi-account", {
 *   aws: {
 *     region: "us-west-2",
 *     profile: "staging"
 *   }
 * }, async () => {
 *   // This VPC uses staging credentials from scope
 *   const stagingVpc = await Vpc("staging-vpc", {
 *     cidrBlock: "10.0.0.0/16"
 *   });
 *
 *   // This VPC overrides to use production credentials
 *   const prodVpc = await Vpc("prod-vpc", {
 *     cidrBlock: "10.1.0.0/16",
 *     profile: "production",
 *     region: "us-east-1"
 *   });
 * });
 */
export const Vpc = Resource("aws::Vpc", async function (_id, props) {
    // Create EC2 client with credential resolution handled internally
    const client = await createEC2Client(props);
    const timeoutConfig = mergeTimeoutConfig(VPC_TIMEOUT, props.timeout);
    if (this.phase === "delete") {
        if (this.output?.vpcId) {
            logger.log(`🗑️ Deleting VPC: ${this.output.vpcId}`);
            // Simply delete the VPC - other resources should handle themselves
            await ignore("InvalidVpcID.NotFound", async () => {
                await callEC2Api(client, "DeleteVpc", parseVpcXmlResponse, {
                    VpcId: this.output.vpcId,
                });
            });
            // Wait for VPC to be fully deleted
            await waitForVpcDeleted(client, this.output.vpcId, timeoutConfig);
            logger.log(`  ✅ VPC ${this.output.vpcId} deletion completed`);
        }
        return this.destroy();
    }
    let vpc;
    if (this.phase === "update" && this.output?.vpcId) {
        // Get existing VPC
        const response = await callEC2Api(client, "DescribeVpcs", parseVpcXmlResponse, {
            "VpcId.1": this.output.vpcId,
        });
        if (!response.Vpcs?.[0]) {
            throw new Error(`VPC ${this.output.vpcId} not found`);
        }
        vpc = response.Vpcs[0];
        // Update DNS attributes if they've changed
        if (props.enableDnsSupport !== undefined) {
            const modifyParams = {
                VpcId: vpc.VpcId,
                EnableDnsSupport: {
                    Value: props.enableDnsSupport,
                },
            };
            await callEC2Api(client, "ModifyVpcAttribute", parseVpcXmlResponse, convertModifyVpcAttributeParamsToAwsFormat(modifyParams));
        }
        if (props.enableDnsHostnames !== undefined) {
            const modifyParams = {
                VpcId: vpc.VpcId,
                EnableDnsHostnames: {
                    Value: props.enableDnsHostnames,
                },
            };
            await callEC2Api(client, "ModifyVpcAttribute", parseVpcXmlResponse, convertModifyVpcAttributeParamsToAwsFormat(modifyParams));
        }
    }
    else {
        // Create new VPC
        const createVpcParams = {
            CidrBlock: props.cidrBlock,
            InstanceTenancy: props.instanceTenancy || "default",
        };
        // Add tags if specified
        if (props.tags) {
            createVpcParams.TagSpecifications = [
                {
                    ResourceType: "vpc",
                    Tags: Object.entries(props.tags).map(([key, value]) => ({
                        Key: key,
                        Value: value,
                    })),
                },
            ];
        }
        const createParams = convertVpcParamsToAwsFormat(createVpcParams);
        const response = await callEC2Api(client, "CreateVpc", parseVpcXmlResponse, createParams);
        if (!response.Vpc) {
            throw new Error(`Failed to create VPC - Response: ${JSON.stringify(response)}`);
        }
        vpc = response.Vpc;
        // Wait for VPC to be available
        await waitForVpcAvailable(client, vpc.VpcId, timeoutConfig);
        // Set DNS attributes if specified
        if (props.enableDnsSupport !== undefined) {
            const modifyParams = {
                VpcId: vpc.VpcId,
                EnableDnsSupport: {
                    Value: props.enableDnsSupport,
                },
            };
            await callEC2Api(client, "ModifyVpcAttribute", parseVpcXmlResponse, convertModifyVpcAttributeParamsToAwsFormat(modifyParams));
        }
        if (props.enableDnsHostnames !== undefined) {
            const modifyParams = {
                VpcId: vpc.VpcId,
                EnableDnsHostnames: {
                    Value: props.enableDnsHostnames,
                },
            };
            await callEC2Api(client, "ModifyVpcAttribute", parseVpcXmlResponse, convertModifyVpcAttributeParamsToAwsFormat(modifyParams));
        }
    }
    return {
        vpcId: vpc.VpcId,
        state: vpc.State,
        isDefault: vpc.IsDefault || false,
        dhcpOptionsId: vpc.DhcpOptionsId,
        ...props,
    };
});
/**
 * VPC timeout constants
 */
export const VPC_TIMEOUT = {
    maxAttempts: 60,
    delayMs: 2000, // 2 seconds - VPCs are medium speed resources
};
/**
 * Helper function to convert typed VPC parameters to AWS API format
 */
function convertVpcParamsToAwsFormat(params) {
    const awsParams = {
        CidrBlock: params.CidrBlock,
    };
    if (params.InstanceTenancy) {
        awsParams.InstanceTenancy = params.InstanceTenancy;
    }
    if (params.AmazonProvidedIpv6CidrBlock !== undefined) {
        awsParams.AmazonProvidedIpv6CidrBlock =
            params.AmazonProvidedIpv6CidrBlock.toString();
    }
    if (params.Ipv6Pool) {
        awsParams.Ipv6Pool = params.Ipv6Pool;
    }
    if (params.Ipv6CidrBlock) {
        awsParams.Ipv6CidrBlock = params.Ipv6CidrBlock;
    }
    if (params.Ipv4IpamPoolId) {
        awsParams.Ipv4IpamPoolId = params.Ipv4IpamPoolId;
    }
    if (params.Ipv4NetmaskLength !== undefined) {
        awsParams.Ipv4NetmaskLength = params.Ipv4NetmaskLength.toString();
    }
    if (params.Ipv6IpamPoolId) {
        awsParams.Ipv6IpamPoolId = params.Ipv6IpamPoolId;
    }
    if (params.Ipv6NetmaskLength !== undefined) {
        awsParams.Ipv6NetmaskLength = params.Ipv6NetmaskLength.toString();
    }
    if (params.DryRun !== undefined) {
        awsParams.DryRun = params.DryRun.toString();
    }
    // Handle TagSpecifications
    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;
}
function convertModifyVpcAttributeParamsToAwsFormat(params) {
    const awsParams = {
        VpcId: params.VpcId,
    };
    if (params.EnableDnsHostnames) {
        awsParams["EnableDnsHostnames.Value"] =
            params.EnableDnsHostnames.Value.toString();
    }
    if (params.EnableDnsSupport) {
        awsParams["EnableDnsSupport.Value"] =
            params.EnableDnsSupport.Value.toString();
    }
    if (params.EnableNetworkAddressUsageMetrics) {
        awsParams["EnableNetworkAddressUsageMetrics.Value"] =
            params.EnableNetworkAddressUsageMetrics.Value.toString();
    }
    if (params.DryRun !== undefined) {
        awsParams.DryRun = params.DryRun.toString();
    }
    return awsParams;
}
/**
 * Parse XML responses specifically for VPC operations
 */
function parseVpcXmlResponse(xmlText) {
    const parser = new XMLParser({
        ignoreAttributes: true,
        parseAttributeValue: true,
        parseTagValue: true,
        trimValues: true,
    });
    const parsed = parser.parse(xmlText);
    const result = {};
    // Parse CreateVpcResponse
    if (parsed.CreateVpcResponse) {
        const vpc = parsed.CreateVpcResponse.vpc;
        if (vpc) {
            result.Vpc = {
                VpcId: vpc.vpcId,
                State: vpc.state,
                CidrBlock: vpc.cidrBlock,
                DhcpOptionsId: vpc.dhcpOptionsId,
                InstanceTenancy: vpc.instanceTenancy,
                IsDefault: vpc.isDefault === "true" || vpc.isDefault === true,
                Tags: vpc.tagSet?.item
                    ? Array.isArray(vpc.tagSet.item)
                        ? vpc.tagSet.item.map((tag) => ({ Key: tag.key, Value: tag.value }))
                        : [{ Key: vpc.tagSet.item.key, Value: vpc.tagSet.item.value }]
                    : undefined,
                CidrBlockAssociationSet: vpc.cidrBlockAssociationSet?.item
                    ? Array.isArray(vpc.cidrBlockAssociationSet.item)
                        ? vpc.cidrBlockAssociationSet.item.map((assoc) => ({
                            AssociationId: assoc.associationId,
                            CidrBlock: assoc.cidrBlock,
                            CidrBlockState: {
                                State: assoc.cidrBlockState.state,
                                StatusMessage: assoc.cidrBlockState.statusMessage,
                            },
                        }))
                        : [
                            {
                                AssociationId: vpc.cidrBlockAssociationSet.item.associationId,
                                CidrBlock: vpc.cidrBlockAssociationSet.item.cidrBlock,
                                CidrBlockState: {
                                    State: vpc.cidrBlockAssociationSet.item.cidrBlockState
                                        .state,
                                    StatusMessage: vpc.cidrBlockAssociationSet.item.cidrBlockState
                                        .statusMessage,
                                },
                            },
                        ]
                    : undefined,
                Ipv6CidrBlockAssociationSet: vpc.ipv6CidrBlockAssociationSet?.item
                    ? Array.isArray(vpc.ipv6CidrBlockAssociationSet.item)
                        ? vpc.ipv6CidrBlockAssociationSet.item.map((assoc) => ({
                            AssociationId: assoc.associationId,
                            Ipv6CidrBlock: assoc.ipv6CidrBlock,
                            Ipv6CidrBlockState: {
                                State: assoc.ipv6CidrBlockState.state,
                                StatusMessage: assoc.ipv6CidrBlockState.statusMessage,
                            },
                            NetworkBorderGroup: assoc.networkBorderGroup,
                            Ipv6Pool: assoc.ipv6Pool,
                        }))
                        : [
                            {
                                AssociationId: vpc.ipv6CidrBlockAssociationSet.item.associationId,
                                Ipv6CidrBlock: vpc.ipv6CidrBlockAssociationSet.item.ipv6CidrBlock,
                                Ipv6CidrBlockState: {
                                    State: vpc.ipv6CidrBlockAssociationSet.item
                                        .ipv6CidrBlockState.state,
                                    StatusMessage: vpc.ipv6CidrBlockAssociationSet.item.ipv6CidrBlockState
                                        .statusMessage,
                                },
                                NetworkBorderGroup: vpc.ipv6CidrBlockAssociationSet.item.networkBorderGroup,
                                Ipv6Pool: vpc.ipv6CidrBlockAssociationSet.item.ipv6Pool,
                            },
                        ]
                    : undefined,
                OwnerId: vpc.ownerId,
            };
        }
    }
    // Parse DescribeVpcsResponse
    if (parsed.DescribeVpcsResponse) {
        const vpcSet = parsed.DescribeVpcsResponse.vpcSet;
        if (vpcSet?.item) {
            const vpcs = Array.isArray(vpcSet.item) ? vpcSet.item : [vpcSet.item];
            result.Vpcs = vpcs.map((vpc) => ({
                VpcId: vpc.vpcId,
                State: vpc.state,
                CidrBlock: vpc.cidrBlock,
                DhcpOptionsId: vpc.dhcpOptionsId,
                InstanceTenancy: vpc.instanceTenancy,
                IsDefault: vpc.isDefault === "true" || vpc.isDefault === true,
                Tags: vpc.tagSet?.item
                    ? Array.isArray(vpc.tagSet.item)
                        ? vpc.tagSet.item.map((tag) => ({ Key: tag.key, Value: tag.value }))
                        : [{ Key: vpc.tagSet.item.key, Value: vpc.tagSet.item.value }]
                    : undefined,
                CidrBlockAssociationSet: vpc.cidrBlockAssociationSet?.item
                    ? Array.isArray(vpc.cidrBlockAssociationSet.item)
                        ? vpc.cidrBlockAssociationSet.item.map((assoc) => ({
                            AssociationId: assoc.associationId,
                            CidrBlock: assoc.cidrBlock,
                            CidrBlockState: {
                                State: assoc.cidrBlockState.state,
                                StatusMessage: assoc.cidrBlockState.statusMessage,
                            },
                        }))
                        : [
                            {
                                AssociationId: vpc.cidrBlockAssociationSet.item.associationId,
                                CidrBlock: vpc.cidrBlockAssociationSet.item.cidrBlock,
                                CidrBlockState: {
                                    State: vpc.cidrBlockAssociationSet.item.cidrBlockState
                                        .state,
                                    StatusMessage: vpc.cidrBlockAssociationSet.item.cidrBlockState
                                        .statusMessage,
                                },
                            },
                        ]
                    : undefined,
                Ipv6CidrBlockAssociationSet: vpc.ipv6CidrBlockAssociationSet?.item
                    ? Array.isArray(vpc.ipv6CidrBlockAssociationSet.item)
                        ? vpc.ipv6CidrBlockAssociationSet.item.map((assoc) => ({
                            AssociationId: assoc.associationId,
                            Ipv6CidrBlock: assoc.ipv6CidrBlock,
                            Ipv6CidrBlockState: {
                                State: assoc.ipv6CidrBlockState.state,
                                StatusMessage: assoc.ipv6CidrBlockState.statusMessage,
                            },
                            NetworkBorderGroup: assoc.networkBorderGroup,
                            Ipv6Pool: assoc.ipv6Pool,
                        }))
                        : [
                            {
                                AssociationId: vpc.ipv6CidrBlockAssociationSet.item.associationId,
                                Ipv6CidrBlock: vpc.ipv6CidrBlockAssociationSet.item.ipv6CidrBlock,
                                Ipv6CidrBlockState: {
                                    State: vpc.ipv6CidrBlockAssociationSet.item
                                        .ipv6CidrBlockState.state,
                                    StatusMessage: vpc.ipv6CidrBlockAssociationSet.item.ipv6CidrBlockState
                                        .statusMessage,
                                },
                                NetworkBorderGroup: vpc.ipv6CidrBlockAssociationSet.item.networkBorderGroup,
                                Ipv6Pool: vpc.ipv6CidrBlockAssociationSet.item.ipv6Pool,
                            },
                        ]
                    : undefined,
                OwnerId: vpc.ownerId,
            }));
        }
        else {
            result.Vpcs = [];
        }
    }
    // Parse DescribeSecurityGroupsResponse
    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,
                VpcId: sg.vpcId,
            }));
        }
        else {
            result.SecurityGroups = [];
        }
    }
    // Handle success responses
    if (parsed.ModifyVpcAttributeResponse || parsed.DeleteVpcResponse) {
        result.success = true;
    }
    return result;
}
/**
 * Wait for VPC to be in available state
 */
async function waitForVpcAvailable(client, vpcId, timeoutConfig) {
    const checkFunction = async () => {
        const response = await callEC2Api(client, "DescribeVpcs", parseVpcXmlResponse, {
            "VpcId.1": vpcId,
        });
        return response.Vpcs?.[0];
    };
    const isReady = (vpc) => {
        return vpc?.State === "available";
    };
    await waitForResourceState(checkFunction, isReady, timeoutConfig, vpcId, "VPC", "is now available");
}
/**
 * Wait for VPC to be deleted
 */
async function waitForVpcDeleted(client, vpcId, timeoutConfig) {
    const checkFunction = async () => {
        try {
            const response = await callEC2Api(client, "DescribeVpcs", parseVpcXmlResponse, {
                "VpcId.1": vpcId,
            });
            return response.Vpcs?.[0];
        }
        catch (error) {
            // If VPC is not found, it's been deleted - return undefined
            if (error.code === "InvalidVpcID.NotFound" ||
                error.code === "InvalidVpc.NotFound") {
                return undefined;
            }
            throw error;
        }
    };
    const isReady = (vpc) => {
        // VPC is deleted if it doesn't exist
        return !vpc;
    };
    await waitForResourceState(checkFunction, isReady, timeoutConfig, vpcId, "VPC", "be deleted");
}
//# sourceMappingURL=vpc.js.map