#!/usr/bin/env bash

resolve_route() {
  case "$1" in
    primary) printf '%s\n' 'https://api.nexagwapi.com/v1' ;;
    hongkong) printf '%s\n' 'https://api.nexagw.org/v1' ;;
    *) return 2 ;;
  esac
}

sha256_file() {
  if command -v sha256sum >/dev/null 2>&1; then
    sha256sum "$1" | awk '{print $1}'
  elif command -v shasum >/dev/null 2>&1; then
    shasum -a 256 "$1" | awk '{print $1}'
  else
    printf '%s\n' 'A SHA-256 utility is required.' >&2
    return 1
  fi
}

shell_quote() {
  printf "'"
  printf '%s' "$1" | sed "s/'/'\\\\''/g"
  printf "'"
}

select_profile() {
  case "${SHELL:-}" in
    */zsh) printf '%s\n' "$HOME/.zshrc" ;;
    *)
      if [ -f "$HOME/.bash_profile" ]; then
        printf '%s\n' "$HOME/.bash_profile"
      else
        printf '%s\n' "$HOME/.profile"
      fi
      ;;
  esac
}

update_codex_config() {
  config_path=$1
  route=$2
  route_url=$(resolve_route "$route") || {
    printf 'Unknown route: %s\n' "$route" >&2
    return 2
  }
  config_dir=$(dirname "$config_path")
  mkdir -p "$config_dir" || return 1
  temp_path=$(mktemp "${config_path}.nexagw.XXXXXX") || return 1

  if [ -f "$config_path" ]; then
    input_path=$config_path
  else
    input_path=/dev/null
  fi

  if ! awk -v route_url="$route_url" '
    function table_name(line, value) {
      value = line
      sub(/^[[:space:]]*/, "", value)
      sub(/[[:space:]]*(#.*)?$/, "", value)
      if (value ~ /^\[\[[^][]+\]\]$/) {
        sub(/^\[\[/, "", value)
        sub(/\]\]$/, "", value)
        return value
      }
      if (value ~ /^\[[^][]+\]$/) {
        sub(/^\[/, "", value)
        sub(/\]$/, "", value)
        return value
      }
      return ""
    }
    BEGIN {
      root_mode = 1
      managed = 0
      managed_count = 0
      fatal = 0
    }
    {
      raw = $0
      trimmed = raw
      sub(/^[[:space:]]*/, "", trimmed)
      if (substr(trimmed, 1, 1) == "[") {
        name = table_name(trimmed)
        if (name == "") {
          print "Malformed TOML table header: " raw > "/dev/stderr"
          fatal = 1
          exit 41
        }
        root_mode = 0
        if (name == "model_providers.nexagw") {
          managed_count++
          if (managed_count > 1) {
            print "Duplicate [model_providers.nexagw] tables were found." > "/dev/stderr"
            fatal = 1
            exit 42
          }
          managed = 1
          next
        }
        managed = 0
      }
      if (managed) next
      if (root_mode) {
        if (raw ~ /^[[:space:]]*(model|model_provider)[[:space:]]*=/) next
        root[++root_count] = raw
      } else {
        rest[++rest_count] = raw
      }
    }
    END {
      if (fatal) exit
      print "model = \"gpt-5.6-sol\""
      print "model_provider = \"nexagw\""
      root_start = 1
      root_end = root_count
      while (root_start <= root_end && root[root_start] ~ /^[[:space:]]*$/) root_start++
      while (root_end >= root_start && root[root_end] ~ /^[[:space:]]*$/) root_end--
      if (root_start <= root_end) {
        print ""
        for (i = root_start; i <= root_end; i++) print root[i]
      }
      print ""
      print "[model_providers.nexagw]"
      print "name = \"NexaGW\""
      print "base_url = \"" route_url "\""
      print "env_key = \"NEXAGW_API_KEY\""
      print "wire_api = \"responses\""
      if (rest_count > 0 && rest[1] != "") print ""
      for (i = 1; i <= rest_count; i++) print rest[i]
    }
  ' "$input_path" > "$temp_path"; then
    rm -f "$temp_path"
    return 1
  fi

  provider_count=$(grep -c '^\[model_providers\.nexagw\]$' "$temp_path" 2>/dev/null || true)
  if [ "$provider_count" -ne 1 ] || ! grep -Fqx "base_url = \"$route_url\"" "$temp_path"; then
    printf '%s\n' 'Generated configuration validation failed.' >&2
    rm -f "$temp_path"
    return 1
  fi
  mv "$temp_path" "$config_path"
}

write_manifest() {
  directory=$1
  manifest_temp=$(mktemp "${directory}/SHA256SUMS.XXXXXX") || return 1
  : > "$manifest_temp"
  for file_path in "$directory"/*; do
    [ -f "$file_path" ] || continue
    file_name=$(basename "$file_path")
    case "$file_name" in SHA256SUMS|SHA256SUMS.*) continue ;; esac
    printf '%s  %s\n' "$(sha256_file "$file_path")" "$file_name" >> "$manifest_temp" || {
      rm -f "$manifest_temp"
      return 1
    }
  done
  LC_ALL=C sort "$manifest_temp" > "$directory/SHA256SUMS"
  rm -f "$manifest_temp"
}

verify_manifest() {
  backup_path=$1
  manifest="$backup_path/SHA256SUMS"
  [ -f "$manifest" ] || {
    printf 'Missing backup manifest: %s\n' "$manifest" >&2
    return 1
  }
  while IFS= read -r line || [ -n "$line" ]; do
    [ -n "$line" ] || continue
    digest=${line%%  *}
    name=${line#*  }
    case "$digest" in *[!0-9a-f]*|'') return 1 ;; esac
    [ "${#digest}" -eq 64 ] || return 1
    case "$name" in ''|*/*|*\\*) return 1 ;; esac
    [ -f "$backup_path/$name" ] || return 1
    [ "$(sha256_file "$backup_path/$name")" = "$digest" ] || {
      printf 'Backup checksum mismatch: %s\n' "$name" >&2
      return 1
    }
  done < "$manifest"
}

json_escape() {
  printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g'
}

new_backup() {
  codex_home=$1
  config_home=$2
  backup_root="$codex_home/nexagw-backups"
  mkdir -p "$backup_root" || return 1
  chmod 700 "$backup_root" 2>/dev/null || true
  stamp=$(date '+%Y%m%d_%H%M%S')
  suffix=0
  backup_path="$backup_root/${stamp}_$$"
  while [ -e "$backup_path" ]; do
    suffix=$((suffix + 1))
    backup_path="$backup_root/${stamp}_$$_$suffix"
  done
  mkdir -m 700 "$backup_path" || return 1

  if [ -f "$codex_home/config.toml" ]; then
    cp "$codex_home/config.toml" "$backup_path/config.toml.before" || return 1
  else
    printf '%s\n' 'missing' > "$backup_path/config.toml.missing"
  fi
  if [ -f "$config_home/env" ]; then
    cp "$config_home/env" "$backup_path/env.before" || return 1
  else
    printf '%s\n' 'missing' > "$backup_path/env.missing"
  fi

  profile_path=$(select_profile)
  printf '%s\n' "$profile_path" > "$backup_path/profile.path"
  if [ -f "$profile_path" ]; then
    cp "$profile_path" "$backup_path/profile.before" || return 1
  else
    printf '%s\n' 'missing' > "$backup_path/profile.missing"
  fi

  env_present=false
  profile_present=false
  [ -f "$config_home/env" ] && env_present=true
  [ -f "$profile_path" ] && profile_present=true
  printf '{"envFilePresent":%s,"profilePresent":%s}\n' "$env_present" "$profile_present" > "$backup_path/environment.json"
  printf '{"type":"backup","createdAt":"%s","codexHome":"%s","configHome":"%s"}\n' \
    "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "$(json_escape "$codex_home")" "$(json_escape "$config_home")" > "$backup_path/operation.json"
  write_manifest "$backup_path" || return 1
  printf '%s\n' "$backup_path"
}

restore_backup() {
  backup_path=$1
  codex_home=$2
  config_home=$3
  verify_manifest "$backup_path" || return 1
  safety_backup=$(new_backup "$codex_home" "$config_home") || return 1
  mkdir -p "$codex_home" "$config_home" || return 1

  if [ -f "$backup_path/config.toml.before" ]; then
    cp "$backup_path/config.toml.before" "$codex_home/config.toml" || return 1
  elif [ -f "$backup_path/config.toml.missing" ]; then
    rm -f "$codex_home/config.toml"
  else
    return 1
  fi

  if [ -f "$backup_path/env.before" ]; then
    cp "$backup_path/env.before" "$config_home/env" || return 1
    chmod 600 "$config_home/env" || return 1
  elif [ -f "$backup_path/env.missing" ]; then
    rm -f "$config_home/env"
  else
    return 1
  fi

  profile_path=$(sed -n '1p' "$backup_path/profile.path")
  if [ -f "$backup_path/profile.before" ]; then
    mkdir -p "$(dirname "$profile_path")" || return 1
    cp "$backup_path/profile.before" "$profile_path" || return 1
  elif [ -f "$backup_path/profile.missing" ]; then
    rm -f "$profile_path"
  else
    return 1
  fi
  printf 'SAFETY_BACKUP=%s\n' "$safety_backup"
}

persist_key() {
  key=$1
  config_home=$2
  profile_path=${3:-$(select_profile)}
  [ -n "$key" ] || {
    printf '%s\n' 'API Key cannot be empty.' >&2
    return 1
  }
  mkdir -p "$config_home" "$(dirname "$profile_path")" || return 1
  chmod 700 "$config_home" 2>/dev/null || true
  env_file="$config_home/env"
  env_temp=$(mktemp "${env_file}.XXXXXX") || return 1
  profile_temp=$(mktemp "${profile_path}.nexagw.XXXXXX") || {
    rm -f "$env_temp"
    return 1
  }
  umask 077
  printf 'export NEXAGW_API_KEY=%s\n' "$(shell_quote "$key")" > "$env_temp" || return 1
  chmod 600 "$env_temp" || return 1

  if [ -f "$profile_path" ]; then
    begin_count=$(grep -c '^# BEGIN NEXAGW MANAGED ENV$' "$profile_path" 2>/dev/null || true)
    end_count=$(grep -c '^# END NEXAGW MANAGED ENV$' "$profile_path" 2>/dev/null || true)
    if [ "$begin_count" -gt 1 ] || [ "$end_count" -gt 1 ] || [ "$begin_count" -ne "$end_count" ]; then
      printf '%s\n' 'The existing NexaGW profile block is malformed.' >&2
      rm -f "$env_temp" "$profile_temp"
      return 1
    fi
    awk '
      /^# BEGIN NEXAGW MANAGED ENV$/ { skip = 1; next }
      /^# END NEXAGW MANAGED ENV$/ { skip = 0; next }
      !skip { print }
    ' "$profile_path" > "$profile_temp" || return 1
  else
    : > "$profile_temp"
  fi
  if [ -s "$profile_temp" ]; then printf '\n' >> "$profile_temp"; fi
  {
    printf '%s\n' '# BEGIN NEXAGW MANAGED ENV'
    printf '%s\n' '[ -f "$HOME/.config/nexagw/env" ] && . "$HOME/.config/nexagw/env"'
    printf '%s\n' '# END NEXAGW MANAGED ENV'
  } >> "$profile_temp"

  mv "$profile_temp" "$profile_path" || return 1
  mv "$env_temp" "$env_file" || return 1
  chmod 600 "$env_file" || return 1
  export NEXAGW_API_KEY="$key"
}

test_config() {
  config_path=$1
  if [ ! -f "$config_path" ]; then
    printf '%s\n' 'CONFIG_EXISTS=no'
    printf 'KEY_CONFIGURED=%s\n' "${NEXAGW_API_KEY:+yes}"
    return 1
  fi
  provider_count=$(grep -c '^\[model_providers\.nexagw\]$' "$config_path" 2>/dev/null || true)
  base_url=$(sed -n 's/^base_url = "\(https:\/\/api\.nexagw[^\"]*\/v1\)"$/\1/p' "$config_path" | head -n 1)
  printf '%s\n' 'CONFIG_EXISTS=yes'
  printf 'PROVIDER_VALID=%s\n' "$(if [ "$provider_count" -eq 1 ]; then printf yes; else printf no; fi)"
  printf 'BASE_URL=%s\n' "$base_url"
  printf 'KEY_CONFIGURED=%s\n' "${NEXAGW_API_KEY:+yes}"
  [ "$provider_count" -eq 1 ] && [ -n "$base_url" ]
}

confirm_mutation() {
  printf '%s [y/N] ' "$1"
  IFS= read -r answer
  case "$answer" in y|Y|yes|YES|Yes) return 0 ;; *) return 1 ;; esac
}

main() {
  action=${1:-Menu}
  route=${2:-primary}
  [ "$#" -le 2 ] || {
    printf '%s\n' 'Usage: nexagw-codex-setup.sh [Menu|Configure|Status|Switch|Restore] [primary|hongkong]' >&2
    return 2
  }
  case "$action" in Menu|Configure|Status|Switch|Restore) ;; *) return 2 ;; esac
  resolve_route "$route" >/dev/null || return 2
  codex_home="$HOME/.codex"
  config_home="$HOME/.config/nexagw"
  config_path="$codex_home/config.toml"

  if [ "$action" = Menu ]; then
    printf '%s\n' 'NexaGW Codex Setup'
    printf '%s\n' '1. Configure  2. Status  3. Switch route  4. Restore  0. Exit'
    printf '%s' 'Choose an action: '
    IFS= read -r choice
    case "$choice" in 1) action=Configure ;; 2) action=Status ;; 3) action=Switch ;; 4) action=Restore ;; *) return 0 ;; esac
  fi

  if [ "$action" = Status ]; then
    test_config "$config_path"
    return $?
  fi
  confirm_mutation "Run $action now?" || {
    printf '%s\n' 'Cancelled. No changes were made.'
    return 0
  }

  if [ "$action" = Restore ]; then
    backup_root="$codex_home/nexagw-backups"
    backups=$(
      for candidate in "$backup_root"/*; do
        [ -d "$candidate" ] && printf '%s\n' "$candidate"
      done 2>/dev/null | LC_ALL=C sort -r
    )
    [ -n "$backups" ] || {
      printf 'No backups found: %s\n' "$backup_root" >&2
      return 1
    }
    index=0
    old_ifs=$IFS
    IFS='
'
    for backup in $backups; do
      index=$((index + 1))
      printf '[%s] %s\n' "$index" "$(basename "$backup")"
    done
    IFS=$old_ifs
    printf '%s' 'Choose a backup number: '
    IFS= read -r selected
    chosen=$(printf '%s\n' "$backups" | sed -n "${selected}p")
    [ -n "$chosen" ] || return 2
    restore_backup "$chosen" "$codex_home" "$config_home"
    return $?
  fi

  backup=$(new_backup "$codex_home" "$config_home") || return 1
  if [ "$action" = Configure ]; then
    printf '%s' 'Enter the NexaGW API Key (input is hidden): '
    IFS= read -r -s key
    printf '\n'
    persist_key "$key" "$config_home" || return 1
    key=
  fi
  update_codex_config "$config_path" "$route" || return 1
  printf 'Configured route: %s\n' "$(resolve_route "$route")"
  printf 'Backup: %s\n' "$backup"
  printf 'Rollback: run this tool again, choose Restore, then select %s\n' "$(basename "$backup")"
}

if [ "${NEXAGW_SETUP_LIBRARY:-0}" != 1 ]; then
  main "$@"
fi
