#!/bin/bash
# HQ API helper for minion chat context
#
# Fetches resource details from the HQ server API.
# Used by Claude CLI during chat to retrieve information about
# skills, workflows, and projects that the user is viewing on the dashboard.
#
# Environment variables (inherited from minion server):
#   HQ_URL    - HQ server URL (e.g., https://minion-agent.com)
#   API_TOKEN - Minion API token for authentication
#
# Usage:
#   hq fetch skill <name>                 - Get skill details (content, description, files)
#   hq fetch workflow <name>              - Get workflow details (pipeline, cron, etc.)
#   hq fetch project <id>                 - Get project info (name, description, role)
#   hq fetch project-context <id>         - Get project context (shared Markdown document)
#   hq fetch dag-workflow <id>            - Get DAG workflow details (graph, version)
#   hq fetch dag-execution <id>           - Get DAG execution details (nodes, status)
#   hq list workspaces                    - List workspaces this minion belongs to
#   hq list dag-workflows [project_id]    - List DAG workflows (optionally filter by project)
#   hq note create <project_id> --title "Title" --content "Body" [--file path]
#   hq note create --workspace <workspace_id> --title "Title" --content "Body" [--file path] [--project-ids <id,id>]
#                                         - Create a note (project- or workspace-scoped)
#   hq note update <project_id> <note_id> --content "Body" [--title "Title"] [--file path] [--change-summary "summary"]
#   hq note update --workspace <workspace_id> <note_id> --content "Body" [--title "Title"] [--status active|archived]
#                                         - Update an existing note
#   hq note list <project_id>             - List notes in the project
#   hq note list --workspace <workspace_id>  - List notes in the workspace (incl. project-unlinked)
#   hq note get <project_id> <note_id>    - Get a single note with full content
#   hq note get --workspace <workspace_id> <note_id>  - Same, workspace-scoped
#   hq note search <project_id> <query>   - Search notes in the project
#   hq note search --workspace <workspace_id> <query> - Search across the whole workspace
#   hq create dag-workflow <body.json>    - Create a DAG workflow (PM only). Body: {project_id, name, graph?, ...}
#   hq put dag-workflow <id> <body.json>  - Update DAG workflow draft (PM only). Body: {graph?, content?, ...}
#   hq publish dag-workflow <id>          - Publish the draft as a new version (PM only, validated)
#   hq dag add-node <wf-id> <body.json>  - Add a node to DAG draft. Body: {type, label?, after?, before?, ...}
#   hq dag update-node <wf-id> <node-id> <body.json> - Update a node. Body: {label?, skill_version_id?, ...}
#   hq dag remove-node <wf-id> <node-id> - Remove a node (auto-reconnects if possible)
#   hq dag add-edge <wf-id> <body.json>  - Add an edge. Body: {source, target, kind?, condition_label?}
#   hq dag update-edge <wf-id> <edge-id> <body.json> - Update edge kind/condition_label
#   hq dag remove-edge <wf-id> <edge-id> - Remove an edge
#   hq dag validate <wf-id>              - Validate draft without publishing

set -euo pipefail

# Validate required environment variables
if [ -z "${HQ_URL:-}" ]; then
  echo "Error: HQ_URL is not set" >&2
  exit 1
fi

if [ -z "${API_TOKEN:-}" ]; then
  echo "Error: API_TOKEN is not set" >&2
  exit 1
fi

BASE_URL="${HQ_URL}/api/minion"

# Pretty-print JSON if jq is available, otherwise output raw
format_json() {
  if command -v jq &>/dev/null; then
    jq .
  else
    cat
  fi
}

# Validate that a file contains syntactically valid JSON. Exits with error on failure.
validate_json_file() {
  local file="$1"
  if [ ! -f "$file" ]; then
    echo "Error: file not found: $file" >&2
    exit 1
  fi
  if command -v jq &>/dev/null; then
    if ! jq empty "$file" 2>/dev/null; then
      echo "Error: invalid JSON syntax in $file" >&2
      jq empty "$file" || true
      exit 1
    fi
  elif command -v python3 &>/dev/null; then
    if ! python3 -c "import json,sys; json.load(open(sys.argv[1]))" "$file" 2>/dev/null; then
      echo "Error: invalid JSON syntax in $file" >&2
      python3 -c "import json,sys; json.load(open(sys.argv[1]))" "$file" || true
      exit 1
    fi
  else
    echo "Error: neither jq nor python3 is available to validate JSON" >&2
    exit 1
  fi
}

fetch_resource() {
  local url="$1"
  local response
  local http_code

  response=$(curl -s -w "\n%{http_code}" -H "Authorization: Bearer $API_TOKEN" "$url")
  http_code=$(echo "$response" | tail -1)
  body=$(echo "$response" | sed '$d')

  if [ "$http_code" -ge 200 ] && [ "$http_code" -lt 300 ]; then
    echo "$body" | format_json
  else
    echo "Error: HQ API returned HTTP $http_code" >&2
    echo "$body" >&2
    exit 1
  fi
}

# Send a JSON request body from a file. Method is POST, PUT, or PATCH.
send_json_request() {
  local method="$1"
  local url="$2"
  local file="$3"
  local response
  local http_code

  response=$(curl -s -w "\n%{http_code}" -X "$method" \
    -H "Authorization: Bearer $API_TOKEN" \
    -H "Content-Type: application/json" \
    --data-binary "@$file" \
    "$url")
  http_code=$(echo "$response" | tail -1)
  body=$(echo "$response" | sed '$d')

  if [ "$http_code" -ge 200 ] && [ "$http_code" -lt 300 ]; then
    echo "$body" | format_json
  else
    echo "Error: HQ API returned HTTP $http_code" >&2
    echo "$body" >&2
    exit 1
  fi
}

# Send a body-less POST (e.g., publish).
send_empty_post() {
  local url="$1"
  local response
  local http_code

  response=$(curl -s -w "\n%{http_code}" -X POST \
    -H "Authorization: Bearer $API_TOKEN" \
    -H "Content-Length: 0" \
    "$url")
  http_code=$(echo "$response" | tail -1)
  body=$(echo "$response" | sed '$d')

  if [ "$http_code" -ge 200 ] && [ "$http_code" -lt 300 ]; then
    echo "$body" | format_json
  else
    echo "Error: HQ API returned HTTP $http_code" >&2
    echo "$body" >&2
    exit 1
  fi
}

print_usage() {
  echo "HQ API helper for minion chat" >&2
  echo "" >&2
  echo "Usage:" >&2
  echo "  hq fetch skill <name>                 - Get skill details" >&2
  echo "  hq fetch workflow <name>              - Get workflow details" >&2
  echo "  hq fetch project <id>                 - Get project info" >&2
  echo "  hq fetch project-context <id>         - Get project context" >&2
  echo "  hq fetch dag-workflow <id>            - Get DAG workflow details" >&2
  echo "  hq fetch dag-execution <id>           - Get DAG execution details" >&2
  echo "  hq list workspaces                    - List workspaces this minion belongs to" >&2
  echo "  hq list dag-workflows [project_id]    - List DAG workflows (optionally by project)" >&2
  echo "  hq note create <project_id> --title \"Title\" --content \"Body\" [--file path]" >&2
  echo "  hq note create --workspace <ws_id> --title \"Title\" --content \"Body\" [--project-ids <id,id>]" >&2
  echo "  hq note update <project_id> <note_id> --content \"Body\" [--title \"Title\"] [--file path]" >&2
  echo "  hq note update --workspace <ws_id> <note_id> --content \"Body\" [--title \"Title\"] [--status active|archived]" >&2
  echo "  hq note list <project_id>                 - List notes in the project" >&2
  echo "  hq note list --workspace <ws_id>          - List notes in the workspace" >&2
  echo "  hq note get <project_id> <note_id>        - Get a single note" >&2
  echo "  hq note get --workspace <ws_id> <note_id> - Get a single note (workspace-scoped)" >&2
  echo "  hq note search <project_id> <query>       - Search notes in the project" >&2
  echo "  hq note search --workspace <ws_id> <query> - Search notes across the workspace" >&2
  echo "  hq create dag-workflow <body.json>    - Create a DAG workflow (PM only)" >&2
  echo "  hq put dag-workflow <id> <body.json>  - Update DAG workflow draft (PM only)" >&2
  echo "  hq publish dag-workflow <id>          - Publish the draft as a new version (PM only)" >&2
  echo "" >&2
  echo "  DAG node/edge operations (PM only, each returns validation warnings):" >&2
  echo "  hq dag add-node <wf-id> <body.json>              - Add a node to DAG draft" >&2
  echo "  hq dag update-node <wf-id> <node-id> <body.json> - Update a node's properties" >&2
  echo "  hq dag remove-node <wf-id> <node-id>             - Remove a node" >&2
  echo "  hq dag add-edge <wf-id> <body.json>              - Add an edge" >&2
  echo "  hq dag update-edge <wf-id> <edge-id> <body.json> - Update edge kind/label" >&2
  echo "  hq dag remove-edge <wf-id> <edge-id>             - Remove an edge" >&2
  echo "  hq dag validate <wf-id>                          - Validate draft (no publish)" >&2
}

# Main command dispatch
case "${1:-}" in
  fetch)
    resource="${2:-}"
    identifier="${3:-}"

    if [ -z "$resource" ] || [ -z "$identifier" ]; then
      echo "Usage: hq fetch {skill|workflow|project|project-context|dag-workflow|dag-execution} <identifier>" >&2
      exit 1
    fi

    case "$resource" in
      skill)
        fetch_resource "$BASE_URL/skills/$identifier"
        ;;
      workflow)
        fetch_resource "$BASE_URL/workflows/$identifier"
        ;;
      project)
        response=$(curl -s -H "Authorization: Bearer $API_TOKEN" "$BASE_URL/me/projects")
        if command -v jq &>/dev/null; then
          echo "$response" | jq --arg id "$identifier" '.projects[] | select(.id == $id)'
        else
          echo "$response" | format_json
        fi
        ;;
      project-context)
        fetch_resource "$BASE_URL/me/project/$identifier/context"
        ;;
      dag-workflow)
        fetch_resource "$BASE_URL/dag-workflows/$identifier"
        ;;
      dag-execution)
        fetch_resource "$BASE_URL/dag-executions/$identifier"
        ;;
      *)
        echo "Unknown resource: $resource" >&2
        echo "Usage: hq fetch {skill|workflow|project|project-context|dag-workflow|dag-execution} <identifier>" >&2
        exit 1
        ;;
    esac
    ;;

  list)
    resource="${2:-}"
    case "$resource" in
      workspaces)
        fetch_resource "$BASE_URL/workspaces"
        ;;
      dag-workflows)
        project_filter="${3:-}"
        if [ -n "$project_filter" ]; then
          fetch_resource "$BASE_URL/dag-workflows?project_id=$project_filter"
        else
          fetch_resource "$BASE_URL/dag-workflows"
        fi
        ;;
      *)
        echo "Unknown list resource: $resource" >&2
        echo "Usage: hq list {workspaces|dag-workflows}" >&2
        exit 1
        ;;
    esac
    ;;

  create)
    resource="${2:-}"
    case "$resource" in
      dag-workflow)
        body_file="${3:-}"
        if [ -z "$body_file" ]; then
          echo "Usage: hq create dag-workflow <body.json>" >&2
          echo "  body.json must contain at least { project_id, name } and optionally { graph, content, change_summary }" >&2
          exit 1
        fi
        validate_json_file "$body_file"
        send_json_request POST "$BASE_URL/dag-workflows" "$body_file"
        ;;
      *)
        echo "Unknown create resource: $resource" >&2
        echo "Usage: hq create dag-workflow <body.json>" >&2
        exit 1
        ;;
    esac
    ;;

  put)
    resource="${2:-}"
    case "$resource" in
      dag-workflow)
        id="${3:-}"
        body_file="${4:-}"
        if [ -z "$id" ] || [ -z "$body_file" ]; then
          echo "Usage: hq put dag-workflow <id> <body.json>" >&2
          echo "  body.json may contain { graph, content, change_summary, name, is_active }" >&2
          exit 1
        fi
        validate_json_file "$body_file"
        send_json_request PUT "$BASE_URL/dag-workflows/$id" "$body_file"
        ;;
      *)
        echo "Unknown put resource: $resource" >&2
        echo "Usage: hq put dag-workflow <id> <body.json>" >&2
        exit 1
        ;;
    esac
    ;;

  publish)
    resource="${2:-}"
    case "$resource" in
      dag-workflow)
        id="${3:-}"
        if [ -z "$id" ]; then
          echo "Usage: hq publish dag-workflow <id>" >&2
          exit 1
        fi
        send_empty_post "$BASE_URL/dag-workflows/$id/publish"
        ;;
      *)
        echo "Unknown publish resource: $resource" >&2
        echo "Usage: hq publish dag-workflow <id>" >&2
        exit 1
        ;;
    esac
    ;;

  note)
    action="${2:-}"
    shift 2 || true
    # Resolve scope: either "--workspace <ws_id>" (must be first) or positional <project_id>.
    note_base=""
    if [ "${1:-}" = "--workspace" ]; then
      ws_id="${2:-}"
      if [ -z "$ws_id" ]; then
        echo "Error: --workspace requires a workspace_id" >&2
        exit 1
      fi
      note_base="$BASE_URL/workspaces/$ws_id/notes"
      shift 2
    else
      project_id="${1:-}"
      if [ -z "$project_id" ] || [ "${project_id#--}" != "$project_id" ]; then
        echo "Usage: hq note $action <project_id> ... | hq note $action --workspace <ws_id> ..." >&2
        exit 1
      fi
      note_base="$BASE_URL/projects/$project_id/notes"
      shift
    fi
    case "$action" in
      create)
        note_title=""
        note_content=""
        note_file=""
        project_ids_csv=""
        while [ $# -gt 0 ]; do
          case "$1" in
            --title) note_title="$2"; shift 2 ;;
            --content) note_content="$2"; shift 2 ;;
            --file) note_file="$2"; shift 2 ;;
            --project-ids) project_ids_csv="$2"; shift 2 ;;
            *) echo "Unknown option: $1" >&2; exit 1 ;;
          esac
        done
        if [ -z "$note_title" ]; then
          echo "Error: --title is required" >&2
          exit 1
        fi
        if [ -n "$note_file" ]; then
          if [ ! -f "$note_file" ]; then
            echo "Error: file not found: $note_file" >&2
            exit 1
          fi
          note_content=$(cat "$note_file")
        fi
        if [ -z "$note_content" ]; then
          echo "Error: --content or --file is required" >&2
          exit 1
        fi
        if command -v jq &>/dev/null; then
          body=$(jq -n --arg t "$note_title" --arg c "$note_content" '{title: $t, content: $c}')
          if [ -n "$project_ids_csv" ]; then
            body=$(echo "$body" | jq --arg ids "$project_ids_csv" '. + {project_ids: ($ids | split(","))}')
          fi
        elif command -v python3 &>/dev/null; then
          body=$(python3 -c "
import json, sys
d = {'title': sys.argv[1], 'content': sys.argv[2]}
if sys.argv[3]:
    d['project_ids'] = [s for s in sys.argv[3].split(',') if s]
print(json.dumps(d))
" "$note_title" "$note_content" "$project_ids_csv")
        else
          echo "Error: jq or python3 is required to build JSON" >&2
          exit 1
        fi
        tmpfile=$(mktemp)
        echo "$body" > "$tmpfile"
        send_json_request POST "$note_base" "$tmpfile"
        rm -f "$tmpfile"
        ;;
      update)
        note_id="${1:-}"
        if [ -z "$note_id" ]; then
          echo "Error: <note_id> is required" >&2
          exit 1
        fi
        shift
        note_title=""
        note_content=""
        note_file=""
        change_summary=""
        note_status=""
        while [ $# -gt 0 ]; do
          case "$1" in
            --title) note_title="$2"; shift 2 ;;
            --content) note_content="$2"; shift 2 ;;
            --file) note_file="$2"; shift 2 ;;
            --change-summary) change_summary="$2"; shift 2 ;;
            --status) note_status="$2"; shift 2 ;;
            *) echo "Unknown option: $1" >&2; exit 1 ;;
          esac
        done
        if [ -n "$note_file" ]; then
          if [ ! -f "$note_file" ]; then
            echo "Error: file not found: $note_file" >&2
            exit 1
          fi
          note_content=$(cat "$note_file")
        fi
        if [ -z "$note_content" ] && [ -z "$note_title" ] && [ -z "$note_status" ]; then
          echo "Error: at least --content, --file, --title, or --status is required" >&2
          exit 1
        fi
        if command -v jq &>/dev/null; then
          body="{}"
          [ -n "$note_title" ] && body=$(echo "$body" | jq --arg t "$note_title" '. + {title: $t}')
          [ -n "$note_content" ] && body=$(echo "$body" | jq --arg c "$note_content" '. + {content: $c}')
          [ -n "$change_summary" ] && body=$(echo "$body" | jq --arg s "$change_summary" '. + {change_summary: $s}')
          [ -n "$note_status" ] && body=$(echo "$body" | jq --arg st "$note_status" '. + {status: $st}')
        elif command -v python3 &>/dev/null; then
          body=$(python3 -c "
import json, sys
d = {}
if sys.argv[1]: d['title'] = sys.argv[1]
if sys.argv[2]: d['content'] = sys.argv[2]
if sys.argv[3]: d['change_summary'] = sys.argv[3]
if sys.argv[4]: d['status'] = sys.argv[4]
print(json.dumps(d))
" "$note_title" "$note_content" "$change_summary" "$note_status")
        else
          echo "Error: jq or python3 is required to build JSON" >&2
          exit 1
        fi
        tmpfile=$(mktemp)
        echo "$body" > "$tmpfile"
        response=$(curl -s -w "\n%{http_code}" -X PATCH \
          -H "Authorization: Bearer $API_TOKEN" \
          -H "Content-Type: application/json" \
          --data-binary "@$tmpfile" \
          "$note_base/$note_id")
        rm -f "$tmpfile"
        http_code=$(echo "$response" | tail -1)
        body_out=$(echo "$response" | sed '$d')
        if [ "$http_code" -ge 200 ] && [ "$http_code" -lt 300 ]; then
          echo "$body_out" | format_json
        else
          echo "Error: HQ API returned HTTP $http_code" >&2
          echo "$body_out" >&2
          exit 1
        fi
        ;;
      list)
        fetch_resource "$note_base?include_content=false"
        ;;
      get)
        note_id="${1:-}"
        if [ -z "$note_id" ]; then
          echo "Error: <note_id> is required" >&2
          exit 1
        fi
        fetch_resource "$note_base/$note_id"
        ;;
      search)
        query="${1:-}"
        if [ -z "$query" ]; then
          echo "Error: <query> is required" >&2
          exit 1
        fi
        if command -v jq &>/dev/null; then
          encoded=$(printf '%s' "$query" | jq -sRr @uri)
        elif command -v python3 &>/dev/null; then
          encoded=$(python3 -c "import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1]))" "$query")
        else
          encoded="$query"
        fi
        fetch_resource "$note_base/search?q=$encoded"
        ;;
      *)
        echo "Unknown note action: $action" >&2
        echo "Usage: hq note {create|update|list|get|search} ..." >&2
        exit 1
        ;;
    esac
    ;;

  dag)
    action="${2:-}"
    case "$action" in
      add-node)
        wf_id="${3:-}"
        body_file="${4:-}"
        if [ -z "$wf_id" ] || [ -z "$body_file" ]; then
          echo "Usage: hq dag add-node <wf-id> <body.json>" >&2
          echo "  body.json: { type, label?, after?, before?, skill_id?, skill_version_id?, assigned_role?, review?, ... }" >&2
          exit 1
        fi
        validate_json_file "$body_file"
        send_json_request POST "$BASE_URL/dag-workflows/$wf_id/nodes" "$body_file"
        ;;
      update-node)
        wf_id="${3:-}"
        node_id="${4:-}"
        body_file="${5:-}"
        if [ -z "$wf_id" ] || [ -z "$node_id" ] || [ -z "$body_file" ]; then
          echo "Usage: hq dag update-node <wf-id> <node-id> <body.json>" >&2
          echo "  body.json: { label?, skill_version_id?, assigned_role?, review?, ... }" >&2
          exit 1
        fi
        validate_json_file "$body_file"
        send_json_request PATCH "$BASE_URL/dag-workflows/$wf_id/nodes/$node_id" "$body_file"
        ;;
      remove-node)
        wf_id="${3:-}"
        node_id="${4:-}"
        if [ -z "$wf_id" ] || [ -z "$node_id" ]; then
          echo "Usage: hq dag remove-node <wf-id> <node-id>" >&2
          exit 1
        fi
        # DELETE with no body
        response=$(curl -s -w "\n%{http_code}" -X DELETE \
          -H "Authorization: Bearer $API_TOKEN" \
          "$BASE_URL/dag-workflows/$wf_id/nodes/$node_id")
        http_code=$(echo "$response" | tail -1)
        body=$(echo "$response" | sed '$d')
        if [ "$http_code" -ge 200 ] && [ "$http_code" -lt 300 ]; then
          echo "$body" | format_json
        else
          echo "Error: HQ API returned HTTP $http_code" >&2
          echo "$body" >&2
          exit 1
        fi
        ;;
      add-edge)
        wf_id="${3:-}"
        body_file="${4:-}"
        if [ -z "$wf_id" ] || [ -z "$body_file" ]; then
          echo "Usage: hq dag add-edge <wf-id> <body.json>" >&2
          echo "  body.json: { source, target, kind?, condition_label? }" >&2
          exit 1
        fi
        validate_json_file "$body_file"
        send_json_request POST "$BASE_URL/dag-workflows/$wf_id/edges" "$body_file"
        ;;
      update-edge)
        wf_id="${3:-}"
        edge_id="${4:-}"
        body_file="${5:-}"
        if [ -z "$wf_id" ] || [ -z "$edge_id" ] || [ -z "$body_file" ]; then
          echo "Usage: hq dag update-edge <wf-id> <edge-id> <body.json>" >&2
          echo "  body.json: { kind?, condition_label? }" >&2
          exit 1
        fi
        validate_json_file "$body_file"
        send_json_request PATCH "$BASE_URL/dag-workflows/$wf_id/edges/$edge_id" "$body_file"
        ;;
      remove-edge)
        wf_id="${3:-}"
        edge_id="${4:-}"
        if [ -z "$wf_id" ] || [ -z "$edge_id" ]; then
          echo "Usage: hq dag remove-edge <wf-id> <edge-id>" >&2
          exit 1
        fi
        response=$(curl -s -w "\n%{http_code}" -X DELETE \
          -H "Authorization: Bearer $API_TOKEN" \
          "$BASE_URL/dag-workflows/$wf_id/edges/$edge_id")
        http_code=$(echo "$response" | tail -1)
        body=$(echo "$response" | sed '$d')
        if [ "$http_code" -ge 200 ] && [ "$http_code" -lt 300 ]; then
          echo "$body" | format_json
        else
          echo "Error: HQ API returned HTTP $http_code" >&2
          echo "$body" >&2
          exit 1
        fi
        ;;
      validate)
        wf_id="${3:-}"
        if [ -z "$wf_id" ]; then
          echo "Usage: hq dag validate <wf-id>" >&2
          exit 1
        fi
        send_empty_post "$BASE_URL/dag-workflows/$wf_id/validate"
        ;;
      *)
        echo "Unknown dag action: $action" >&2
        echo "Usage: hq dag {add-node|update-node|remove-node|add-edge|remove-edge|validate} ..." >&2
        exit 1
        ;;
    esac
    ;;

  *)
    print_usage
    exit 1
    ;;
esac
