#!/usr/bin/env bash
# smart-env.sh — Smart skill script locator, bash resolver, and shared helpers.
# Sourced by other smart-*.sh scripts and by the agent (per SKILL.md).
# Sets: SMART_BASH, SMART_STATE, SMART_GUARD, SMART_HANDOFF, SMART_ARCHIVE,
#       SMART_OPENSPEC_ROOT, SMART_CHANGES_DIR, SMART_DIR, SMART_SCRIPTS_DIR.
# Provides shared helpers: colors, validate_change_name, validate_enum,
#       validate_path_field, yaml_field, replace_yaml_field, strip_inline_comment,
#       strip_wrapping_quotes, hash_stream, hash_file, file_nonempty.
# 样板版本: v1  (keep in sync across all smart-*.sh and SKILL.md)

# --- locate this file ---
if [ -z "${SMART_ENV:-}" ]; then
  if [ -n "${BASH_SOURCE[1]:-}" ]; then SMART_ENV="${BASH_SOURCE[1]}"
  elif [ -n "${BASH_SOURCE[0]:-}" ]; then SMART_ENV="${BASH_SOURCE[0]}"
  else SMART_ENV="$(find . "$HOME"/.*/skills "$HOME/.config" "$HOME/.gemini" -path '*/scripts/smart-env.sh' -type f -print -quit 2>/dev/null)"; fi
fi
if [ -z "${SMART_ENV:-}" ] || [ ! -f "$SMART_ENV" ]; then
  echo "ERROR: smart-env.sh not found. Ensure the smart skill is installed (scripts/smart-env.sh)." >&2
  echo "Expected path pattern: */scripts/smart-env.sh under project root or platform skill directories." >&2
  return 1 2>/dev/null || exit 1
fi

SMART_SCRIPTS_DIR="$(cd "$(dirname "$SMART_ENV")" && pwd -P)"
SMART_DIR="$(cd "$SMART_SCRIPTS_DIR/.." && pwd -P)"

export SMART_STATE="${SMART_STATE:-$SMART_SCRIPTS_DIR/smart-state.sh}"
export SMART_GUARD="${SMART_GUARD:-$SMART_SCRIPTS_DIR/smart-guard.sh}"
export SMART_HANDOFF="${SMART_HANDOFF:-$SMART_SCRIPTS_DIR/smart-handoff.sh}"
export SMART_ARCHIVE="${SMART_ARCHIVE:-$SMART_SCRIPTS_DIR/smart-archive.sh}"
export SMART_OPENSPEC_ROOT="${SMART_OPENSPEC_ROOT:-openspec}"
export SMART_CHANGES_DIR="${SMART_CHANGES_DIR:-$SMART_OPENSPEC_ROOT/changes}"
export SMART_DIR
export SMART_SCRIPTS_DIR

# --- bash resolver (port of comet-env.sh; avoids Windows WSL bash.exe) ---
_smart_bash_is_usable() {
  local cand="$1"
  [ -n "$cand" ] || return 1
  case "$cand" in
    */Windows/System32/bash.exe|*/windows/system32/bash.exe|*\\Windows\\System32\\bash.exe|*\\windows\\system32\\bash.exe) return 1 ;;
  esac
  "$cand" -lc 'printf smart-bash-ok' >/dev/null 2>&1
}
_smart_resolve_bash() {
  local cand
  if _smart_bash_is_usable "${SMART_BASH:-}"; then printf '%s\n' "$SMART_BASH"; return 0; fi
  if _smart_bash_is_usable "${BASH:-}"; then printf '%s\n' "$BASH"; return 0; fi
  cand="$(command -v sh 2>/dev/null | awk '{ sub(/\/sh(\.exe)?$/, "/bash.exe"); print }')"
  if _smart_bash_is_usable "$cand"; then printf '%s\n' "$cand"; return 0; fi
  cand="$(command -v bash 2>/dev/null || true)"
  if _smart_bash_is_usable "$cand"; then printf '%s\n' "$cand"; return 0; fi
  return 1
}
SMART_BASH="$(_smart_resolve_bash || true)"
export SMART_BASH

_smart_env_missing=0
[ -n "$SMART_BASH" ] || { echo "ERROR: usable bash not found. Install Git Bash or set SMART_BASH. Windows WSL launcher bash.exe is not supported." >&2; _smart_env_missing=1; }
for _s in "$SMART_STATE" "$SMART_GUARD" "$SMART_HANDOFF" "$SMART_ARCHIVE"; do
  [ -f "$_s" ] || { echo "WARN: expected script not found: $_s" >&2; }
done
unset _s _smart_env_missing
unset -f _smart_bash_is_usable _smart_resolve_bash

# --- shared color helpers ---
red()    { printf '\033[31m%s\033[0m\n' "$1" >&2; }
green()  { printf '\033[32m%s\033[0m\n' "$1" >&2; }
yellow() { printf '\033[33m%s\033[0m\n' "$1" >&2; }
warn()   { printf '\033[33m%s\033[0m\n' "$1" >&2; }

# --- shared input validation (port of comet-state.sh) ---
validate_change_name() {
  local name="$1"
  [ -n "$name" ] || { red "ERROR: Change name cannot be empty"; exit 1; }
  [[ "$name" =~ ^[a-zA-Z0-9_-]+$ ]] || { red "ERROR: Invalid change name: '$name' (valid: a-z A-Z 0-9 - _)"; exit 1; }
  [[ "$name" =~ \.\. ]] && { red "ERROR: Change name cannot contain '..' (path traversal not allowed)"; exit 1; }
}
validate_enum() {
  local value="$1"; shift
  local valid
  for valid in "$@"; do [ "$value" = "$valid" ] && return 0; done
  red "ERROR: Invalid value: '$value' (valid: $*)"; exit 1
}
validate_path_field() {
  local value="$1" field="$2"
  [ -z "$value" ] || [ "$value" = "null" ] && return 0
  case "$value" in
    /*|~*|[A-Za-z]:*|\\*) red "ERROR: $field must be a relative path within the repo: '$value'"; exit 1 ;;
  esac
  [[ "$value" =~ \.\. ]] && { red "ERROR: $field cannot contain '..' (path traversal): '$value'"; exit 1; }
}

# --- shared flat-YAML helpers (port of comet-state.sh; .smart.yaml is key: value) ---
strip_inline_comment() {
  local value="$1"
  printf '%s\n' "$value" | awk -v squote="'" '
    { out=""; quote=""
      for (i=1; i<=length($0); i++) { c=substr($0,i,1)
        if (quote=="") { if (c=="\""||c==squote) { quote=c } else if (c=="#" && (i==1||substr($0,i-1,1)~/[[:space:]]/)) { sub(/[[:space:]]+$/,"",out); print out; next } }
        else if (c==quote) { quote="" }
        out=out c }
      print out }'
}
strip_wrapping_quotes() {
  local value="$1"
  case "$value" in
    \"*\") printf '%s\n' "${value:1:${#value}-2}" ;;
    \'*\") printf '%s\n' "${value:1:${#value}-2}" ;;
    *) printf '%s\n' "$value" ;;
  esac
}
# yaml_field <field> <yaml_file>  -> prints value (empty if missing)
yaml_field() {
  local field="$1" yaml_file="$2"
  [ -f "$yaml_file" ] || return 0
  local value
  value=$(grep "^${field}:" "$yaml_file" 2>/dev/null | sed "s/^${field}: *//" || true)
  value=$(strip_inline_comment "$value")
  strip_wrapping_quotes "$value"
}
# replace_yaml_field <yaml_file> <field> <value>  -> read-modify-write, dedup keeping last
replace_yaml_field() {
  local yaml_file="$1" field="$2" value="$3" tmp_file
  tmp_file=$(mktemp); chmod 600 "$tmp_file"
  awk -v field="$field" -v value="$value" '
    index($0, field ":") == 1 { $0 = field ": " value }
    { buf[NR]=$0; keys[NR]=$0; sub(/:.*$/,"",keys[NR]); n=NR }
    END { for (i=1;i<=n;i++) last[keys[i]]=i; for (i=1;i<=n;i++) if (last[keys[i]]==i) print buf[i] }
  ' "$yaml_file" > "$tmp_file"
  mv "$tmp_file" "$yaml_file"
}

# --- shared hash/file helpers ---
hash_stream() {
  if command -v sha256sum >/dev/null 2>&1; then sha256sum | awk '{print $1}'
  elif command -v shasum >/dev/null 2>&1; then shasum -a 256 | awk '{print $1}'
  else red "ERROR: sha256sum or shasum is required"; exit 1; fi
}
hash_file() {
  local file="$1"
  if command -v sha256sum >/dev/null 2>&1; then sha256sum "$file" | awk '{print $1}'
  elif command -v shasum >/dev/null 2>&1; then shasum -a 256 "$file" | awk '{print $1}'
  else red "ERROR: sha256sum or shasum is required"; exit 1; fi
}
file_nonempty() { [ -f "$1" ] && [ -s "$1" ]; }
