variable "account_id" {
  description = <<-EOT
    The AWS account this stack belongs in. Checked at plan time against the credentials in use.

    Required and has no default on purpose: a default would be the thing that lets a wrong-profile
    apply succeed, which is the exact failure this variable exists to prevent.
  EOT
  type        = string

  validation {
    condition     = can(regex("^[0-9]{12}$", var.account_id))
    error_message = "account_id must be a 12-digit AWS account number."
  }
}

variable "region" {
  description = "Region for every resource in this stack. Fixed by the architecture decision."
  type        = string
  default     = "us-west-2"
}

variable "oidc_subject" {
  description = <<-EOT
    The FULL subject claim GitHub puts in the OIDC token for this repository's main branch.

    Not a repo slug, and that is the correction. This was `repo_slug` and the trust policy built
    `repo:<slug>:ref:refs/heads/main` from it, which is the documented default and was wrong here.
    The tidbcloud organisation configures Actions to emit IMMUTABLE subjects, which carry numeric ids
    rather than names, so the real claim looks like

      repo:tidbcloud@69240164/mcp-server@1336587763:ref:refs/heads/main

    That is a better setting than the default: renaming the repository, or moving it between
    organisations, does not silently re-point who may assume the deploy role. It also means the value
    cannot be derived from anything we know, so it is passed in whole.

    Read it from the workflow itself rather than guessing. The token endpoint is available to any job
    with `id-token: write`, and decoding only the `sub` claim from it costs one step. Guessing this
    produced a deploy role that refused every request with `Not authorized to perform
    sts:AssumeRoleWithWebIdentity`, which names nothing useful.
  EOT
  type        = string

  validation {
    condition     = can(regex("^repo:.+:ref:refs/heads/.+$", var.oidc_subject))
    error_message = "oidc_subject must be a full subject claim, e.g. repo:org@123/name@456:ref:refs/heads/main"
  }

  # The subject carries the repository name alongside its numeric id, so the two variables can be
  # required to AGREE. Without this, nothing notices when one is updated and the other is not, and
  # `repo_slug` decays into decoration. With it, `repo_slug` is the readable half of a pair: anyone
  # asking "which repository does this account trust?" reads the slug, and this check is what makes
  # reading the slug safe.
  validation {
    condition = (
      strcontains(var.oidc_subject, "repo:${split("/", var.repo_slug)[0]}@") &&
      strcontains(var.oidc_subject, "/${split("/", var.repo_slug)[1]}@")
    )
    error_message = "oidc_subject and repo_slug disagree about the repository. Update them together."
  }
}

variable "repo_slug" {
  description = <<-EOT
    owner/name of the repository CI deploys from. Used only for tagging now that `oidc_subject`
    carries the trust relationship, and kept because a resource tag that says which repository owns
    it is worth more than one character of saved config.
  EOT
  type        = string
}

variable "interim_domain" {
  description = <<-EOT
    Optional hostname to serve from before mcp.tidbcloud.com exists.

    The public hostname attaches only after the security review, and an ACM certificate for it is
    requested early because DNS validation has lead time. Until a valid certificate exists, smoke
    tests run over HTTP with dummy credentials only; real-credential traffic waits.

    Empty means no interim hostname and no certificate is created.
  EOT
  type        = string
  default     = ""
}

variable "poc_interim_domain" {
  description = <<-EOT
    Optional hostname for the PARTNER POC endpoint, tracked separately from `interim_domain`.

    Two variables rather than one because the two endpoints do not have to arrive together and, when
    they were governed by a single flag, only the official one was actually wired: the POC had no
    HTTPS listener and no certificate at all. Setting the official domain alone would have given
    that endpoint TLS and left the POC serving a static bearer token over plaintext.

    Empty means the POC serves nothing but `/health` over port 80, which is the safe half of that
    asymmetry rather than the dangerous one.

    The Fly cutover is blocked on THIS value, not on `interim_domain`. Until the POC has a
    certificate, moving the partner here would put their bearer token on the wire in clear text.
  EOT
  type        = string
  default     = ""
}

variable "operator_cidrs" {
  description = <<-EOT
    Source addresses allowed to reach the STAGING listener rule on the official ALB.

    Staging shares the official ALB rather than paying for a third one, so this list is what keeps
    it from being a second public endpoint. Empty means the staging rule is not created at all,
    which is the safe default: an empty allowlist that fell through to "allow all" is the classic
    way this shape leaks.
  EOT
  type        = list(string)
  default     = []
}

variable "alert_email" {
  description = "Where CloudWatch alarms and budget notifications go. Empty disables the SNS topic."
  type        = string
  default     = ""
}

variable "monthly_budget_usd" {
  description = <<-EOT
    Budget ceiling with alerts at 50/80/100 percent.

    The estimate is 85-95/month and the approved ceiling is 150. The gap is deliberate: alerting at
    percentages of the ESTIMATE would page on normal variance, and alerting only at the ceiling
    would page when it is already too late to act cheaply.
  EOT
  type        = number
  default     = 150
}

variable "official_task_count" {
  description = "Tasks for the public read-only service. Two, spread across AZs."
  type        = number
  default     = 2
}

variable "log_retention_days" {
  description = "CloudWatch retention. Detail beyond this is not kept; telemetry aggregates are."
  type        = number
  default     = 30
}
