"""
zones.py — Terraform skill pack zones for NEXUS.
"""

from engine.zone import Zone
from engine.challenge import Challenge

ZONE_ORDER = [
    "hcl_fundamentals",
    "variables_and_outputs",
    "state_management",
    "providers_and_registry",
    "resource_lifecycle",
    "modules",
    "data_sources",
    "workspaces_and_environments",
    "plan_and_apply_workflow",
    "aws_with_terraform",
    "advanced_patterns",
]

ZONES = {
    "hcl_fundamentals": Zone(
        id="hcl_fundamentals",
        title="HCL Fundamentals",
        description="HCL syntax, resources, providers, arguments, and blocks.",
        challenges=[
            Challenge(
                id="tf_hcl_what_is",
                type="quiz",
                prompt="HCL (HashiCorp Configuration Language) is:",
                options=[
                    "A compiled programming language used to write Terraform providers",
                    "A human-readable declarative configuration language used to write Terraform configs",
                    "A YAML superset used only for HashiCorp Vault",
                    "A query language for querying Terraform state",
                ],
                answer="b",
                explanation="HCL is a declarative, human-readable configuration language. You describe the desired end state — Terraform figures out the API calls. It's not a programming language; it has no loops at the expression level (without meta-arguments), no functions to define, and intentionally limited control flow.",
                url="https://developer.hashicorp.com/terraform/language/syntax/configuration",
                xp=10,
                hints=["It's declarative, not imperative.", "HashiCorp Configuration Language"],
            ),
            Challenge(
                id="tf_resource_block",
                type="fill_blank",
                prompt="In HCL, the block type used to declare a managed infrastructure object (like an EC2 instance or S3 bucket) is the ___ block.",
                answer="resource",
                explanation='A resource block declares a managed infrastructure object. The syntax is: `resource "aws_instance" "web" { ... }`. The first label is the resource type; the second is the local name used to reference it elsewhere.',
                url="https://developer.hashicorp.com/terraform/language/resources/syntax",
                xp=15,
                hints=["It's the most common block type in Terraform.", "You use it to create EC2, S3, etc."],
            ),
            Challenge(
                id="tf_provider_block",
                type="quiz",
                prompt="In a Terraform configuration, the `provider` block is used to:",
                options=[
                    "Define input variables that can be passed at runtime",
                    "Configure the plugin that translates resource blocks into API calls for a specific platform",
                    "Declare outputs to expose after apply",
                    "Reference existing infrastructure not managed by this configuration",
                ],
                answer="b",
                explanation='The provider block configures a provider plugin — for example, `provider "aws" { region = "us-east-1" }`. Providers are plugins that know how to talk to a specific API (AWS, Azure, GCP, Kubernetes, etc.).',
                url="https://developer.hashicorp.com/terraform/language/providers/configuration",
                xp=10,
                hints=["Think of it as the plugin configuration.", "It specifies region, credentials, etc."],
            ),
            Challenge(
                id="tf_argument_vs_block",
                type="quiz",
                prompt="In HCL, what is the difference between an argument and a nested block?",
                options=[
                    "Arguments use = to assign a value; nested blocks use { } to group related arguments",
                    "Arguments are optional; nested blocks are required",
                    "Arguments are for strings only; blocks are for numbers",
                    "There is no difference — they are interchangeable",
                ],
                answer="a",
                explanation='Arguments assign a value: `ami = "ami-0c55b159cbfafe1f0"`. Nested blocks group related configuration without `=`: `tags { Name = "web" }` or `lifecycle { create_before_destroy = true }`. The distinction matters — using `=` on a block type is a syntax error.',
                url="https://developer.hashicorp.com/terraform/language/syntax/configuration",
                xp=10,
                hints=["One uses `=`, the other uses `{}`.", "Think key=value vs grouped configuration."],
            ),
            Challenge(
                id="tf_terraform_block",
                type="quiz",
                prompt="The special `terraform` block at the top of a configuration is used to:",
                options=[
                    "Define all resource types that will be created",
                    "Specify required providers, Terraform version constraints, and backend configuration",
                    "List all output values for the module",
                    "Configure workspace-specific variable overrides",
                ],
                answer="b",
                explanation='The `terraform {}` block controls Terraform\'s own behavior: `required_version` pins the CLI version, `required_providers` pins provider versions with source addresses, and `backend` configures where state is stored. Example: `terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } } }`.',
                url="https://developer.hashicorp.com/terraform/language/terraform",
                xp=10,
                hints=["It controls Terraform itself, not the infrastructure.", "Backend and required_providers live here."],
            ),
            Challenge(
                id="tf_hcl_boss",
                type="quiz",
                prompt="A Terraform configuration has this block:\n\n  resource \"aws_s3_bucket\" \"evidence\" {\n    bucket = \"nexus-evidence-${var.env}\"\n    tags = {\n      Owner = \"nexus-ops\"\n    }\n  }\n\nWhich statements are true? (Choose the BEST answer)",
                options=[
                    "`tags` is an argument assigned with `=`, and its value is an object expression",
                    "`tags` is a nested block because it uses curly braces",
                    "The `${}` syntax is invalid in HCL resource arguments",
                    "`bucket` is a nested block because it contains a string interpolation",
                ],
                answer="a",
                explanation='`tags = { ... }` is an argument (note the `=`) whose value is an object literal. Nested blocks do NOT use `=`. The `${var.env}` is string interpolation — a core HCL feature for embedding expressions in strings. Understanding this distinction prevents common syntax errors.',
                url="https://developer.hashicorp.com/terraform/language/expressions/strings",
                xp=25,
                hints=["Look carefully: does `tags` use `=`?", "Object literals and nested blocks look similar but are syntactically different."],
                is_boss=True,
            ),
        ],
    ),
    "variables_and_outputs": Zone(
        id="variables_and_outputs",
        title="Variables and Outputs",
        description="Input variables, output values, locals, and variable types.",
        challenges=[
            Challenge(
                id="tf_variable_block",
                type="quiz",
                prompt="In Terraform, a `variable` block defines:",
                options=[
                    "A computed value derived from resource attributes after apply",
                    "An input parameter that can be set at runtime via CLI, .tfvars files, or environment variables",
                    "A local alias used only within the current module",
                    "A reference to a data source from an external provider",
                ],
                answer="b",
                explanation='Variables are input parameters: `variable "env" { type = string default = "prod" }`. Set them with `TF_VAR_env=staging terraform apply`, in a `terraform.tfvars` file, or with `-var="env=staging"`. They make configurations reusable across environments.',
                url="https://developer.hashicorp.com/terraform/language/values/variables",
                xp=10,
                hints=["Think of them as function arguments for your configuration.", "They can be set from outside the configuration."],
            ),
            Challenge(
                id="tf_output_block",
                type="fill_blank",
                prompt="To expose a value from a Terraform module (like a resource's ARN or IP address) so it can be consumed by other modules or printed after apply, you use an ___ block.",
                answer="output",
                explanation='Output blocks expose values: `output "bucket_arn" { value = aws_s3_bucket.evidence.arn }`. After `terraform apply`, outputs are printed. In root modules they\'re informational; in child modules they\'re the only way to pass values back to the calling module.',
                url="https://developer.hashicorp.com/terraform/language/values/outputs",
                xp=15,
                hints=["It 'outputs' values to the caller or terminal.", "It's the opposite of a variable block."],
            ),
            Challenge(
                id="tf_locals_block",
                type="quiz",
                prompt="Terraform `locals` are used to:",
                options=[
                    "Store Terraform state locally instead of in a remote backend",
                    "Define named expressions that avoid repetition within a module",
                    "Declare local provider configurations that override global settings",
                    "Set default values for variables that haven't been provided",
                ],
                answer="b",
                explanation='Locals are named intermediate values: `locals { full_name = "${var.project}-${var.env}" }`. Referenced as `local.full_name`. Unlike variables, locals cannot be overridden externally — they\'re purely internal computation aliases. Use them to avoid repeating complex expressions.',
                url="https://developer.hashicorp.com/terraform/language/values/locals",
                xp=10,
                hints=["They're like constants or aliases within a module.", "They use `local.name` to reference."],
            ),
            Challenge(
                id="tf_variable_types",
                type="quiz",
                prompt="Which is a valid Terraform variable type constraint?",
                options=[
                    "type = json",
                    "type = list(string)",
                    "type = array[number]",
                    "type = dict(string, number)",
                ],
                answer="b",
                explanation='Terraform\'s type system includes: `string`, `number`, `bool`, `list(TYPE)`, `set(TYPE)`, `map(TYPE)`, `object({ key = TYPE })`, and `tuple([TYPE, ...])`. `list(string)` is a list of strings. JSON and array are not valid type constraints.',
                url="https://developer.hashicorp.com/terraform/language/expressions/types",
                xp=10,
                hints=["Terraform has its own type system.", "Think list, map, set, object."],
            ),
            Challenge(
                id="tf_tfvars_file",
                type="fill_blank",
                prompt="Terraform automatically loads variable definitions from a file named `terraform.tfvars` or any file ending in `.___.tfvars`.",
                answer="auto",
                explanation='Files named `terraform.tfvars` or `*.auto.tfvars` are loaded automatically. Other `.tfvars` files must be passed explicitly with `-var-file=staging.tfvars`. The auto-load behavior is convenient but means any `.auto.tfvars` file in the directory will affect your apply.',
                url="https://developer.hashicorp.com/terraform/language/values/variables#variable-definitions-tfvars-files",
                xp=15,
                hints=["It's a suffix that triggers automatic loading.", "Like `staging.auto.tfvars`."],
            ),
            Challenge(
                id="tf_vars_outputs_boss",
                type="quiz",
                prompt="A module has `variable \"region\" { type = string }` with no default. The calling module invokes it without passing `region`. An `output \"arn\"` in the child module references `aws_s3_bucket.main.arn`. Which statements are true?",
                options=[
                    "Terraform will error at plan time because `region` has no default and wasn't provided",
                    "Terraform will use `us-east-1` as the default region automatically",
                    "The output `arn` can be accessed in the parent as `module.child.arn` after apply",
                    "Both A and C are true",
                ],
                answer="d",
                explanation='Both are correct: (A) Terraform will error — variables without defaults are required. (C) Module outputs are accessed as `module.<MODULE_NAME>.<OUTPUT_NAME>` — so `module.child.arn`. This tests the full variable/output contract: inputs must be satisfied, outputs are the only return channel from child modules.',
                url="https://developer.hashicorp.com/terraform/language/modules/syntax",
                xp=25,
                hints=["What happens when a required variable is missing?", "How do you reference outputs from a child module?"],
                is_boss=True,
            ),
        ],
    ),
    "state_management": Zone(
        id="state_management",
        title="State Management",
        description="terraform.tfstate, remote state, state locking, and terraform state commands.",
        challenges=[
            Challenge(
                id="tf_state_purpose",
                type="quiz",
                prompt="The Terraform state file (`terraform.tfstate`) serves as:",
                options=[
                    "A log of all CLI commands ever run against the configuration",
                    "The mapping between configuration resources and real infrastructure objects",
                    "A cache of provider plugin binaries",
                    "An encrypted backup of your Terraform variables",
                ],
                answer="b",
                explanation="State maps your configuration's resource blocks to real-world infrastructure IDs. When you write `resource \"aws_instance\" \"web\"`, Terraform needs to know which EC2 instance ID (`i-0abc...`) that corresponds to. Without state, Terraform can't determine what exists, what needs to change, or what to destroy.",
                url="https://developer.hashicorp.com/terraform/language/state",
                xp=10,
                hints=["It's the 'memory' of what Terraform has created.", "It stores resource IDs from the cloud provider."],
            ),
            Challenge(
                id="tf_remote_state",
                type="quiz",
                prompt="Why should teams use remote state (e.g., S3 + DynamoDB) instead of local state files?",
                options=[
                    "Remote state is faster to read than local disk",
                    "Local state files can't store more than 1MB of data",
                    "Remote state enables team collaboration, state locking to prevent concurrent runs, and keeps secrets off developer laptops",
                    "Terraform Cloud requires remote state for all paid plans",
                ],
                answer="c",
                explanation='Remote state (e.g., S3 backend + DynamoDB lock table) solves three team problems: (1) Shared access — everyone reads the same state. (2) State locking — DynamoDB prevents two `terraform apply` runs from corrupting state simultaneously. (3) Security — state often contains sensitive values; S3 with encryption is safer than laptop files.',
                url="https://developer.hashicorp.com/terraform/language/state/remote",
                xp=10,
                hints=["Think about what happens when two engineers run apply at the same time.", "State files can contain sensitive values."],
            ),
            Challenge(
                id="tf_backend_config",
                type="fill_blank",
                prompt="The Terraform configuration block that specifies where state is stored (e.g., S3, GCS, Terraform Cloud) is called the ___.",
                answer="backend",
                explanation='The backend is declared inside the `terraform {}` block: `terraform { backend "s3" { bucket = "my-tfstate" key = "prod/terraform.tfstate" region = "us-east-1" dynamodb_table = "terraform-locks" } }`. Changing backends requires `terraform init` to migrate state.',
                url="https://developer.hashicorp.com/terraform/language/backend",
                xp=15,
                hints=["It's a block inside the `terraform {}` block.", "S3, GCS, and azurerm are common examples."],
            ),
            Challenge(
                id="tf_state_list",
                type="quiz",
                prompt="The command `terraform state list` is used to:",
                options=[
                    "Show the pending changes that will be applied on next `terraform apply`",
                    "List all resource addresses tracked in the current state",
                    "Display the contents of all .tfstate files in the directory",
                    "List all available Terraform versions",
                ],
                answer="b",
                explanation='`terraform state list` prints every resource address in state, like `aws_s3_bucket.evidence` or `module.vpc.aws_vpc.main`. Use it to verify what Terraform is tracking. Related commands: `terraform state show <address>` (inspect a resource\'s state), `terraform state rm <address>` (remove from state without destroying), `terraform state mv` (rename/move resources).',
                url="https://developer.hashicorp.com/terraform/cli/commands/state/list",
                xp=10,
                hints=["It lists addresses, not configuration blocks.", "Like `ls` but for your state."],
            ),
            Challenge(
                id="tf_state_lock",
                type="quiz",
                prompt="State locking in Terraform means:",
                options=[
                    "Encrypting the state file so only authorized users can read it",
                    "Preventing the state file from being modified while a Terraform operation is in progress",
                    "Restricting which resource types can be added to state",
                    "Making the state file read-only permanently",
                ],
                answer="b",
                explanation='State locking prevents concurrent writes. With S3+DynamoDB, `terraform plan` and `apply` acquire a lock before modifying state. If another operation holds the lock, Terraform waits or errors. A failed apply can leave a stale lock — use `terraform force-unlock <LOCK_ID>` to remove it (carefully!).',
                url="https://developer.hashicorp.com/terraform/language/state/locking",
                xp=10,
                hints=["Think database transaction isolation.", "DynamoDB is the typical lock backend for S3 state."],
            ),
            Challenge(
                id="tf_state_boss",
                type="quiz",
                prompt="The NEXUS state file at `s3://nexus-tfstate/prod/terraform.tfstate` was manually edited — a resource entry was removed without destroying the real infrastructure. What are the consequences, and what command would you use to re-import that infrastructure into state?",
                options=[
                    "No consequence — Terraform will auto-detect the resource on next plan. No import needed.",
                    "Terraform will think the resource doesn't exist and attempt to CREATE a new one, causing a conflict. Use `terraform import` to bring the existing resource back into state.",
                    "Terraform will detect the orphaned resource and automatically run destroy on it.",
                    "Terraform will lock the state permanently and refuse to run until manually unlocked.",
                ],
                answer="b",
                explanation='State drift from manual edits is dangerous. Terraform only knows what\'s in state — if a resource is missing from state but exists in the config, Terraform will try to create it, which fails if the resource already exists (duplicate name/ID conflict). Fix: `terraform import aws_s3_bucket.evidence nexus-evidence-prod` associates the existing resource with the config block. Always use `terraform state rm` (not manual edits) to remove things from state.',
                url="https://developer.hashicorp.com/terraform/cli/commands/import",
                xp=25,
                hints=["What does Terraform see if something is in config but not in state?", "There's a specific command to associate existing resources with state."],
                is_boss=True,
            ),
        ],
    ),
    "providers_and_registry": Zone(
        id="providers_and_registry",
        title="Providers and Registry",
        description="Provider blocks, version constraints, the Terraform Registry, and HashiCorp providers.",
        challenges=[
            Challenge(
                id="tf_provider_what",
                type="quiz",
                prompt="A Terraform provider is:",
                options=[
                    "A pre-built Terraform module available in the public registry",
                    "A plugin that translates Terraform resource blocks into API calls for a specific platform or service",
                    "A Terraform Cloud workspace that hosts your configuration",
                    "An HCL file that declares all required variables",
                ],
                answer="b",
                explanation="Providers are the bridge between Terraform and the outside world. The AWS provider knows how to call `ec2:RunInstances`; the Kubernetes provider knows how to call the Kubernetes API. Without a provider, Terraform has no idea what to do with a resource block. Providers are downloaded by `terraform init` from the Terraform Registry.",
                url="https://developer.hashicorp.com/terraform/language/providers",
                xp=10,
                hints=["They're plugins downloaded during `terraform init`.", "AWS, Azure, GCP, Kubernetes are all examples."],
            ),
            Challenge(
                id="tf_provider_source",
                type="fill_blank",
                prompt="In a `required_providers` block, the `source` attribute uses the format `<NAMESPACE>/___` to identify a provider on the Terraform Registry.",
                answer="provider",
                explanation='The full source address is `<HOSTNAME>/<NAMESPACE>/<TYPE>`. For public registry providers, the hostname defaults to `registry.terraform.io`, so you write: `aws = { source = "hashicorp/aws" }`. The `hashicorp` namespace hosts official HashiCorp-maintained providers. Third-party providers use their own namespace: `source = "datadog/datadog"`.',
                url="https://developer.hashicorp.com/terraform/language/providers/requirements",
                xp=15,
                hints=["hashicorp/aws — what is `aws` here?", "The format is namespace/TYPE."],
            ),
            Challenge(
                id="tf_version_constraint",
                type="quiz",
                prompt="In a `required_providers` block, `version = \"~> 5.0\"` means:",
                options=[
                    "Exactly version 5.0.0 — no other version is acceptable",
                    "Any version >= 5.0 and < 6.0 (allows patch and minor updates within the 5.x series)",
                    "Any version >= 5.0 with no upper bound",
                    "Version 5.0 or lower",
                ],
                answer="b",
                explanation='The `~>` operator is the "pessimistic constraint operator." `~> 5.0` allows `>= 5.0, < 6.0`. `~> 5.0.1` allows `>= 5.0.1, < 5.1.0`. Other operators: `>=` (minimum), `<=` (maximum), `!=` (exclude), `=` (exact). Use `~>` to accept patch updates but guard against breaking major/minor changes.',
                url="https://developer.hashicorp.com/terraform/language/expressions/version-constraints",
                xp=10,
                hints=["The `~>` is called the pessimistic constraint operator.", "It allows updates within the same minor or patch series."],
            ),
            Challenge(
                id="tf_registry_url",
                type="quiz",
                prompt="The Terraform Registry (registry.terraform.io) provides:",
                options=[
                    "Only official HashiCorp providers",
                    "Public providers, modules, and policy libraries from HashiCorp and community contributors",
                    "A private artifact store for internal Terraform configurations",
                    "Pre-built Terraform state files for common architectures",
                ],
                answer="b",
                explanation="The public Terraform Registry hosts providers (AWS, Azure, GCP, 3,000+ providers) and modules (reusable configurations). HashiCorp maintains official providers under the `hashicorp` namespace; community providers use their own namespaces. Modules can be sourced directly: `source = \"terraform-aws-modules/vpc/aws\"`.",
                url="https://developer.hashicorp.com/terraform/registry",
                xp=10,
                hints=["It's like npm but for Terraform providers and modules.", "Community contributors can publish too."],
            ),
            Challenge(
                id="tf_provider_alias",
                type="quiz",
                prompt="A Terraform configuration needs to deploy resources in two AWS regions. How is this achieved?",
                options=[
                    "Declare two separate `resource` blocks with a `region` argument",
                    "Use `terraform workspace` to switch regions between applies",
                    "Declare two `provider \"aws\"` blocks with `alias` and reference the alias in resources using `provider = aws.<ALIAS>`",
                    "Set the `AWS_REGION` environment variable and run `terraform apply` twice",
                ],
                answer="c",
                explanation='Provider aliases allow multiple instances of the same provider. Example: `provider "aws" { alias = "us_east" region = "us-east-1" }` and `provider "aws" { alias = "eu_west" region = "eu-west-1" }`. Resources specify `provider = aws.eu_west` to use the aliased config. Without an alias, there can only be one default provider per type.',
                url="https://developer.hashicorp.com/terraform/language/providers/configuration#alias-multiple-provider-configurations",
                xp=10,
                hints=["One configuration, two regions — you need two provider instances.", "The `alias` meta-argument is key."],
            ),
            Challenge(
                id="tf_providers_boss",
                type="quiz",
                prompt="After running `terraform init`, you examine `.terraform.lock.hcl`. What is its purpose, and why should it be committed to version control?",
                options=[
                    "It stores your Terraform Cloud API token — should NOT be committed",
                    "It records the exact provider versions and checksums selected during init, ensuring all team members and CI/CD use identical provider versions",
                    "It caches provider binaries to speed up future inits — optional to commit",
                    "It locks the Terraform CLI version to prevent accidental upgrades",
                ],
                answer="b",
                explanation='`.terraform.lock.hcl` is the provider dependency lock file. It records exact versions AND cryptographic checksums (hashes) for each provider. Committing it ensures reproducibility: everyone gets exactly the same provider version regardless of when they run `terraform init`. Without it, `~> 5.0` might resolve to 5.0.1 for one engineer and 5.3.0 for another. Run `terraform providers lock` to update it after changing version constraints.',
                url="https://developer.hashicorp.com/terraform/language/dependency-lock",
                xp=25,
                hints=["It's generated by `terraform init`.", "Think about reproducibility across team members."],
                is_boss=True,
            ),
        ],
    ),
    "resource_lifecycle": Zone(
        id="resource_lifecycle",
        title="Resource Lifecycle",
        description="Create/read/update/delete, depends_on, lifecycle blocks, and create_before_destroy.",
        challenges=[
            Challenge(
                id="tf_crud_terraform",
                type="quiz",
                prompt="When you run `terraform apply` after removing a resource block from your configuration, Terraform will:",
                options=[
                    "Ignore the removed block and leave the real resource running",
                    "Prompt you to rename the resource before proceeding",
                    "Plan to DESTROY the real infrastructure resource that block was managing",
                    "Move the resource to a 'decommissioned' state without deleting it",
                ],
                answer="c",
                explanation="Terraform owns the full lifecycle: create (resource added to config), update (resource arguments changed), and destroy (resource removed from config). Removing a resource block tells Terraform the desired state is that resource not existing — so it plans a destroy. This is why `terraform plan` should always be reviewed before `apply`.",
                url="https://developer.hashicorp.com/terraform/language/resources/behavior",
                xp=10,
                hints=["Terraform reconciles desired state with actual state.", "Desired state = config; actual state = what exists."],
            ),
            Challenge(
                id="tf_depends_on",
                type="fill_blank",
                prompt="When Terraform cannot automatically infer a dependency between two resources (because the dependency doesn't flow through an attribute reference), you explicitly declare it with the `___` meta-argument.",
                answer="depends_on",
                explanation='`depends_on` forces explicit ordering: `resource "aws_iam_role_policy" "policy" { depends_on = [aws_iam_role.role] }`. Terraform normally infers dependencies from attribute references (`${aws_iam_role.role.name}`). Use `depends_on` only when there\'s a hidden dependency — like an IAM policy that must exist before a resource can use the permissions, even if the ARN isn\'t referenced.',
                url="https://developer.hashicorp.com/terraform/language/meta-arguments/depends_on",
                xp=15,
                hints=["It's a meta-argument that affects ordering.", "Use it when there's no attribute reference to create the implicit dependency."],
            ),
            Challenge(
                id="tf_lifecycle_block",
                type="quiz",
                prompt="The `lifecycle { create_before_destroy = true }` setting in a resource block tells Terraform to:",
                options=[
                    "Always create a new instance of the resource on every apply",
                    "Create the replacement resource before destroying the old one, avoiding downtime",
                    "Prevent the resource from ever being destroyed",
                    "Run the resource creation in a separate Terraform plan phase",
                ],
                answer="b",
                explanation='By default, Terraform destroys then creates. `create_before_destroy = true` reverses this: the new resource is created first, then the old one is destroyed. Critical for zero-downtime replacements of EC2 instances, SSL certificates, or Auto Scaling Launch Configs. Note: the new resource must be able to coexist with the old one temporarily (e.g., unique name constraints can prevent this).',
                url="https://developer.hashicorp.com/terraform/language/meta-arguments/lifecycle",
                xp=10,
                hints=["Think about downtime during resource replacement.", "New first, then destroy old."],
            ),
            Challenge(
                id="tf_prevent_destroy",
                type="quiz",
                prompt="A production database has `lifecycle { prevent_destroy = true }` set. What happens if you run `terraform destroy`?",
                options=[
                    "The destroy is skipped silently for that resource but proceeds for all others",
                    "Terraform errors and refuses to create a plan that would destroy that resource",
                    "The resource is moved to a 'protected' state and can only be deleted from the AWS console",
                    "Terraform prompts for a special confirmation passphrase before destroying",
                ],
                answer="b",
                explanation='`prevent_destroy = true` causes `terraform plan` to error if any plan includes destroying that resource. The message is: "Error: Instance cannot be destroyed." This is a guardrail — removing the `prevent_destroy` setting (and applying) is the only way to allow destruction. Common use: protecting databases, S3 buckets with data, or any infrastructure where accidental deletion would be catastrophic.',
                url="https://developer.hashicorp.com/terraform/language/meta-arguments/lifecycle#prevent_destroy",
                xp=10,
                hints=["It causes an error at plan time.", "To remove the protection, you remove the lifecycle block."],
            ),
            Challenge(
                id="tf_ignore_changes",
                type="quiz",
                prompt="The `lifecycle { ignore_changes = [tags] }` setting is useful when:",
                options=[
                    "You want Terraform to always enforce the exact tag values from your configuration",
                    "An external system (like AWS autoscaling or a tag management tool) modifies resource attributes, and you don't want Terraform to revert those changes on every apply",
                    "You want to disable all Terraform plan drift detection for this resource",
                    "You're importing a resource that was created outside Terraform",
                ],
                answer="b",
                explanation='`ignore_changes` tells Terraform to ignore specific attribute changes when detecting drift. Useful when an external system legitimately modifies attributes — like AWS Auto Scaling changing desired capacity, or a tag management system applying company-wide tags. Without `ignore_changes`, Terraform would detect drift and attempt to revert those external changes on every apply.',
                url="https://developer.hashicorp.com/terraform/language/meta-arguments/lifecycle#ignore_changes",
                xp=10,
                hints=["Think about external systems modifying your resources.", "You're telling Terraform: 'don't fight over these attributes'."],
            ),
            Challenge(
                id="tf_lifecycle_boss",
                type="quiz",
                prompt="You're replacing an AWS ACM SSL certificate attached to an Application Load Balancer. The certificate resource requires replacement (not update-in-place). Which lifecycle setting prevents downtime, and what constraint must your configuration satisfy for it to work?",
                options=[
                    "`prevent_destroy = true` — prevents the old cert from being removed before the new one is ready",
                    "`create_before_destroy = true` — creates the new certificate first, but requires the new and old certs to be able to coexist (they have different ARNs, so this works fine)",
                    "`ignore_changes = [certificate_arn]` — tells the ALB to ignore certificate changes",
                    "`create_before_destroy = true` — but this fails for ACM certificates because only one cert per domain is allowed",
                ],
                answer="b",
                explanation='ACM certificates have unique ARNs (not domain-unique constraints in the same way), so `create_before_destroy = true` works: the new cert is provisioned and validated first, then the old cert is deleted. The ALB listener reference would then update to the new ARN. The critical insight is that `create_before_destroy` requires the old and new resource to temporarily coexist — for resources with globally-unique name constraints (like some S3 bucket names or IAM role names without randomness), this pattern can fail.',
                url="https://developer.hashicorp.com/terraform/language/meta-arguments/lifecycle",
                xp=25,
                hints=["Zero downtime = new before old.", "Does ACM allow two certs to exist simultaneously?"],
                is_boss=True,
            ),
        ],
    ),
    "modules": Zone(
        id="modules",
        title="Modules",
        description="Module blocks, calling modules, module sources, module outputs, and the public registry.",
        challenges=[
            Challenge(
                id="tf_module_what",
                type="quiz",
                prompt="In Terraform, a module is:",
                options=[
                    "A provider plugin downloaded from the Terraform Registry",
                    "A collection of Terraform configuration files in a directory, used to encapsulate and reuse infrastructure patterns",
                    "A special Terraform Cloud feature for team collaboration",
                    "An encrypted container for sensitive variable values",
                ],
                answer="b",
                explanation="A module is simply a directory of `.tf` files. Every Terraform configuration is a module — the root module. You create child modules by organizing related resources into a subdirectory with their own variables and outputs. Modules enable reuse: define a VPC module once, call it multiple times for dev/staging/prod with different variables.",
                url="https://developer.hashicorp.com/terraform/language/modules",
                xp=10,
                hints=["It's just a directory of .tf files.", "Every configuration is already a module."],
            ),
            Challenge(
                id="tf_module_source",
                type="quiz",
                prompt="Which of the following is a valid `source` value for a module block?",
                options=[
                    "source = \"aws_vpc\" (just the resource type)",
                    "source = \"./modules/vpc\" (local path)",
                    "source = \"hashicorp::vpc::aws\" (double-colon notation)",
                    "source = \"@terraform-modules/aws-vpc\" (npm-style)",
                ],
                answer="b",
                explanation='Module sources can be: local paths (`./modules/vpc`), Terraform Registry (`terraform-aws-modules/vpc/aws`), GitHub (`github.com/org/repo//modules/vpc`), S3 (`s3::https://s3.amazonaws.com/bucket/module.zip`), or HTTP archives. Local paths must start with `./` or `../`. Registry modules use `<NAMESPACE>/<MODULE>/<PROVIDER>` format.',
                url="https://developer.hashicorp.com/terraform/language/modules/sources",
                xp=10,
                hints=["Local paths must start with ./", "Registry modules use namespace/name/provider format."],
            ),
            Challenge(
                id="tf_module_call",
                type="fill_blank",
                prompt="To call a child module, you use a ___ block with a label that becomes the module's local name.",
                answer="module",
                explanation='Module calls use the `module` block: `module "vpc" { source = "./modules/vpc" cidr_block = "10.0.0.0/16" }`. The label `vpc` becomes the local name, referenced as `module.vpc.vpc_id` (accessing the `vpc_id` output). Each `module` block creates one instance of the child module.',
                url="https://developer.hashicorp.com/terraform/language/modules/syntax",
                xp=15,
                hints=["It's the same keyword as the block type.", "Like `module \"vpc\" { source = \"./modules/vpc\" }`"],
            ),
            Challenge(
                id="tf_module_version",
                type="quiz",
                prompt="When sourcing a module from the Terraform Registry, you should use the `version` argument because:",
                options=[
                    "Registry modules require a version argument by Terraform's syntax rules",
                    "Without a version constraint, Terraform always uses v1.0.0",
                    "Without a version constraint, Terraform uses the latest version, which may introduce breaking changes unpredictably",
                    "Version constraints are only needed for provider blocks, not module blocks",
                ],
                answer="c",
                explanation='Without `version`, Terraform fetches the latest registry module version every time, which can introduce breaking changes in production. Always pin: `version = "~> 5.0"`. This applies the same constraint semantics as provider versions. Local path modules don\'t support `version` — it\'s only for registry sources.',
                url="https://developer.hashicorp.com/terraform/language/modules/syntax#version",
                xp=10,
                hints=["Unpinned = unpredictable.", "Same version constraint syntax as providers."],
            ),
            Challenge(
                id="tf_module_output_access",
                type="quiz",
                prompt="A child module at `./modules/database` has `output \"connection_string\" { ... }`. How does the root module access this value?",
                options=[
                    "output.database.connection_string",
                    "module.database.connection_string",
                    "var.database.connection_string",
                    "data.module.database.connection_string",
                ],
                answer="b",
                explanation='Module outputs are accessed as `module.<MODULE_LABEL>.<OUTPUT_NAME>`. If you called the module with `module "database" { source = \"./modules/database\" }`, then the output is at `module.database.connection_string`. This is the only way to consume values from a child module — outputs are the module\'s public API.',
                url="https://developer.hashicorp.com/terraform/language/modules/syntax#accessing-module-output-values",
                xp=10,
                hints=["The prefix is `module.`, then the module label.", "module.NAME.output_name"],
            ),
            Challenge(
                id="tf_modules_boss",
                type="quiz",
                prompt="You have a registry module `source = \"terraform-aws-modules/vpc/aws\" version = \"~> 5.0\"`. The module creates a VPC and outputs `vpc_id` and `private_subnets`. You need to pass `private_subnets` to an EKS cluster module. Which approach correctly threads this dependency?",
                options=[
                    "Access it directly: `aws_vpc.main.private_subnets` (resource attribute)",
                    "Declare a variable in the root module and manually copy the subnet IDs in",
                    "Pass it as `subnet_ids = module.vpc.private_subnets` in the EKS module block — Terraform infers the dependency ordering automatically",
                    "Use a `data \"terraform_remote_state\"` block to read VPC outputs from a separate state file",
                ],
                answer="c",
                explanation='`module.vpc.private_subnets` is the correct reference. By using a module output as an argument to another module, Terraform automatically creates an implicit dependency — the VPC module will be applied before the EKS module. This is the standard composition pattern: modules expose outputs, consumers reference them, Terraform builds the dependency graph. No explicit `depends_on` needed here.',
                url="https://developer.hashicorp.com/terraform/language/modules/syntax",
                xp=25,
                hints=["Module outputs flow to other module inputs.", "Does Terraform infer ordering from attribute references?"],
                is_boss=True,
            ),
        ],
    ),
    "data_sources": Zone(
        id="data_sources",
        title="Data Sources",
        description="Data blocks, querying existing resources, aws_ami and aws_vpc data sources.",
        challenges=[
            Challenge(
                id="tf_data_source_what",
                type="quiz",
                prompt="A Terraform `data` block is used to:",
                options=[
                    "Create a new resource and store the result in state",
                    "Read information from existing infrastructure not managed by this Terraform configuration",
                    "Declare input variables with default data values",
                    "Store arbitrary data objects in Terraform state",
                ],
                answer="b",
                explanation='Data sources read existing infrastructure. `data "aws_vpc" "main" { id = "vpc-0abc123" }` queries AWS for that VPC\'s current attributes without managing it. The result is referenced as `data.aws_vpc.main.cidr_block`. Unlike resources, data sources don\'t create, update, or destroy — they only read.',
                url="https://developer.hashicorp.com/terraform/language/data-sources",
                xp=10,
                hints=["Think read-only. No create, no destroy.", "They reference things that already exist."],
            ),
            Challenge(
                id="tf_data_aws_ami",
                type="quiz",
                prompt="Why would you use `data \"aws_ami\" \"amazon_linux\"` instead of hardcoding an AMI ID?",
                options=[
                    "Hardcoding AMI IDs is against AWS terms of service",
                    "AMI IDs are region-specific and change over time as new versions are released; a data source always finds the latest matching AMI in the current region",
                    "Data source AMI lookups are significantly faster than hardcoded IDs",
                    "AWS requires data source lookups for all EC2 AMI references in IaC tools",
                ],
                answer="b",
                explanation='AMI IDs like `ami-0c55b159cbfafe1f0` are region-specific (the same AMI has a different ID in us-west-2). They also get replaced when AWS releases patches. Using `data "aws_ami"` with `most_recent = true` and `owners = ["amazon"]` with name filters always finds the latest AMI matching your criteria in the current region — eliminating hardcoded IDs and manual updates.',
                url="https://developer.hashicorp.com/terraform/language/data-sources",
                xp=10,
                hints=["AMI IDs are region-specific.", "They change when new versions are released."],
            ),
            Challenge(
                id="tf_data_filter",
                type="fill_blank",
                prompt="In a `data` block, the ___ argument is used to narrow results when multiple objects match the query criteria.",
                answer="filter",
                explanation='Many data sources accept `filter` blocks to narrow lookup results. Example: `data "aws_ami" "al2" { most_recent = true owners = ["amazon"] filter { name = "name" values = ["amzn2-ami-hvm-*-x86_64-gp2"] } }`. Without filters, a data source might return multiple results and error, or return a different resource than intended.',
                url="https://developer.hashicorp.com/terraform/language/data-sources",
                xp=15,
                hints=["It narrows down search results.", "Like a WHERE clause in SQL."],
            ),
            Challenge(
                id="tf_data_reference",
                type="quiz",
                prompt="A configuration declares `data \"aws_subnet\" \"private\" { ... }`. The subnet's CIDR block is accessed in a resource as:",
                options=[
                    "aws_subnet.private.cidr_block",
                    "data.aws_subnet.private.cidr_block",
                    "var.aws_subnet.private.cidr_block",
                    "module.aws_subnet.private.cidr_block",
                ],
                answer="b",
                explanation='Data source references always start with `data.` followed by the data source type and local name: `data.<TYPE>.<NAME>.<ATTRIBUTE>`. This distinguishes them from managed resources (which are referenced as `<TYPE>.<NAME>.<ATTRIBUTE>`). For example: `data.aws_subnet.private.cidr_block` vs `aws_subnet.private.cidr_block` (if you had created it as a resource).',
                url="https://developer.hashicorp.com/terraform/language/data-sources#using-data-sources",
                xp=10,
                hints=["Data sources have their own prefix.", "data.<TYPE>.<NAME>.<ATTR>"],
            ),
            Challenge(
                id="tf_remote_state_data",
                type="quiz",
                prompt="The `terraform_remote_state` data source is used to:",
                options=[
                    "Migrate state from one backend to another",
                    "Read output values from another Terraform configuration's state file",
                    "Replicate state to a disaster recovery region",
                    "Back up state to a remote location before running apply",
                ],
                answer="b",
                explanation='`data "terraform_remote_state" "vpc" { backend = "s3" config = { bucket = "nexus-tfstate" key = "vpc/terraform.tfstate" region = "us-east-1" } }` reads outputs from another config\'s state. Access with `data.terraform_remote_state.vpc.outputs.vpc_id`. This enables cross-stack references — a common pattern for splitting large infrastructures into separate state files that can reference each other.',
                url="https://developer.hashicorp.com/terraform/language/state/remote-state-data",
                xp=10,
                hints=["It reads another configuration's outputs.", "Common for cross-stack references."],
            ),
            Challenge(
                id="tf_data_boss",
                type="quiz",
                prompt="You need to launch an EC2 instance into the VPC and private subnet managed by a separate Terraform configuration (different state file). Which approach correctly threads the cross-stack dependency?",
                options=[
                    "Hardcode the VPC ID and subnet ID — they don't change often enough to matter",
                    "Use `data \"terraform_remote_state\" \"network\"` to read the VPC outputs, then reference `data.terraform_remote_state.network.outputs.private_subnet_ids[0]` in the EC2 resource",
                    "Use `data \"aws_vpc\"` with a name tag filter — this is simpler and avoids state coupling",
                    "Both B and C are valid approaches with different trade-offs",
                ],
                answer="d",
                explanation='Both approaches work with different trade-offs: (B) `terraform_remote_state` gives exact references to values the other config explicitly exported — tightly coupled to that state file. (C) `aws_vpc` data source with tag filters works independently — no state coupling, but relies on consistent tagging. In practice, B is used when you control both configurations and want explicit contracts; C is used when querying infrastructure you don\'t own or when avoiding tight cross-state coupling.',
                url="https://developer.hashicorp.com/terraform/language/state/remote-state-data",
                xp=25,
                hints=["Is there more than one right answer here?", "Think about coupling vs. flexibility."],
                is_boss=True,
            ),
        ],
    ),
    "workspaces_and_environments": Zone(
        id="workspaces_and_environments",
        title="Workspaces and Environments",
        description="terraform workspace, workspace isolation, and per-environment variable files.",
        challenges=[
            Challenge(
                id="tf_workspace_what",
                type="quiz",
                prompt="A Terraform workspace provides:",
                options=[
                    "A separate Terraform Cloud account for each team",
                    "An isolated state file for the same configuration, enabling multiple environments from one codebase",
                    "A containerized execution environment for Terraform commands",
                    "A visual dashboard for monitoring Terraform plan/apply operations",
                ],
                answer="b",
                explanation="Workspaces give each environment its own state file. `terraform workspace new staging` creates a `staging` workspace with a fresh state, separate from `default`. The same configuration can be applied to `dev`, `staging`, and `prod` workspaces without state collision. With S3 backend, each workspace gets its own state file path.",
                url="https://developer.hashicorp.com/terraform/language/state/workspaces",
                xp=10,
                hints=["One config, multiple state files.", "Think dev/staging/prod isolation."],
            ),
            Challenge(
                id="tf_workspace_commands",
                type="fill_blank",
                prompt="To create and switch to a new workspace called `staging`, you run `terraform workspace ___ staging`.",
                answer="new",
                explanation='`terraform workspace new <NAME>` creates a workspace AND switches to it. Other commands: `terraform workspace list` (list all), `terraform workspace select <NAME>` (switch to existing), `terraform workspace show` (current workspace), `terraform workspace delete <NAME>` (delete, cannot delete current). The default workspace is always named `default`.',
                url="https://developer.hashicorp.com/terraform/cli/commands/workspace/new",
                xp=15,
                hints=["It creates AND switches.", "terraform workspace ___ staging"],
            ),
            Challenge(
                id="tf_workspace_variable",
                type="quiz",
                prompt="Inside a Terraform configuration, the current workspace name is available as:",
                options=[
                    "var.workspace",
                    "env.TF_WORKSPACE",
                    "terraform.workspace",
                    "local.workspace_name",
                ],
                answer="c",
                explanation='`terraform.workspace` is a built-in value that returns the current workspace name as a string. Use it to configure environment-specific behavior: `tags = { Environment = terraform.workspace }` or to select different variable files. It\'s the standard way to make a single configuration workspace-aware without hardcoding.',
                url="https://developer.hashicorp.com/terraform/language/state/workspaces#using-workspaces",
                xp=10,
                hints=["It's a built-in Terraform expression.", "terraform.___"],
            ),
            Challenge(
                id="tf_workspace_vs_separate",
                type="quiz",
                prompt="For managing different environments (dev/staging/prod) that have significantly different configurations (different resource counts, different IAM policies), which approach is generally recommended?",
                options=[
                    "Terraform workspaces — they're designed exactly for this use case",
                    "Separate Terraform configuration directories (or a root module per environment) with separate state files — workspaces are better for identical configurations with different state",
                    "A single configuration with all three environments defined and a `count = 0` toggle",
                    "One workspace per region, not per environment",
                ],
                answer="b",
                explanation="HashiCorp recommends workspaces for minor variations of the SAME configuration (e.g., a test copy of an existing deployment). When environments differ significantly — different sizes, different policies, different resource counts — separate directories or separate root modules (one per env) are cleaner. Workspaces can become unwieldy with too much `terraform.workspace == \"prod\" ? X : Y` conditional logic.",
                url="https://developer.hashicorp.com/terraform/language/state/workspaces#when-to-use-multiple-workspaces",
                xp=10,
                hints=["What happens when environments have very different configs?", "HashiCorp has specific guidance on this."],
            ),
            Challenge(
                id="tf_per_env_vars",
                type="quiz",
                prompt="You want environment-specific values (different `instance_type` for dev vs prod). What is the cleanest Terraform pattern?",
                options=[
                    "Hardcode all environment values in a giant if/else expression in main.tf",
                    "Create separate `dev.tfvars` and `prod.tfvars` files, passing the correct one with `-var-file=prod.tfvars` during apply",
                    "Use environment variables with the `TF_VAR_` prefix — they automatically select by environment",
                    "Create a single `terraform.tfvars` file and change it manually before each apply",
                ],
                answer="b",
                explanation='Per-environment `.tfvars` files are the standard pattern: `dev.tfvars` has `instance_type = "t3.micro"`, `prod.tfvars` has `instance_type = "r6g.2xlarge"`. Apply with: `terraform apply -var-file=prod.tfvars`. In CI/CD pipelines, the environment name selects the appropriate var file automatically. This keeps environment differences explicit and reviewable in version control.',
                url="https://developer.hashicorp.com/terraform/language/values/variables#variable-definitions-tfvars-files",
                xp=10,
                hints=["Environment-specific files that you explicitly select.", "dev.tfvars, prod.tfvars — how do you pass them?"],
            ),
            Challenge(
                id="tf_workspaces_boss",
                type="quiz",
                prompt="The NEXUS fraud infrastructure used three workspaces: `dev`, `staging`, and `prod`. The state file for `prod` is at `s3://nexus-tfstate/terraform.tfstate.env:prod`. You need to read the `prod` outputs without switching the current workspace. Which command works?",
                options=[
                    "`terraform output -workspace=prod` — the -workspace flag reads from another workspace",
                    "Switch to prod with `terraform workspace select prod`, run `terraform output`, then switch back",
                    "Use `data \"terraform_remote_state\"` pointing to `key = \"env:/prod/terraform.tfstate\"` (S3 backend workspace key format) to read prod outputs from any workspace",
                    "Workspaces cannot be read cross-workspace — you must use separate state files",
                ],
                answer="c",
                explanation='The S3 backend stores workspace states at `env:/<WORKSPACE>/terraform.tfstate`. Use `data "terraform_remote_state" "prod" { backend = "s3" config = { bucket = "nexus-tfstate" key = "env:/prod/terraform.tfstate" region = "us-east-1" } }` to read prod outputs from any workspace. This is how investigators (and architects) cross-reference state without switching contexts.',
                url="https://developer.hashicorp.com/terraform/language/state/remote-state-data",
                xp=25,
                hints=["S3 backend workspace paths have a specific format.", "env:/<WORKSPACE>/terraform.tfstate"],
                is_boss=True,
            ),
        ],
    ),
    "plan_and_apply_workflow": Zone(
        id="plan_and_apply_workflow",
        title="Plan and Apply Workflow",
        description="init/plan/apply/destroy, -target, -auto-approve, and refresh.",
        challenges=[
            Challenge(
                id="tf_init_purpose",
                type="quiz",
                prompt="`terraform init` is responsible for:",
                options=[
                    "Creating the initial infrastructure defined in your configuration",
                    "Downloading provider plugins, initializing the backend, and installing module sources",
                    "Validating that HCL syntax is correct without calling any APIs",
                    "Generating a plan of changes and writing it to a .tfplan file",
                ],
                answer="b",
                explanation='`terraform init` prepares the working directory: downloads required provider plugins to `.terraform/providers/`, initializes the backend (creates state if needed), and downloads any module sources. Must be re-run when you add new providers, change backend configuration, or add module sources. It\'s idempotent — safe to run multiple times.',
                url="https://developer.hashicorp.com/terraform/cli/commands/init",
                xp=10,
                hints=["It's the first command in every Terraform workflow.", "Provider plugins, backends, modules."],
            ),
            Challenge(
                id="tf_plan_purpose",
                type="quiz",
                prompt="`terraform plan` shows you:",
                options=[
                    "The current cost estimate for your infrastructure",
                    "The diff between your desired state (configuration) and current state (actual infrastructure + state file), as a list of planned creates/updates/destroys",
                    "The Terraform version and provider versions currently in use",
                    "All resources currently tracked in the state file",
                ],
                answer="b",
                explanation='`terraform plan` is a dry run — it calls cloud provider APIs to check current state, compares to configuration, and shows what would change: `+ create`, `~ update`, `- destroy`, `-/+ replace`. No changes are made. Always review the plan before apply. Use `terraform plan -out=tfplan` to save the plan, then `terraform apply tfplan` to execute exactly that plan.',
                url="https://developer.hashicorp.com/terraform/cli/commands/plan",
                xp=10,
                hints=["It's a dry run — nothing changes.", "+, ~, - symbols mean create, update, destroy."],
            ),
            Challenge(
                id="tf_apply_confirm",
                type="fill_blank",
                prompt="By default, `terraform apply` requires you to type ___ to confirm the plan before making changes.",
                answer="yes",
                explanation='Terraform shows the plan and prompts: "Do you want to perform these actions? Enter a value: ". You must type `yes` (not `y` or `YES`). Use `-auto-approve` flag to skip the prompt in CI/CD pipelines: `terraform apply -auto-approve`. Use this flag carefully — always review plans before running auto-approved applies.',
                url="https://developer.hashicorp.com/terraform/cli/commands/apply",
                xp=15,
                hints=["It's not just 'y'.", "The full word confirming the action."],
            ),
            Challenge(
                id="tf_target_flag",
                type="quiz",
                prompt="The `-target=resource.name` flag in `terraform plan`/`apply` is used to:",
                options=[
                    "Set the target environment (dev/staging/prod) for deployment",
                    "Limit the plan or apply to a specific resource and its dependencies, ignoring all others",
                    "Specify the target region for all resources in the apply",
                    "Set the target backend for state storage",
                ],
                answer="b",
                explanation='`-target` scopes the operation to a specific resource: `terraform apply -target=aws_s3_bucket.evidence`. Terraform only plans/applies that resource and anything it depends on. Useful for: debugging specific resources, recovering from partial failures, or testing new resources before a full apply. Warning: using `-target` frequently indicates a configuration architecture problem.',
                url="https://developer.hashicorp.com/terraform/cli/commands/plan#target-address",
                xp=10,
                hints=["It scopes the operation to one resource.", "Good for debugging, bad as a habit."],
            ),
            Challenge(
                id="tf_destroy_command",
                type="quiz",
                prompt="The safest way to destroy all resources in a Terraform configuration is:",
                options=[
                    "`terraform apply -destroy` or `terraform destroy` — both show a plan first and require confirmation",
                    "`terraform delete` — the dedicated deletion command",
                    "`terraform apply` after removing all resource blocks from the configuration",
                    "`terraform state rm` on each resource followed by `terraform apply`",
                ],
                answer="a",
                explanation='`terraform destroy` (or equivalently `terraform apply -destroy`) generates a destroy plan — showing every resource that will be deleted — and requires `yes` confirmation. It respects `prevent_destroy` lifecycle rules. Option C also works but is less explicit. Option D removes resources from state without destroying real infrastructure — a different operation entirely.',
                url="https://developer.hashicorp.com/terraform/cli/commands/destroy",
                xp=10,
                hints=["It shows a plan before destroying.", "`terraform destroy` and `terraform apply -destroy` are equivalent."],
            ),
            Challenge(
                id="tf_workflow_boss",
                type="quiz",
                prompt="A `terraform apply` was interrupted mid-way. Some resources were created, some were not. The state file is partially updated. What is the correct recovery procedure?",
                options=[
                    "Delete the state file and run `terraform apply` from scratch — it will recreate everything",
                    "Run `terraform apply` again — Terraform will reconcile the partial state, create missing resources, and skip already-created ones without duplication",
                    "Run `terraform plan -refresh=false` to avoid calling the cloud APIs, then selectively apply",
                    "Manually edit the state file to mark failed resources as created, then apply",
                ],
                answer="b",
                explanation='Terraform apply is idempotent by design. If interrupted, simply re-run `terraform apply`. Terraform: (1) refreshes state by calling cloud provider APIs for all tracked resources, (2) compares to configuration, (3) only creates/updates what\'s missing or drifted. Resources already created are detected and skipped. Never delete the state file — it would cause Terraform to try recreating already-existing resources, causing conflicts.',
                url="https://developer.hashicorp.com/terraform/cli/commands/apply",
                xp=25,
                hints=["Terraform apply is idempotent.", "It checks what exists before trying to create."],
                is_boss=True,
            ),
        ],
    ),
    "aws_with_terraform": Zone(
        id="aws_with_terraform",
        title="AWS with Terraform",
        description="Writing AWS resources (EC2, S3, VPC, IAM) in Terraform HCL.",
        challenges=[
            Challenge(
                id="tf_aws_provider_config",
                type="quiz",
                prompt="The minimal required configuration to use the AWS provider in Terraform is:",
                options=[
                    "A `provider \"aws\" {}` block with no arguments — Terraform reads credentials from the environment",
                    "A `provider \"aws\"` block with `access_key`, `secret_key`, and `region` hardcoded",
                    "No provider block needed — Terraform auto-detects AWS from resource types",
                    "A `credentials \"aws\" {}` block pointing to ~/.aws/credentials",
                ],
                answer="a",
                explanation='The AWS provider can be configured with just `provider "aws" { region = "us-east-1" }` — or even empty `provider "aws" {}` if AWS_DEFAULT_REGION is set. Credentials are automatically sourced from: environment variables (AWS_ACCESS_KEY_ID), ~/.aws/credentials, IAM instance profile, or ECS task role. NEVER hardcode credentials in provider blocks — they end up in state files and version control.',
                url="https://developer.hashicorp.com/terraform/language/providers/configuration",
                xp=10,
                hints=["Credentials should come from the environment, not the config.", "The provider reads from ~/.aws/credentials automatically."],
            ),
            Challenge(
                id="tf_aws_s3_bucket",
                type="quiz",
                prompt="In current versions of the AWS Terraform provider (v4+), an S3 bucket with versioning and server-side encryption requires:",
                options=[
                    "A single `aws_s3_bucket` resource with `versioning` and `server_side_encryption_configuration` nested blocks",
                    "Separate resources: `aws_s3_bucket`, `aws_s3_bucket_versioning`, and `aws_s3_bucket_server_side_encryption_configuration`",
                    "An `aws_s3_bucket` resource plus a single `aws_s3_bucket_configuration` resource with all settings",
                    "CloudFormation for S3 — Terraform's S3 support is limited to bucket creation only",
                ],
                answer="b",
                explanation='In AWS provider v4+, S3 configuration was broken into separate resources for better modularity: `aws_s3_bucket` (the bucket itself), `aws_s3_bucket_versioning` (versioning config), `aws_s3_bucket_server_side_encryption_configuration` (SSE), `aws_s3_bucket_public_access_block` (public access), etc. The old nested-block approach still works for some settings but is deprecated. Each feature is now its own manageable resource.',
                url="https://developer.hashicorp.com/terraform/language/resources/syntax",
                xp=10,
                hints=["AWS provider v4 changed S3 significantly.", "S3 features became their own resources."],
            ),
            Challenge(
                id="tf_aws_iam_role",
                type="fill_blank",
                prompt="An `aws_iam_role` resource requires an `assume_role_policy` argument containing a JSON policy document. The Terraform function that generates JSON from HCL is `jsonencode(___)` or the `aws_iam_policy_document` ___ source.",
                answer="data",
                explanation='Two patterns exist: (1) `assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [{ Effect = "Allow" Principal = { Service = "lambda.amazonaws.com" } Action = "sts:AssumeRole" }] })` — inline JSON via `jsonencode()`. (2) `data "aws_iam_policy_document" "assume" { statement { principals { type = "Service" identifiers = ["lambda.amazonaws.com"] } actions = ["sts:AssumeRole"] } }` then `assume_role_policy = data.aws_iam_policy_document.assume.json`. The data source approach is more readable and validates IAM syntax.',
                url="https://developer.hashicorp.com/terraform/language/functions/jsonencode",
                xp=15,
                hints=["One approach uses a function, the other uses a data source.", "aws_iam_policy_document is a ___ source."],
            ),
            Challenge(
                id="tf_aws_vpc_structure",
                type="quiz",
                prompt="To deploy a standard AWS VPC with public and private subnets in Terraform, the minimum required resources are:",
                options=[
                    "Just `aws_vpc` and `aws_subnet` — routing is automatic",
                    "`aws_vpc`, `aws_subnet`, `aws_internet_gateway`, `aws_route_table`, and `aws_route_table_association`",
                    "`aws_vpc` and `aws_network_interface` — interfaces handle routing",
                    "`aws_vpc` with a `subnets` block containing all subnet definitions",
                ],
                answer="b",
                explanation='A functional public subnet requires: `aws_vpc` (the network), `aws_subnet` (the subnet), `aws_internet_gateway` (the IGW, attached to the VPC), `aws_route_table` (with a `0.0.0.0/0` route pointing to the IGW), and `aws_route_table_association` (linking the route table to the subnet). Private subnets need `aws_nat_gateway`, `aws_eip`, and their own route table instead.',
                url="https://developer.hashicorp.com/terraform/language/resources/syntax",
                xp=10,
                hints=["Don't forget the internet gateway and route tables.", "Public subnets need explicit routing to 0.0.0.0/0."],
            ),
            Challenge(
                id="tf_aws_ec2",
                type="quiz",
                prompt="A Terraform `aws_instance` resource requires which minimum arguments?",
                options=[
                    "Only `instance_type` — AMI defaults to Amazon Linux",
                    "`ami` and `instance_type` — all other settings have defaults",
                    "`ami`, `instance_type`, `key_name`, and `vpc_security_group_ids`",
                    "`ami`, `instance_type`, `subnet_id`, and `iam_instance_profile`",
                ],
                answer="b",
                explanation='`aws_instance` only strictly requires `ami` and `instance_type`. All other settings (key_name, security groups, subnet, IAM profile) are optional and use defaults. Without key_name you can\'t SSH in; without security groups the default SG is used; without a subnet_id it\'s placed in the default VPC. Minimal example: `resource "aws_instance" "web" { ami = data.aws_ami.al2.id instance_type = "t3.micro" }`.',
                url="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance",
                xp=10,
                hints=["Only two arguments are truly required.", "Think about what's strictly necessary vs. recommended."],
            ),
            Challenge(
                id="tf_aws_boss",
                type="quiz",
                prompt="You're writing Terraform to create an S3 bucket that the NEXUS evidence archive used. The bucket needs: versioning enabled, AES256 server-side encryption, and public access blocked. Which resource set is correct for AWS provider v5?",
                options=[
                    "One `aws_s3_bucket` resource with `versioning { enabled = true }`, `server_side_encryption_configuration {}`, and `block_public_access {}` nested blocks",
                    "Four separate resources: `aws_s3_bucket`, `aws_s3_bucket_versioning`, `aws_s3_bucket_server_side_encryption_configuration`, and `aws_s3_bucket_public_access_block` — each with `bucket = aws_s3_bucket.evidence.id`",
                    "An `aws_s3_bucket_config` resource that accepts all settings in a single `configuration {}` block",
                    "Two resources: `aws_s3_bucket` and `aws_s3_bucket_policy` — encryption and versioning are automatic",
                ],
                answer="b",
                explanation='AWS provider v4+ decomposed S3 configuration into separate resources. The correct pattern: `aws_s3_bucket` (creates the bucket), `aws_s3_bucket_versioning` (sets `versioning_configuration { status = "Enabled" }`), `aws_s3_bucket_server_side_encryption_configuration` (sets `rule { apply_server_side_encryption_by_default { sse_algorithm = "AES256" } }`), `aws_s3_bucket_public_access_block` (sets all four block_public_* flags to true). Each references the bucket via `bucket = aws_s3_bucket.evidence.id`.',
                url="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket",
                xp=25,
                hints=["AWS provider v4+ changed how S3 is configured.", "Each S3 feature is now a separate resource."],
                is_boss=True,
            ),
        ],
    ),
    "advanced_patterns": Zone(
        id="advanced_patterns",
        title="Advanced Patterns",
        description="for_each, count, dynamic blocks, templatefile(), and locals for complex transforms.",
        challenges=[
            Challenge(
                id="tf_count_meta",
                type="quiz",
                prompt="The `count` meta-argument in a resource block creates:",
                options=[
                    "A count of how many times that resource has been applied",
                    "N identical instances of the resource, referenced by index as `resource.name[0]`, `resource.name[1]`, etc.",
                    "A single resource with a count attribute set in the cloud provider",
                    "Multiple Terraform workspaces, one per count value",
                ],
                answer="b",
                explanation='`count = 3` creates three instances of the resource, indexed 0, 1, 2. Reference: `aws_instance.web[0]`. Use `count.index` within the block for the current index: `tags = { Name = "web-${count.index}" }`. Limitation: if you remove item from the middle of a count sequence, Terraform renumbers and replaces downstream resources. For this reason, `for_each` is preferred for non-uniform resources.',
                url="https://developer.hashicorp.com/terraform/language/meta-arguments/count",
                xp=10,
                hints=["Resources become a list, indexed from 0.", "Removing middle items causes renumbering."],
            ),
            Challenge(
                id="tf_for_each_meta",
                type="quiz",
                prompt="The advantage of `for_each` over `count` is:",
                options=[
                    "`for_each` can create more than 10 resources; `count` is limited to 10",
                    "`for_each` uses a stable key (map key or set element) to identify instances, so removing one instance doesn't affect others; `count` uses integer indexes which renumber on deletion",
                    "`for_each` is faster to apply because Terraform parallelizes the creation",
                    "`for_each` works with all resource types; `count` only works with EC2 instances",
                ],
                answer="b",
                explanation='`for_each = { web = "t3.micro", db = "r6g.large" }` creates two instances keyed by `"web"` and `"db"`. Reference: `aws_instance.servers["web"]`. Removing the `"web"` entry only destroys that instance — `"db"` is unaffected. With `count = 2`, removing index 0 would shift index 1 to 0 and trigger a replace. `for_each` is the preferred approach for any resource set where items might be added or removed independently.',
                url="https://developer.hashicorp.com/terraform/language/meta-arguments/for_each",
                xp=10,
                hints=["It uses a stable key, not an integer index.", "Removing one item doesn't affect others."],
            ),
            Challenge(
                id="tf_dynamic_block",
                type="fill_blank",
                prompt="To generate multiple instances of a nested block (like multiple `ingress` rules in a security group) from a list or map variable, you use a ___ block.",
                answer="dynamic",
                explanation='`dynamic` blocks generate nested blocks programmatically: `dynamic "ingress" { for_each = var.ingress_rules content { from_port = ingress.value.from_port to_port = ingress.value.to_port protocol = ingress.value.protocol cidr_blocks = ingress.value.cidr_blocks } }`. The iterator variable defaults to the block label (`ingress`) — change it with `iterator = rule` to use `rule.value.*`. Essential for security groups, lifecycle rules, and any resource with variable-length nested blocks.',
                url="https://developer.hashicorp.com/terraform/language/expressions/dynamic-blocks",
                xp=15,
                hints=["It generates repeated nested blocks from a collection.", "dynamic { for_each = var.rules content { ... } }"],
            ),
            Challenge(
                id="tf_templatefile",
                type="quiz",
                prompt="The `templatefile(path, vars)` function is used to:",
                options=[
                    "Load a Terraform module from a local template directory",
                    "Render a text template file with variable substitutions, typically used for user_data scripts or config files",
                    "Create a new .tf file from a template at runtime",
                    "Generate a Terraform plan file from a configuration template",
                ],
                answer="b",
                explanation='`templatefile("./templates/user_data.sh.tpl", { db_host = aws_db_instance.main.endpoint })` reads the template file and replaces `${db_host}` placeholders with the provided variable values. Returns the rendered string. Commonly used for EC2 `user_data` scripts, Kubernetes YAML, or any config file that needs dynamic values. The template uses the same `${...}` syntax as HCL strings.',
                url="https://developer.hashicorp.com/terraform/language/functions/templatefile",
                xp=10,
                hints=["Think of it like a string template with file input.", "Used for user_data scripts that need dynamic values."],
            ),
            Challenge(
                id="tf_locals_complex",
                type="quiz",
                prompt="The primary purpose of `locals` with complex expressions (e.g., `for` expressions, `merge()`, `flatten()`) is:",
                options=[
                    "To define variables that can be overridden from the command line",
                    "To pre-compute complex transformations once and reference them as `local.name`, reducing repetition and improving readability",
                    "To store Terraform state locally instead of in a remote backend",
                    "To declare reusable functions that can be called from multiple resource blocks",
                ],
                answer="b",
                explanation='Locals are the place to put complex data transformations: `locals { all_tags = merge(var.common_tags, { Environment = var.env }) subnet_ids = flatten([for az in var.azs : aws_subnet.private[az].id]) }`. This computes the expression once and makes it reusable. Without locals, you\'d repeat complex `merge(var.common_tags, {...})` in every resource\'s `tags` argument. Terraform doesn\'t have functions you can define — locals are the equivalent.',
                url="https://developer.hashicorp.com/terraform/language/values/locals",
                xp=10,
                hints=["They're aliases for computed values, not callable functions.", "compute once, use many times."],
            ),
            Challenge(
                id="tf_advanced_boss",
                type="quiz",
                prompt="The NEXUS fraud infrastructure used Terraform to provision IAM roles for 8 phantom subsidiaries, all identical except for the name. The configuration uses `for_each`. Write the correct resource structure mentally — which option correctly describes how `for_each` with a `toset()` of subsidiary names would work?",
                options=[
                    "`for_each = toset(var.subsidiaries)` creates one IAM role per name; each role is referenced as `aws_iam_role.subsidiary[\"nexus-shell-1\"]`; the role name uses `each.key` in its arguments",
                    "`for_each = toset(var.subsidiaries)` creates one IAM role with a list of all names; use `count.index` to access each name",
                    "`for_each` requires a map, not a set; `toset()` would cause a plan error",
                    "`each.key` is only available inside `dynamic` blocks, not in resource arguments",
                ],
                answer="a",
                explanation='`for_each = toset(var.subsidiaries)` creates one resource instance per set element. Inside the block, `each.key` (and `each.value`, which equals `each.key` for sets) is the current element. Example: `name = "nexus-role-${each.key}"`. Addresses in state: `aws_iam_role.subsidiary["nexus-shell-1"]`, `aws_iam_role.subsidiary["nexus-shell-2"]`, etc. `for_each` accepts both maps (where each.key and each.value differ) and sets (where each.key == each.value). `toset()` converts a list to a set, removing duplicates and enabling stable keying.',
                url="https://developer.hashicorp.com/terraform/language/meta-arguments/for_each",
                xp=25,
                hints=["for_each with a set: each.key is the element value.", "Addresses look like resource.name[\"key\"]."],
                is_boss=True,
            ),
        ],
    ),
}
