name: Anno Reject (close issue)

on:
  issues:
    types: [labeled]
  workflow_dispatch:
    inputs:
      issue:
        description: "Issue number to reject/close"
        required: true

env:
  NODE_OPTIONS: --dns-result-order=ipv4first

defaults:
  run:
    shell: bash

concurrency:
  group: anno-reject-${{ github.event.issue.number || github.event.issue.index || inputs.issue || 'manual' }}
  cancel-in-progress: true

jobs:
  reject:
    runs-on: mac-ci
    container:
      image: mcr.microsoft.com/devcontainers/javascript-node:22-bookworm

    steps:
      - name: Tools sanity
        run: |
          set -euo pipefail
          node --version

      - name: Derive context (event.json / workflow_dispatch)
        env:
          INPUT_ISSUE: ${{ inputs.issue }}
          FORGE_API: ${{ vars.FORGE_API || vars.FORGE_BASE || vars.FORGE_BASE_URL }}
        run: |
          set -euo pipefail
          export EVENT_JSON="/var/run/act/workflow/event.json"
          test -f "$EVENT_JSON" || { echo "❌ Missing $EVENT_JSON"; exit 1; }

          node --input-type=module - <<'NODE' > /tmp/reject.env
          import fs from "node:fs";

          const ev = JSON.parse(fs.readFileSync(process.env.EVENT_JSON, "utf8"));
          const repoObj = ev?.repository || {};

          const cloneUrl =
            repoObj?.clone_url ||
            (repoObj?.html_url ? (repoObj.html_url.replace(/\/$/,"") + ".git") : "");

          let owner =
            repoObj?.owner?.login ||
            repoObj?.owner?.username ||
            (repoObj?.full_name ? repoObj.full_name.split("/")[0] : "");

          let repo =
            repoObj?.name ||
            (repoObj?.full_name ? repoObj.full_name.split("/")[1] : "");

          if ((!owner || !repo) && cloneUrl) {
            const m = cloneUrl.match(/[:/](?<o>[^/]+)\/(?<r>[^/]+?)(?:\.git)?$/);
            if (m?.groups) { owner = owner || m.groups.o; repo = repo || m.groups.r; }
          }
          if (!owner || !repo) throw new Error("Cannot infer owner/repo");

          const issueNumber =
            ev?.issue?.number ||
            ev?.issue?.index ||
            (process.env.INPUT_ISSUE ? Number(process.env.INPUT_ISSUE) : 0);

          if (!issueNumber || !Number.isFinite(Number(issueNumber))) {
            throw new Error("No issue number in event.json or workflow_dispatch input");
          }

          // label name: best-effort (non-bloquant)
          let labelName = "workflow_dispatch";
          const lab = ev?.label;
          if (typeof lab === "string") labelName = lab;
          else if (lab && typeof lab === "object" && typeof lab.name === "string") labelName = lab.name;

          let apiBase = "";
          if (process.env.FORGE_API && String(process.env.FORGE_API).trim()) {
            apiBase = String(process.env.FORGE_API).trim().replace(/\/+$/,"");
          } else if (cloneUrl) {
            apiBase = new URL(cloneUrl).origin;
          } else {
            apiBase = "";
          }

          function sh(s){ return JSON.stringify(String(s)); }

          process.stdout.write([
            `OWNER=${sh(owner)}`,
            `REPO=${sh(repo)}`,
            `ISSUE_NUMBER=${sh(issueNumber)}`,
            `LABEL_NAME=${sh(labelName)}`,
            `API_BASE=${sh(apiBase)}`
          ].join("\n") + "\n");
          NODE

          echo "✅ context:"
          sed -n '1,120p' /tmp/reject.env

      - name: Early gate (fast-skip, tolerant)
        run: |
          set -euo pipefail
          source /tmp/reject.env
          echo "ℹ️ event label = $LABEL_NAME"

          if [[ "$LABEL_NAME" != "state/rejected" && "$LABEL_NAME" != "workflow_dispatch" && "$LABEL_NAME" != "" && "$LABEL_NAME" != "[object Object]" ]]; then
            echo "ℹ️ label=$LABEL_NAME => skip early"
            echo "SKIP=1" >> /tmp/reject.env
            echo "SKIP_REASON=\"label_not_rejected_event\"" >> /tmp/reject.env
            exit 0
          fi

      - name: Comment + close (only if label state/rejected is PRESENT now, and no conflict)
        env:
          FORGE_TOKEN: ${{ secrets.FORGE_TOKEN }}
        run: |
          set -euo pipefail
          source /tmp/reject.env
          [[ "${SKIP:-0}" != "1" ]] || { echo "ℹ️ skipped"; exit 0; }

          test -n "${FORGE_TOKEN:-}" || { echo "❌ Missing secret FORGE_TOKEN"; exit 1; }
          test -n "${API_BASE:-}" || { echo "❌ Missing API_BASE"; exit 1; }

          curl -fsS \
            -H "Authorization: token $FORGE_TOKEN" \
            -H "Accept: application/json" \
            "$API_BASE/api/v1/repos/$OWNER/$REPO/issues/$ISSUE_NUMBER" \
            -o /tmp/reject.issue.json

          node --input-type=module - <<'NODE' > /tmp/reject.flags
          import fs from "node:fs";
          const issue = JSON.parse(fs.readFileSync("/tmp/reject.issue.json","utf8"));
          const labels = Array.isArray(issue.labels) ? issue.labels.map(l => String(l.name || "")).filter(Boolean) : [];
          const hasApproved = labels.includes("state/approved");
          const hasRejected = labels.includes("state/rejected");
          process.stdout.write(`HAS_APPROVED=${hasApproved ? "1":"0"}\nHAS_REJECTED=${hasRejected ? "1":"0"}\n`);
          NODE

          source /tmp/reject.flags

          # Do nothing unless state/rejected is truly present now (anti payload weird)
          if [[ "${HAS_REJECTED:-0}" != "1" ]]; then
            echo "ℹ️ state/rejected not present -> skip"
            exit 0
          fi

          if [[ "${HAS_APPROVED:-0}" == "1" && "${HAS_REJECTED:-0}" == "1" ]]; then
            MSG="⚠️ Conflit d'état sur le ticket #${ISSUE_NUMBER} : labels **state/approved** et **state/rejected** présents.\n\n➡️ Action manuelle requise : retirer l'un des deux labels avant relance."
            PAYLOAD="$(node --input-type=module -e 'console.log(JSON.stringify({body: process.argv[1]||""}))' "$MSG")"
            curl -fsS -X POST \
              -H "Authorization: token $FORGE_TOKEN" \
              -H "Content-Type: application/json" \
              "$API_BASE/api/v1/repos/$OWNER/$REPO/issues/$ISSUE_NUMBER/comments" \
              --data-binary "$PAYLOAD"
            echo "ℹ️ conflict => stop"
            exit 0
          fi

          MSG="❌ Ticket #${ISSUE_NUMBER} refusé (label state/rejected)."
          PAYLOAD="$(node --input-type=module -e 'console.log(JSON.stringify({body: process.argv[1]||""}))' "$MSG")"

          curl -fsS -X POST \
            -H "Authorization: token $FORGE_TOKEN" \
            -H "Content-Type: application/json" \
            "$API_BASE/api/v1/repos/$OWNER/$REPO/issues/$ISSUE_NUMBER/comments" \
            --data-binary "$PAYLOAD"

          curl -fsS -X PATCH \
            -H "Authorization: token $FORGE_TOKEN" \
            -H "Content-Type: application/json" \
            "$API_BASE/api/v1/repos/$OWNER/$REPO/issues/$ISSUE_NUMBER" \
            --data-binary '{"state":"closed"}'

          echo "✅ rejected+closed"