name: Release Publish

# Builds the Nquiry application image and pushes it to ECR Public (always)
# and AWS Marketplace ECR (if MARKETPLACE_ECR_URI is set). Triggered on
# semver tag push or manually via workflow_dispatch.
#
# The pushed image tag is read from infrastructure/terraform/modules/nquiry-stack/VERSION
# at the tagged ref. This must match the tag name — the workflow fails fast
# if they differ. This is the NQU-645 Decision 2 lock enforcement at the
# publish boundary (module pin and image tag cannot drift).

on:
  push:
    tags:
      - "v*.*.*"
  workflow_dispatch:
    inputs:
      tag:
        description: "Version tag to publish (must match infrastructure/terraform/modules/nquiry-stack/VERSION at HEAD; e.g. v0.1.0)"
        required: true
        type: string

permissions:
  id-token: write # OIDC
  contents: read

jobs:
  publish:
    runs-on: ubuntu-latest
    concurrency:
      group: release-publish-${{ github.event_name == 'push' && github.ref_name || github.event.inputs.tag }}
      cancel-in-progress: false

    env:
      AWS_REGION: us-east-1
      VERSION_FILE: infrastructure/terraform/modules/nquiry-stack/VERSION

    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          ref: ${{ github.event.inputs.tag || github.ref }}

      - name: Resolve target version
        id: version
        run: |
          if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
            TAG="${{ github.event.inputs.tag }}"
          else
            TAG="${GITHUB_REF#refs/tags/}"
          fi
          VERSION_FILE_CONTENT=$(tr -d '[:space:]' < "$VERSION_FILE")
          echo "Tag:              $TAG"
          echo "VERSION file:     $VERSION_FILE_CONTENT"
          if [[ "$TAG" != "$VERSION_FILE_CONTENT" ]]; then
            echo "::error::Tag ($TAG) does not match $VERSION_FILE ($VERSION_FILE_CONTENT). NQU-645 Decision 2 lock violated."
            exit 1
          fi
          echo "tag=$TAG" >> "$GITHUB_OUTPUT"

      - name: Configure AWS credentials (OIDC)
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          aws-region: ${{ env.AWS_REGION }}

      - name: Login to Amazon ECR Public
        id: ecr-public-login
        uses: aws-actions/amazon-ecr-login@v2
        with:
          registry-type: public

      - name: Build customer image
        env:
          ECR_PUBLIC_REGISTRY: ${{ steps.ecr-public-login.outputs.registry }}
          ECR_PUBLIC_REPO: ${{ secrets.ECR_PUBLIC_REPO }}
          TAG: ${{ steps.version.outputs.tag }}
        run: |
          # NEXT_PUBLIC_* values are customer-runtime-overridable in v0.2.0+.
          # For v0.1.0 the image bakes placeholders; customer config is set
          # via ECS task definition env vars and a runtime substitution layer
          # planned for the v0.2.0 cut. See docs/licensee/release-notes/v0.1.0.md
          # known issues.
          docker build \
            --build-arg NEXT_PUBLIC_COGNITO_USER_POOL_ID="customer-override-at-deploy" \
            --build-arg NEXT_PUBLIC_COGNITO_CLIENT_ID="customer-override-at-deploy" \
            --build-arg NEXT_PUBLIC_APP_URL="https://customer-override-at-deploy" \
            --build-arg AI_PROVIDER="bedrock" \
            --build-arg APP_AWS_REGION="us-east-1" \
            -t "nquiry-app:$TAG" \
            .

      - name: Push to ECR Public
        env:
          ECR_PUBLIC_REGISTRY: ${{ steps.ecr-public-login.outputs.registry }}
          ECR_PUBLIC_REPO: ${{ secrets.ECR_PUBLIC_REPO }}
          TAG: ${{ steps.version.outputs.tag }}
        run: |
          # NQU-807: secret holds just the repo name (e.g., invapp-dev-app).
          # The registry alias is discovered at runtime via aws ecr-public
          # describe-registries so customers/operators set the secret to the
          # bare repo name from terraform output. If the secret accidentally
          # includes an alias prefix (legacy <alias>/<repo> format) we accept
          # it and strip — backwards compatible with existing setups.
          if [[ -z "$ECR_PUBLIC_REPO" ]]; then
            echo "::error::ECR_PUBLIC_REPO secret is not set. Run terraform apply in environments/dev and copy the repository_name portion of module.cicd.ecr_public_repository_uri into the secret (the part after public.ecr.aws/<alias>/)."
            exit 1
          fi
          ALIAS=$(aws ecr-public describe-registries --region us-east-1 --query 'registries[0].aliases[0].name' --output text)
          if [[ -z "$ALIAS" || "$ALIAS" == "None" ]]; then
            echo "::error::No ECR Public registry alias found for this account. Provision one in the ECR Public console under Settings > Registry alias."
            exit 1
          fi
          # Strip any accidental <alias>/ prefix from the secret to be backwards compatible.
          REPO_NAME="${ECR_PUBLIC_REPO##*/}"
          FULL_URI="$ECR_PUBLIC_REGISTRY/$ALIAS/$REPO_NAME:$TAG"
          docker tag "nquiry-app:$TAG" "$FULL_URI"
          docker push "$FULL_URI"
          echo "Pushed to ECR Public: $FULL_URI"
          echo "PUBLISHED_URI=$FULL_URI" >> "$GITHUB_ENV"
          # Capture image digest (NQU-834). Content digest is registry-agnostic,
          # so this same value applies to the Marketplace ECR push if it runs.
          # Best-effort: if describe-images fails (eventual consistency, missing
          # ecr-public:DescribeImages IAM perm), log a warning and continue.
          # The Marketplace listing step downstream gates on IMAGE_DIGEST being
          # set, so a missing digest just skips that step — it doesn't regress
          # the ECR Public release path. Once seller registration lands and the
          # listing step gets enabled, the IAM policy follow-up will grant the
          # describe perm and this becomes reliable.
          DIGEST=$(aws ecr-public describe-images --region us-east-1 \
            --repository-name "$REPO_NAME" \
            --image-ids imageTag="$TAG" \
            --query 'imageDetails[0].imageDigest' --output text 2>/dev/null || true)
          if [[ -z "$DIGEST" || "$DIGEST" == "None" ]]; then
            echo "::warning::Image digest not captured from ECR Public describe-images (eventual consistency or missing ecr-public:DescribeImages permission). Marketplace listing publish step will skip if enabled. ECR Public release is unaffected."
          else
            echo "IMAGE_DIGEST=$DIGEST" >> "$GITHUB_ENV"
            echo "Captured image digest: $DIGEST"
          fi

      - name: Push to Marketplace ECR (if configured)
        if: ${{ vars.MARKETPLACE_ECR_URI != '' }}
        env:
          MARKETPLACE_ECR_URI: ${{ vars.MARKETPLACE_ECR_URI }}
          TAG: ${{ steps.version.outputs.tag }}
        run: |
          # Marketplace ECR lives in the AWS Marketplace seller account (709825985650).
          # Auth uses the same ECR API but a separate `aws ecr get-login-password` flow.
          aws ecr get-login-password --region us-east-1 \
            | docker login --username AWS --password-stdin "$(echo "$MARKETPLACE_ECR_URI" | cut -d/ -f1)"
          docker tag "nquiry-app:$TAG" "$MARKETPLACE_ECR_URI:$TAG"
          docker push "$MARKETPLACE_ECR_URI:$TAG"
          echo "Pushed to Marketplace ECR: $MARKETPLACE_ECR_URI:$TAG"

      - name: Publish to Marketplace listing (if configured)
        # NQU-834: only fires when ALL three vars are set:
        #   - vars.MARKETPLACE_PRODUCT_ID  (the Marketplace ContainerProduct entity ID)
        #   - vars.MARKETPLACE_ECR_URI     (image was pushed to seller-account ECR above)
        #   - env.IMAGE_DIGEST             (captured by the ECR Public push step)
        # Until seller registration lands and these vars are populated, this step
        # cleanly skips. Once configured, it calls aws marketplace-catalog
        # start-change-set with ReleaseProductVersion so the new image tag
        # appears as a published version in the seller console.
        #
        # IAM: the OIDC role must include marketplace-catalog:StartChangeSet and
        # marketplace-catalog:DescribeChangeSet. Separate terraform follow-up
        # tracked as a sub-task of NQU-834.
        #
        # API SHAPE: ContainerProduct@1.0 ReleaseProductVersion details below
        # were drafted against AWS docs (links in PR). VERIFY against a live
        # call when first exercising — the exact key names for EcrDelivery
        # ContainerImages have been known to shift between API versions.
        # See: https://docs.aws.amazon.com/marketplace-catalog/latest/api-reference/welcome.html
        #
        # DIGEST DEPENDENCY: this step needs IMAGE_DIGEST to pin the published
        # version immutably. If digest capture failed upstream (warning, not
        # error), this step still exits 1 — better to fail loud than to publish
        # a tag-pinned reference that could shift later.
        if: ${{ vars.MARKETPLACE_PRODUCT_ID != '' && vars.MARKETPLACE_ECR_URI != '' }}
        env:
          MARKETPLACE_PRODUCT_ID: ${{ vars.MARKETPLACE_PRODUCT_ID }}
          MARKETPLACE_ECR_URI: ${{ vars.MARKETPLACE_ECR_URI }}
          TAG: ${{ steps.version.outputs.tag }}
        run: |
          if [[ -z "$IMAGE_DIGEST" ]]; then
            echo "::error::IMAGE_DIGEST is empty — the ECR Public describe-images call upstream failed (see warning above). Marketplace listing publish requires an immutable digest reference; cannot proceed. Fix the IAM policy (ecr-public:DescribeImages) or wait for ECR Public eventual consistency, then re-dispatch."
            exit 1
          fi
          # Pin by digest so the published version is immutable even if the
          # tag is later overwritten upstream. The tag is still carried as
          # VersionTitle for human-readability in the seller console.
          IMAGE_REF_BY_DIGEST="${MARKETPLACE_ECR_URI}@${IMAGE_DIGEST}"
          CHANGE_SET=$(cat <<EOF
          [
            {
              "ChangeType": "ReleaseProductVersion",
              "Entity": {
                "Type": "ContainerProduct@1.0",
                "Identifier": "${MARKETPLACE_PRODUCT_ID}"
              },
              "DetailsDocument": {
                "Version": {
                  "DeliveryOptions": [
                    {
                      "Details": {
                        "EcrDeliveryOptionDetails": {
                          "ContainerImages": ["${IMAGE_REF_BY_DIGEST}"]
                        }
                      }
                    }
                  ],
                  "VersionTitle": "${TAG}",
                  "ReleaseNotes": "Nquiry release ${TAG}. See https://github.com/AlligatorC0der/investigation-app/releases/tag/${TAG} for full notes."
                }
              }
            }
          ]
          EOF
          )
          CHANGE_SET_RESULT=$(aws marketplace-catalog start-change-set \
            --catalog AWSMarketplace \
            --change-set "$CHANGE_SET" \
            --change-set-name "release-${TAG}-$(date +%Y%m%d%H%M%S)" \
            --output json)
          CHANGE_SET_ID=$(echo "$CHANGE_SET_RESULT" | jq -r '.ChangeSetId')
          echo "Started Marketplace change-set: $CHANGE_SET_ID"
          echo "MARKETPLACE_CHANGE_SET_ID=$CHANGE_SET_ID" >> "$GITHUB_ENV"
          echo "Monitor at: https://aws.amazon.com/marketplace/management/requests/${CHANGE_SET_ID}"

      - name: Summary
        env:
          TAG: ${{ steps.version.outputs.tag }}
          MARKETPLACE_ECR_URI: ${{ vars.MARKETPLACE_ECR_URI }}
          MARKETPLACE_PRODUCT_ID: ${{ vars.MARKETPLACE_PRODUCT_ID }}
        run: |
          {
            echo "## Release $TAG published"
            echo ""
            echo "| Target | Reference |"
            echo "| --- | --- |"
            echo "| ECR Public | \`${PUBLISHED_URI:-unknown}\` |"
            echo "| Image digest | \`${IMAGE_DIGEST:-unknown}\` |"
            if [[ -n "$MARKETPLACE_ECR_URI" ]]; then
              echo "| Marketplace ECR | \`$MARKETPLACE_ECR_URI:$TAG\` |"
            else
              echo "| Marketplace ECR | _skipped — \`vars.MARKETPLACE_ECR_URI\` not set_ |"
            fi
            if [[ -n "$MARKETPLACE_PRODUCT_ID" && -n "$MARKETPLACE_ECR_URI" ]]; then
              echo "| Marketplace listing | change-set \`${MARKETPLACE_CHANGE_SET_ID:-unknown}\` |"
            else
              echo "| Marketplace listing | _skipped — \`vars.MARKETPLACE_PRODUCT_ID\` and/or \`vars.MARKETPLACE_ECR_URI\` not set_ |"
            fi
          } >> "$GITHUB_STEP_SUMMARY"
