#!/usr/bin/env bash
set -euo pipefail

USAGE="$(cat <<EOF
Waits for a tag to appear on a GitHub repository, then dispatches a workflow.
Exits 0 regardless of outcome (best-effort) unless --wait is used,
in which case exits non-zero on failure or timeout.

Usage: github/dispatch [-h,--help] [-a,--attempts N] [-i,--interval S] [-w,--wait] <owner> <repo> <workflow> <tag>

Arguments:
  owner           GitHub org or user (e.g. kernelle-soft).
  repo            GitHub repository name (e.g. kaitos-toolchain).
  workflow        Workflow filename to dispatch (e.g. release.yaml).
  tag             Tag name to wait for and dispatch on.

Flags:
  -a, --attempts N   Max poll attempts for tag (default: 30).
  -i, --interval S   Seconds between tag poll attempts (default: 10).
  -w, --wait         Wait for the dispatched workflow run to complete.
  -h, --help         Show this help text.

Environment:
  GITHUB_TOKEN    Required. API token with actions:write scope.

EOF
)"

import "$SHELLOPS_DIR/lib/github.api.sh"

RUN_POLL_ATTEMPTS=10
RUN_POLL_INTERVAL=5

ARG_OWNER=""
ARG_REPO=""
ARG_WORKFLOW=""
ARG_TAG=""
FLAG_ATTEMPTS=30
FLAG_INTERVAL=10
FLAG_WAIT=false

main() {
  parse_args "$@"
  require_token
  poll_and_dispatch
}

parse_args() {
  local positional=()

  while [[ $# -gt 0 ]]; do
    case "$1" in
      -a|--attempts) FLAG_ATTEMPTS="${2:?--attempts requires a value}"; shift ;;
      -i|--interval) FLAG_INTERVAL="${2:?--interval requires a value}"; shift ;;
      -w|--wait)     FLAG_WAIT=true ;;
      -h|--help)     log "$USAGE"; exit 0 ;;
      -*)            error "Unknown option: $1"; log "$USAGE"; exit 1 ;;
      *)             positional+=("$1") ;;
    esac
    shift
  done

  ARG_OWNER="${positional[0]:-}"
  ARG_REPO="${positional[1]:-}"
  ARG_WORKFLOW="${positional[2]:-}"
  ARG_TAG="${positional[3]:-}"

  if [[
    -z "$ARG_OWNER"     ||
    -z "$ARG_REPO"      ||
    -z "$ARG_WORKFLOW"  ||
    -z "$ARG_TAG"
  ]]; then
    error "<owner>, <repo>, <workflow>, and <tag> are all required."
    log "$USAGE"
    exit 1
  fi
}

require_token() {
  if [[ -z "${GITHUB_TOKEN:-}" ]]; then
    error "GITHUB_TOKEN is not set."
    exit 1
  fi
}

poll_and_dispatch() {
  local tag_opts dispatch_opts

  tag_opts="$(jq_set "$(new_github_repo_options)" \
    '.owner' "$ARG_OWNER" \
    '.repo'  "$ARG_REPO" \
    '.tag'   "$ARG_TAG" \
    '.token' "$GITHUB_TOKEN"
  )"

  dispatch_opts="$(jq_set "$(new_github_dispatch_options)" \
    '.owner'    "$ARG_OWNER" \
    '.repo'     "$ARG_REPO" \
    '.workflow' "$ARG_WORKFLOW" \
    '.ref'      "$ARG_TAG" \
    '.token'    "$GITHUB_TOKEN"
  )"

  if ! wait_for_tag "$tag_opts"; then
    log "Tag ${ARG_TAG} did not appear on GitHub within timeout. Skipping."
    exit 0
  fi

  github_dispatch_workflow "$dispatch_opts"
  log "Dispatched ${ARG_WORKFLOW} for ${ARG_TAG}"

  track_run "$dispatch_opts"
}

wait_for_tag() {
  local tag_opts="$1"

  for i in $(seq 1 "$FLAG_ATTEMPTS"); do
    if github_tag_exists "$tag_opts"; then
      return 0
    fi
    log "Waiting for ${ARG_TAG} on GitHub (${i}/${FLAG_ATTEMPTS})..."
    sleep "$FLAG_INTERVAL"
  done

  return 1
}

track_run() {
  local dispatch_opts="$1"

  local run_json
  run_json="$(github_wait_for_run_start "$dispatch_opts" "$RUN_POLL_ATTEMPTS" "$RUN_POLL_INTERVAL")" || {
    log "Could not locate workflow run. Check GitHub Actions manually."
    return 0
  }

  local run_url run_id
  run_url="$(jq_get "$run_json" '.html_url')"
  run_id="$(jq_get "$run_json" '.id')"
  log "Run: ${run_url}"

  if [[ "$FLAG_WAIT" = false ]]; then
    return 0
  fi

  local run_opts
  run_opts="$(jq_set "$(new_github_run_options)" \
    '.owner'  "$ARG_OWNER" \
    '.repo'   "$ARG_REPO" \
    '.run_id' "$run_id" \
    '.token'  "$GITHUB_TOKEN"
  )"

  log "Waiting for run to complete..."
  local final_json conclusion
  final_json="$(github_wait_for_run_result "$run_opts")" || {
    log "Timed out waiting for run to complete. Cancelling..."
    if github_cancel_run "$run_opts"; then
      log "Run cancelled."
    else
      error "Failed to cancel run."
    fi
    exit 1
  }

  conclusion="$(jq_get "$final_json" '.conclusion')"
  log "Run completed: ${conclusion}"

  [[ "$conclusion" = "success" ]]
}

main "$@"
