#!/bin/bash

# shellcheck shell=bash

# Initialise JSON backend (jq preferred, python3/node fallbacks)
bridge_init_json_backend() {
  if [[ -n "${BRIDGE_JSON_BACKEND:-}" ]]; then
    return 0
  fi

  if have_cmd jq; then
    BRIDGE_JSON_BACKEND="jq"
  elif command -v python3 >/dev/null 2>&1; then
    BRIDGE_JSON_BACKEND="python"
  elif command -v node >/dev/null 2>&1; then
    BRIDGE_JSON_BACKEND="node"
  else
    err "jq (or python3/node fallback) required for JSON processing"
    exit 1
  fi
}

# Internal helper to emit a JSON object.
# Arguments use the format `key[:type]=value` where type defaults to `s` (string).
# Supported types: s (string), n (numeric), b (boolean), j (raw JSON), null (null literal).
json_object() {
  bridge_init_json_backend
  case "$BRIDGE_JSON_BACKEND" in
    jq)
      local -a jq_args=()
      local -a jq_expr_parts=()
      local idx=0
      local spec key rest type value var_name
      for spec in "$@"; do
        [[ -z "$spec" ]] && continue
        key="${spec%%:*}"
        rest="${spec#*:}"
        if [[ "$key" == "$rest" ]]; then
          rest="s"
        fi
        if [[ "$rest" == *=* ]]; then
          type="${rest%%=*}"
          value="${rest#*=}"
        else
          type="s"
          value="$rest"
        fi
        var_name="__jq_arg_$idx"
        idx=$((idx + 1))
        case "$type" in
          s)
            jq_args+=(--arg "$var_name" "$value")
            jq_expr_parts+=("\"$key\": \$$var_name")
            ;;
          n)
            jq_args+=(--argjson "$var_name" "${value:-0}")
            jq_expr_parts+=("\"$key\": \$$var_name")
            ;;
          b)
            jq_args+=(--argjson "$var_name" "$(printf '%s' "$value" | tr '[:upper:]' '[:lower:]')")
            jq_expr_parts+=("\"$key\": \$$var_name")
            ;;
          j)
            jq_args+=(--argjson "$var_name" "${value:-null}")
            jq_expr_parts+=("\"$key\": \$$var_name")
            ;;
          null)
            jq_expr_parts+=("\"$key\": null")
            ;;
          *)
            jq_args+=(--arg "$var_name" "$value")
            jq_expr_parts+=("\"$key\": \$$var_name")
            ;;
        esac
      done
      local expr="{"
      local first=1
      local part
      for part in "${jq_expr_parts[@]}"; do
        if [[ $first -eq 1 ]]; then
          expr+="$part"
          first=0
        else
          expr+=", $part"
        fi
      done
      expr+="}"
      jq -n "${jq_args[@]}" "$expr"
      ;;
    python)
      python3 - "$@" <<'PY'
import json
import sys

def coerce(spec):
    if not spec:
        return None
    if ':' in spec:
        key, rest = spec.split(':', 1)
    else:
        key, rest = spec, 's'
    if '=' in rest:
        typ, value = rest.split('=', 1)
    else:
        typ, value = rest, ''
    if typ == 's':
        return key, value
    if typ == 'n':
        if value.strip() == '':
            return key, 0
        if any(c in value for c in '.eE'):
            return key, float(value)
        return key, int(value)
    if typ == 'b':
        return key, value.lower() in ('true', '1', 'yes', 'on')
    if typ == 'j':
        return key, json.loads(value) if value else None
    if typ == 'null':
        return key, None
    return key, value

obj = {}
for spec in sys.argv[1:]:
    item = coerce(spec)
    if item:
        key, value = item
        obj[key] = value

print(json.dumps(obj))
PY
      ;;
    node)
      node - "$@" <<'NODE'
const specs = process.argv.slice(2);
const obj = {};

for (const spec of specs) {
  if (!spec) continue;
  let key, rest;
  if (spec.includes(':')) {
    [key, rest] = spec.split(':', 2);
  } else {
    key = spec;
    rest = 's';
  }
  let type, value;
  if (rest.includes('=')) {
    [type, value] = rest.split('=', 2);
  } else {
    type = rest;
    value = '';
  }
  switch (type) {
    case 's':
      obj[key] = value;
      break;
    case 'n':
      if (!value.trim()) {
        obj[key] = 0;
      } else if (/\.|e|E/.test(value)) {
        obj[key] = Number.parseFloat(value);
      } else {
        obj[key] = Number.parseInt(value, 10);
      }
      break;
    case 'b':
      obj[key] = ['true', '1', 'yes', 'on'].includes(value.toLowerCase());
      break;
    case 'j':
      obj[key] = value ? JSON.parse(value) : null;
      break;
    case 'null':
      obj[key] = null;
      break;
    default:
      obj[key] = value;
  }
}

process.stdout.write(JSON.stringify(obj));
NODE
      ;;
  esac
}

# Helper to convert newline-delimited text into a JSON array (dropping trailing blank)
json_array_from_lines() {
  bridge_init_json_backend
  local input="$1"
  case "$BRIDGE_JSON_BACKEND" in
    jq)
      # More robust handling: split and filter out empty lines, then convert to JSON array
      printf '%s' "$input" | jq -R -s 'split("\n") | map(select(length > 0))'
      ;;
    python)
      printf '%s' "$input" | python3 <<'PY'
import json
import sys
text = sys.stdin.read()
lines = text.split('\n')
if lines and lines[-1] == '':
    lines = lines[:-1]
print(json.dumps(lines))
PY
      ;;
    node)
      printf '%s' "$input" | node <<'NODE'
const fs = require('fs');
const text = fs.readFileSync(0, 'utf8');
let lines = text.split('\n');
if (lines.length && lines[lines.length - 1] === '') {
  lines = lines.slice(0, -1);
}
process.stdout.write(JSON.stringify(lines));
NODE
      ;;
  esac
}

# Helper to encode arbitrary text as a JSON string
json_stringify() {
  bridge_init_json_backend
  local input="$1"
  case "$BRIDGE_JSON_BACKEND" in
    jq)
      printf '%s' "$input" | jq -R -s .
      ;;
    python)
      python3 - <<'PY' "$input"
import json
import sys
print(json.dumps(sys.argv[1]))
PY
      ;;
    node)
      node - <<'NODE' "$input"
const input = process.argv[2] ?? '';
process.stdout.write(JSON.stringify(input));
NODE
      ;;
  esac
}

# Helper to convert newline-separated JSON objects into an array
json_array_from_json_lines() {
  bridge_init_json_backend
  local input="$1"
  case "$BRIDGE_JSON_BACKEND" in
    jq)
      printf '%s' "$input" | jq -s .
      ;;
    python)
      printf '%s' "$input" | python3 <<'PY'
import json
import sys
items = []
for line in sys.stdin:
    line = line.strip()
    if not line:
        continue
    items.append(json.loads(line))
print(json.dumps(items))
PY
      ;;
    node)
      printf '%s' "$input" | node <<'NODE'
const fs = require('fs');
const lines = fs.readFileSync(0, 'utf8').split(/\r?\n/);
const items = [];
for (const line of lines) {
  if (!line.trim()) continue;
  items.push(JSON.parse(line));
}
process.stdout.write(JSON.stringify(items));
NODE
      ;;
  esac
}

# Helper function to output JSON safely
json_output() {
  if [[ $# -eq 0 ]]; then
    echo "null"
  else
    local joined
    joined="$(printf '%s\n' "$@")"
    json_array_from_lines "$joined"
  fi
}

# Helper function to output error
json_error() {
  local message="$1"
  json_object "error:s=$message"
}

# Extract a concise summary line from git output for error dialogs
git_error_summary() {
  local text="${1-}"
  local trimmed=""
  local candidate=""
  local fallback=""
  while IFS= read -r line; do
    line="${line%$'\r'}" # drop trailing carriage returns
    trimmed="${line#${line%%[![:space:]]*}}"
    if [[ -z "${trimmed//[[:space:]]/}" ]]; then
      continue
    fi
    local is_hint=0
    if [[ $trimmed == hint:* || $trimmed == Hint:* ]]; then
      is_hint=1
    fi
    if [[ $is_hint -eq 0 ]]; then
      fallback="$trimmed"
    elif [[ -z "$fallback" ]]; then
      fallback="$trimmed"
    fi
    if [[ $is_hint -eq 0 ]]; then
      case "$trimmed" in
        fatal:*|Fatal:*|error:*|Error:*|CONFLICT*|Conflict*|\
          *merge\ failed*|*Merge\ failed*|\
          *could\ not\ apply*)
          candidate="$trimmed"
          ;;
      esac
    fi
  done <<<"$text"
  if [[ -n "$candidate" ]]; then
    printf '%s' "$candidate"
  else
    printf '%s' "$fallback"
  fi
}

# Helper function to output structured worktree data
json_worktrees() {
  bridge_init_json_backend
  case "$BRIDGE_JSON_BACKEND" in
    jq)
      git_list_worktrees | jq -R -s '
        split("\n")[:-1] |
        map(split("\t")) |
        map({
          path: .[0],
          branch: .[1],
          sha: .[2]
        })'
      ;;
    python)
      git_list_worktrees | python3 <<'PY'
import json
import sys
items = []
for line in sys.stdin:
    line = line.rstrip('\n')
    if not line:
        continue
    parts = line.split('\t')
    while len(parts) < 3:
        parts.append('')
    items.append({
        "path": parts[0],
        "branch": parts[1],
        "sha": parts[2],
    })
print(json.dumps(items))
PY
      ;;
    node)
      git_list_worktrees | node <<'NODE'
const fs = require('fs');
const input = fs.readFileSync(0, 'utf8');
const items = [];
for (const line of input.split(/\r?\n/)) {
  if (!line) continue;
  const parts = line.split('\t');
  while (parts.length < 3) parts.push('');
  items.push({ path: parts[0], branch: parts[1], sha: parts[2] });
}
process.stdout.write(JSON.stringify(items));
NODE
      ;;
  esac
}

# Helper function to load Anthropic API key from config or .env file
load_anthropic_api_key() {
  # First try to load from ~/.orchestra/config.json
  local config_file="$HOME/.orchestra/config.json"
  if [[ -f "$config_file" ]]; then
    if have_cmd jq; then
      local api_key
      api_key="$(jq -r '.anthropic_api_key // empty' "$config_file" 2>/dev/null)"
      if [[ -n "$api_key" ]] && [[ "$api_key" != "null" ]]; then
        export ANTHROPIC_API_KEY="$api_key"
        return 0
      fi
    else
      bridge_init_json_backend
      local api_key
      case "$BRIDGE_JSON_BACKEND" in
        python)
          api_key="$(python3 - "$config_file" <<'PY'
import json
import sys
try:
    data = json.load(open(sys.argv[1], 'r'))
except Exception:
    data = {}
value = data.get('anthropic_api_key') or ''
print(value)
PY
)"
          ;;
        node)
          api_key="$(node - "$config_file" <<'NODE'
const fs = require('fs');
let value = '';
try {
  const data = JSON.parse(fs.readFileSync(process.argv[2], 'utf8'));
  value = data.anthropic_api_key || '';
} catch (err) {
  value = '';
}
process.stdout.write(value);
NODE
)"
          ;;
      esac
      if [[ -n "${api_key:-}" ]]; then
        export ANTHROPIC_API_KEY="$api_key"
        return 0
      fi
    fi
  fi
  
  # Fallback to .env file in current directory
  if [[ -f ".env" ]]; then
    source .env
  fi
  
  # Fallback to .env file in repo root
  if [[ -z "${ANTHROPIC_API_KEY-}" ]]; then
    local repo_root
    repo_root="$(git_repo_root 2>/dev/null)"
    if [[ -n "$repo_root" ]] && [[ -f "$repo_root/.env" ]]; then
      source "$repo_root/.env"
    fi
  fi
}

# Helper function to load OpenAI API key from config or .env file
load_openai_api_key() {
  local config_file="$HOME/.orchestra/config.json"
  if [[ -f "$config_file" ]]; then
    if have_cmd jq; then
      local api_key
      api_key="$(jq -r '.openai_api_key // empty' "$config_file" 2>/dev/null)"
      if [[ -n "$api_key" ]] && [[ "$api_key" != "null" ]]; then
        export OPENAI_API_KEY="$api_key"
        return 0
      fi
    else
      bridge_init_json_backend
      local api_key
      case "$BRIDGE_JSON_BACKEND" in
        python)
          api_key="$(python3 - "$config_file" <<'PY'
import json
import sys
try:
    data = json.load(open(sys.argv[1], 'r'))
except Exception:
    data = {}
value = data.get('openai_api_key') or ''
print(value)
PY
)"
          ;;
        node)
          api_key="$(node - "$config_file" <<'NODE'
const fs = require('fs');
let value = '';
try {
  const data = JSON.parse(fs.readFileSync(process.argv[2], 'utf8'));
  value = data.openai_api_key || '';
} catch (err) {
  value = '';
}
process.stdout.write(value);
NODE
)"
          ;;
      esac
      if [[ -n "${api_key:-}" ]]; then
        export OPENAI_API_KEY="$api_key"
        return 0
      fi
    fi
  fi

  if [[ -f ".env" ]]; then
    source .env
  fi

  if [[ -z "${OPENAI_API_KEY-}" ]]; then
    local repo_root
    repo_root="$(git_repo_root 2>/dev/null)"
    if [[ -n "$repo_root" ]] && [[ -f "$repo_root/.env" ]]; then
      source "$repo_root/.env"
    fi
  fi
}

# Helper function to load primary AI provider
load_ai_primary_provider() {
  if [[ -n "${AI_PRIMARY_PROVIDER:-}" ]]; then
    return 0
  fi

  local config_file="$HOME/.orchestra/config.json"
  local provider=""
  if [[ -f "$config_file" ]]; then
    if have_cmd jq; then
      provider="$(jq -r '.ai_primary_provider // empty' "$config_file" 2>/dev/null)"
    else
      bridge_init_json_backend
      case "$BRIDGE_JSON_BACKEND" in
        python)
          provider="$(python3 - "$config_file" <<'PY'
import json
import sys
try:
    data = json.load(open(sys.argv[1], 'r'))
except Exception:
    data = {}
value = data.get('ai_primary_provider') or ''
print(value)
PY
)"
          ;;
        node)
          provider="$(node - "$config_file" <<'NODE'
const fs = require('fs');
let value = '';
try {
  const data = JSON.parse(fs.readFileSync(process.argv[2], 'utf8'));
  value = data.ai_primary_provider || '';
} catch (err) {
  value = '';
}
process.stdout.write(value);
NODE
)"
          ;;
      esac
    fi
  fi

  provider="$(printf '%s' "$provider" | tr '[:upper:]' '[:lower:]')"
  if [[ "$provider" != "openai" && "$provider" != "anthropic" ]]; then
    provider="anthropic"
  fi
  export AI_PRIMARY_PROVIDER="$provider"
}

# Helper function to load OpenAI model
load_openai_model() {
  if [[ -n "${OPENAI_MODEL:-}" ]]; then
    return 0
  fi

  local config_file="$HOME/.orchestra/config.json"
  local model=""
  if [[ -f "$config_file" ]]; then
    if have_cmd jq; then
      model="$(jq -r '.openai_model // empty' "$config_file" 2>/dev/null)"
    else
      bridge_init_json_backend
      case "$BRIDGE_JSON_BACKEND" in
        python)
          model="$(python3 - "$config_file" <<'PY'
import json
import sys
try:
    data = json.load(open(sys.argv[1], 'r'))
except Exception:
    data = {}
value = data.get('openai_model') or ''
print(value)
PY
)"
          ;;
        node)
          model="$(node - "$config_file" <<'NODE'
const fs = require('fs');
let value = '';
try {
  const data = JSON.parse(fs.readFileSync(process.argv[2], 'utf8'));
  value = data.openai_model || '';
} catch (err) {
  value = '';
}
process.stdout.write(value);
NODE
)"
          ;;
      esac
    fi
  fi

  if [[ -z "$model" || "$model" == "null" ]]; then
    model="gpt-4o-mini"
  fi
  export OPENAI_MODEL="$model"
}
