#!/usr/bin/env bash
set -euo pipefail
source "$SHELLOPS_DIR/.envrc"

:<<'DOC'
Scans merged, squashed, and direct pushes for Linear issue references and
transitions each to Done.

Gathers text from PR titles, descriptions, comments, and commit messages,
then regex-scans for <team-key>-<number> patterns.

Designed to run after a successful release as part of publish.yaml.
Errors are logged but never fail the pipeline.
DOC

import \
  "$SHELLOPS_DIR/lib/forgejo.api.sh" \
  "$SHELLOPS_DIR/lib/manifest.api.sh" \
  "$SHELLOPS_DIR/lib/linear.api.sh"

__close_mentioned__NULL_SHA="0000000000000000000000000000000000000000"
__close_mentioned__FALLBACK_DEPTH=10

function main() {
  local forge_token linear_token team_key
  local pr_numbers linear_options forge_options teams_json issue_ids

  forge_token="${FORGE_ADMIN_TOKEN:-}"
  linear_token="${LINEAR_API_KEY:-}"

  if [[ -z "$forge_token" ]]; then
    log "FORGE_ADMIN_TOKEN not set, skipping."; return 0
  fi

  if [[ -z "$linear_token" ]]; then
    log "LINEAR_API_KEY not set, skipping."; return 0
  fi

  team_key="$(manifest_get linear_team)" || {
    log "No linear-team in manifest.json, skipping."; return 0
  }

  pr_numbers="$(extract_pr_numbers || true)"
  linear_options="$(jq_set '{}' '.token' "$linear_token")"
  forge_options="$(jq_set '{}' '.token' "$forge_token")"

  teams_json="$(linear_list_teams "$linear_options")" || {
    log "Failed to fetch Linear teams, skipping."; return 0
  }

  issue_ids="$(collect_issue_ids "$pr_numbers" "$team_key" "$forge_options")"
  if [[ "$(jq_len "$issue_ids")" -eq 0 ]]; then
    if [[ -z "$pr_numbers" ]]; then
      log "No PR numbers found in recent commits."
    fi
    log "No Linear issue references found."; return 0
  fi

  log "Found Linear references: $(jq_join "$issue_ids" ", ")"
  close_mentioned_issues "$issue_ids" "$team_key" "$teams_json" "$linear_options"
}

function resolve_commit_range() {
  local before after
  before="${GITHUB_EVENT_BEFORE:-}"
  after="${GITHUB_SHA:-HEAD}"

  if [[ -z "$before" || "$before" == "$__close_mentioned__NULL_SHA" ]]; then
    before="$(git rev-parse "HEAD~${__close_mentioned__FALLBACK_DEPTH}" 2>/dev/null || echo "")"
  fi

  [[ -z "$before" ]] && return 1
  echo "${before}..${after}"
}

function extract_pr_numbers() {
  local range
  range="$(resolve_commit_range)" || return

  git log --format='%s' "$range" 2>/dev/null \
    | grep -oP '\(#\K[0-9]+(?=\))' \
    | sort -u \
    || true
}

function collect_issue_ids() {
  local pr_numbers="$1" team_key="$2" forge_options="$3"
  local pattern all_text matches

  pattern="${team_key}-[0-9]+"
  all_text="$(extract_commit_text)"

  while IFS= read -r pr_num; do
    [[ -z "$pr_num" ]] && continue
    all_text+=$'\n'"$(gather_pr_text "$pr_num" "$forge_options")"
  done <<< "$pr_numbers"

  matches="$(echo "$all_text" | grep -ioP "$pattern" | tr '[:lower:]' '[:upper:]' | sort -u || true)"
  jq_split "$matches"
}

function extract_commit_text() {
  local range
  range="$(resolve_commit_range)" || return
  git log --format='%s%n%b' "$range" 2>/dev/null || true
}

function gather_pr_text() {
  local pr_num="$1" forge_options="$2"
  local pr_opts pr_json comment_opts comments

  pr_opts="$(jq_set "$forge_options" '.pr_number' "$pr_num")"
  pr_json="$(get_pull_request "$pr_opts" 2>/dev/null)" || true
  if jq_defined "$pr_json"; then
    jq_get "$pr_json" '.title'
    jq_get "$pr_json" '.body'
  fi

  comment_opts="$(jq_set "$forge_options" '.issue_number' "$pr_num")"
  comments="$(forgejo_list_comments "$comment_opts" 2>/dev/null)" || true
  if jq_defined "$comments"; then
    local bodies
    bodies="$(jq_filter "$(jq_pluck "$comments" '.body')" '. != null')"
    jq_join "$bodies" $'\n'
  fi
}

function close_mentioned_issues() {
  local issue_ids="$1" team_key="$2" teams_json="$3" linear_options="$4"
  local done_state_id

  done_state_id="$(linear_find_done_state "$teams_json" "$team_key")"
  if [[ -z "$done_state_id" ]]; then
    log "Could not find Done state for team ${team_key}, skipping."
    return 0
  fi

  jq_foreach try_close_issue "$issue_ids" "$done_state_id" "$linear_options"
}

function try_close_issue() {
  local issue_ref_json="$1" done_state_id="$2" linear_options="$3"
  local issue_ref issue_json current_state issue_id

  issue_ref="$(jq_unwrap "$issue_ref_json")"

  issue_json="$(linear_get_issue "$issue_ref" "$linear_options" 2>/dev/null)" || {
    log "  Could not fetch ${issue_ref}, skipping."
    return 0
  }

  current_state="$(jq_get "$issue_json" '.state.type')"
  if [[ "$current_state" == "completed" || "$current_state" == "cancelled" ]]; then
    log "  ${issue_ref} is already ${current_state}, skipping."
    return 0
  fi

  issue_id="$(jq_get "$issue_json" '.id')"
  if linear_update_issue_state "$issue_id" "$done_state_id" "$linear_options" 2>/dev/null; then
    log "  Closed ${issue_ref}"
  else
    log "  Failed to close ${issue_ref}"
  fi
}

if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
  main "$@"
fi
