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

BIN_PATH="/usr/local/bin/gost"
CONF_DIR="/etc/gost"
RULES_FILE="${CONF_DIR}/rules.conf"
ENV_FILE="${CONF_DIR}/gost.env"
SERVICE_FILE="/etc/systemd/system/gost.service"
CRON_FILE="/etc/cron.d/gost-restart"
CERT_DIR="${CONF_DIR}/certs"

RED="\033[31m"
GRN="\033[32m"
YEL="\033[33m"
BLU="\033[36m"
RST="\033[0m"

require_root() {
  if [[ "${EUID}" -ne 0 ]]; then
    echo -e "${RED}请用 root 运行此脚本${RST}"
    exit 1
  fi
}

need_cmd() {
  command -v "$1" >/dev/null 2>&1 || {
    echo -e "${YEL}缺少依赖: $1，正在安装...${RST}"
    apt-get update -y >/dev/null 2>&1 || true
    apt-get install -y "$1" >/dev/null 2>&1 || {
      echo -e "${RED}安装依赖失败: $1${RST}"
      exit 1
    }
  }
}

prompt_read() {
  local __var="$1" __prompt="$2" __input=""
  if [[ -r /dev/tty ]]; then
    IFS= read -r -p "${__prompt}" __input < /dev/tty || return 1
  else
    IFS= read -r -p "${__prompt}" __input || return 1
  fi
  printf -v "${__var}" '%s' "${__input}"
}

prepare_env() {
  mkdir -p "${CONF_DIR}" "${CERT_DIR}"
  touch "${RULES_FILE}"
  chmod 600 "${RULES_FILE}"
  [[ -f "${ENV_FILE}" ]] || cat > "${ENV_FILE}" <<'ENV_EOF'
# Auto-generated by gost-manager.sh
GOST_ARGS=
ENV_EOF
  chmod 600 "${ENV_FILE}"
}

arch_map() {
  case "$(uname -m)" in
    x86_64) echo "amd64" ;;
    aarch64|arm64) echo "arm64" ;;
    armv7l|armv7*) echo "armv7" ;;
    armv6l|armv6*) echo "armv6" ;;
    i386|i686) echo "386" ;;
    riscv64) echo "riscv64" ;;
    mips64*) echo "mips64" ;;
    mipsel*) echo "mipsle" ;;
    mips*) echo "mips" ;;
    *) echo "unsupported" ;;
  esac
}

install_gost_latest() {
  need_cmd curl
  need_cmd tar
  need_cmd awk

  local os arch arch_alt api version download_url tmp release_json pkg_file
  os="linux"
  arch="$(arch_map)"
  [[ "${arch}" != "unsupported" ]] || { echo -e "${RED}不支持架构: $(uname -m)${RST}"; exit 1; }
  case "${arch}" in
    amd64) arch_alt="x86_64" ;;
    arm64) arch_alt="aarch64" ;;
    *) arch_alt="${arch}" ;;
  esac

  api="https://api.github.com/repos/go-gost/gost/releases/latest"
  release_json="$(curl -fsSL -H "Accept: application/vnd.github+json" -H "User-Agent: gost-manager" "${api}")"
  version="$(printf '%s' "${release_json}" | sed -n 's/.*"tag_name"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')"
  [[ -n "${version}" ]] || { echo -e "${RED}获取版本失败${RST}"; exit 1; }

  download_url="$(printf '%s' "${release_json}" \
    | grep -o '"browser_download_url"[[:space:]]*:[[:space:]]*"[^"]*"' \
    | sed 's/.*"browser_download_url"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/' \
    | awk -v re=".*${os}.*${arch}.*[.]tar[.]gz$" '$0 ~ re {print; exit}')"
  if [[ -z "${download_url}" ]]; then
    local clean_ver candidate base
    clean_ver="${version#v}"
    base="https://github.com/go-gost/gost/releases/download/${version}"
    for candidate in \
      "gost_${version}_${os}_${arch}.tar.gz" \
      "gost_${clean_ver}_${os}_${arch}.tar.gz" \
      "gost-${version}-${os}-${arch}.tar.gz" \
      "gost-${clean_ver}-${os}-${arch}.tar.gz" \
      "gost_${version}_${os}_${arch_alt}.tar.gz" \
      "gost_${clean_ver}_${os}_${arch_alt}.tar.gz" \
      "gost-${version}-${os}-${arch_alt}.tar.gz" \
      "gost-${clean_ver}-${os}-${arch_alt}.tar.gz"; do
      if curl -fsIL "${base}/${candidate}" >/dev/null 2>&1; then
        download_url="${base}/${candidate}"
        break
      fi
    done
  fi
  [[ -n "${download_url}" ]] || { echo -e "${RED}未找到 ${os}/${arch} 安装包（版本 ${version}）${RST}"; exit 1; }

  echo -e "${BLU}安装 gost ${version} ...${RST}"
  tmp="$(mktemp -d)"
  trap 'rm -rf "$tmp"' EXIT
  pkg_file="${tmp}/gost.tar.gz"
  curl -fL "${download_url}" -o "${pkg_file}" || { echo -e "${RED}下载失败: ${download_url}${RST}"; exit 1; }
  tar -xzf "${pkg_file}" -C "${tmp}" || { echo -e "${RED}解压失败，下载内容可能不是有效的 tar.gz: ${download_url}${RST}"; exit 1; }

  local found
  found="$(find "${tmp}" -type f -name gost | head -n1 || true)"
  [[ -n "${found}" ]] || { echo -e "${RED}未找到 gost 二进制${RST}"; exit 1; }
  install -m 0755 "${found}" "${BIN_PATH}"
  echo -e "${GRN}安装完成: $(${BIN_PATH} -V 2>/dev/null || ${BIN_PATH} -v 2>/dev/null || echo gost)${RST}"
}

create_service() {
  cat > "${SERVICE_FILE}" <<SERVICE_EOF
[Unit]
Description=GOST Tunnel Service
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
EnvironmentFile=${ENV_FILE}
ExecStart=${BIN_PATH} \$GOST_ARGS
Restart=always
RestartSec=2s
LimitNOFILE=1048576

[Install]
WantedBy=multi-user.target
SERVICE_EOF
  systemctl daemon-reload
  systemctl enable gost >/dev/null 2>&1 || true
}

render_args_from_rules() {
  local args=() line_no=0
  while IFS='|' read -r name l_arg f_arg extra; do
    line_no=$((line_no + 1))
    [[ -z "${name:-}" || "${name}" =~ ^# ]] && continue
    if [[ -z "${l_arg:-}" ]]; then
      echo -e "${YEL}忽略无效规则(第 ${line_no} 行): 缺少 -L 参数${RST}"
      continue
    fi
    [[ -n "${l_arg:-}" ]] && args+=("-L=${l_arg}")
    [[ -n "${f_arg:-}" ]] && args+=("-F=${f_arg}")
    if [[ -n "${extra:-}" ]]; then
      # shellcheck disable=SC2206
      local extra_parts=( ${extra} )
      args+=("${extra_parts[@]}")
    fi
  done < "${RULES_FILE}"

  local escaped=""
  local item
  for item in "${args[@]}"; do
    escaped+="$(printf '%q ' "${item}")"
  done

  cat > "${ENV_FILE}" <<ENV_EOF
# Auto-generated by gost-manager.sh
GOST_ARGS=${escaped}
ENV_EOF
  chmod 600 "${ENV_FILE}"
}

reload_service() {
  render_args_from_rules
  systemctl daemon-reload
  systemctl restart gost
}

list_rules() {
  echo -e "${BLU}当前规则：${RST}"
  [[ -s "${RULES_FILE}" ]] && nl -ba "${RULES_FILE}" | sed 's/^/  /' || echo "  (空)"
}

add_rule_line() {
  local name="$1" l_arg="$2" f_arg="$3" extra="$4"
  if [[ "${name}${l_arg}${f_arg}${extra}" == *"|"* ]]; then
    echo -e "${RED}规则内容不能包含字符 |${RST}"
    return 1
  fi
  if grep -q "^${name}|" "${RULES_FILE}" 2>/dev/null; then
    echo -e "${RED}规则名称已存在: ${name}${RST}"
    return 1
  fi
  echo "${name}|${l_arg}|${f_arg}|${extra}" >> "${RULES_FILE}"
  chmod 600 "${RULES_FILE}"
  echo -e "${GRN}已添加规则: ${name}${RST}"
}

add_rule_custom() {
  local name l_arg f_arg extra
  prompt_read name "规则名称(唯一): " || return
  prompt_read l_arg "请输入 -L 参数值: " || return
  prompt_read f_arg "请输入 -F 参数值(可空): " || return
  prompt_read extra "额外参数(可空，如 --log.level=info): " || return
  [[ -n "${name}" && -n "${l_arg}" ]] || { echo -e "${RED}名称和 -L 不可为空${RST}"; return; }
  add_rule_line "${name}" "${l_arg}" "${f_arg}" "${extra}"
}

add_rule_quick() {
  local mode name lport target user pass
  echo "1) TCP 转发"
  echo "2) UDP 转发"
  echo "3) relay+tls"
  echo "4) relay+ws"
  echo "5) relay+wss"
  echo "6) 本机 socks5 代理"
  echo "7) 本机 http 代理"
  prompt_read mode "选择 [1-7]: " || return
  prompt_read name "规则名称: " || return
  [[ -n "${name}" ]] || { echo -e "${RED}名称不能为空${RST}"; return; }

  case "${mode}" in
    1|2|3|4|5)
      prompt_read lport "监听端口: " || return
      prompt_read target "目标地址(如 1.2.3.4:20000): " || return
      ;;
    6|7)
      prompt_read lport "监听端口: " || return
      prompt_read user "用户名(可空): " || return
      prompt_read pass "密码(可空): " || return
      ;;
    *) echo -e "${RED}无效选择${RST}"; return ;;
  esac

  case "${mode}" in
    1) add_rule_line "${name}" "tcp://:${lport}/${target}" "" "" ;;
    2) add_rule_line "${name}" "udp://:${lport}/${target}" "" "" ;;
    3) add_rule_line "${name}" "relay+tls://:${lport}/${target}" "" "" ;;
    4) add_rule_line "${name}" "relay+ws://:${lport}/${target}" "" "" ;;
    5) add_rule_line "${name}" "relay+wss://:${lport}/${target}" "" "" ;;
    6)
      [[ -n "${user}" && -n "${pass}" ]] && add_rule_line "${name}" "socks5://${user}:${pass}@:${lport}" "" "" || add_rule_line "${name}" "socks5://:${lport}" "" ""
      ;;
    7)
      [[ -n "${user}" && -n "${pass}" ]] && add_rule_line "${name}" "http://${user}:${pass}@:${lport}" "" "" || add_rule_line "${name}" "http://:${lport}" "" ""
      ;;
  esac
}

add_lb_template() {
  echo -e "${BLU}负载均衡模板：中转 + 多落地（ws/wss/tls）${RST}"
  local prefix mode listen_port scheme backends cert_name l_arg first f_arg extra backend
  prompt_read prefix "规则前缀(如 transit1): " || return
  prompt_read listen_port "监听端口(中转机): " || return
  echo "中转协议: 1) ws  2) wss  3) tls"
  prompt_read mode "选择 [1-3]: " || return

  case "${mode}" in
    1) scheme="relay+ws" ;;
    2) scheme="relay+wss" ;;
    3) scheme="relay+tls" ;;
    *) echo -e "${RED}无效选择${RST}"; return ;;
  esac

  prompt_read backends "落地节点列表(逗号分隔，例: 1.1.1.1:443,2.2.2.2:443): " || return
  [[ -n "${prefix}" && -n "${listen_port}" && -n "${backends}" ]] || { echo -e "${RED}参数不能为空${RST}"; return; }

  if [[ "${scheme}" == "relay+wss" || "${scheme}" == "relay+tls" ]]; then
    prompt_read cert_name "证书目录名(将使用 /etc/gost/certs/<name>/fullchain.cer 和 <name>.key): " || return
    [[ -n "${cert_name}" ]] || { echo -e "${RED}证书目录名不能为空${RST}"; return; }
    l_arg="${scheme}://:${listen_port}?certFile=${CERT_DIR}/${cert_name}/fullchain.cer&keyFile=${CERT_DIR}/${cert_name}/${cert_name}.key"
  else
    l_arg="${scheme}://:${listen_port}"
  fi

  first="${backends%%,*}"
  first="${first// /}"
  [[ -n "${first}" ]] || { echo -e "${RED}至少需要一个落地节点${RST}"; return; }

  f_arg="${scheme}://${first}"
  extra=""
  if [[ "${backends}" == *,* ]]; then
    local rest old_ifs
    rest="${backends#*,}"
    old_ifs="${IFS}"
    IFS=','
    for backend in ${rest}; do
      backend="${backend// /}"
      [[ -z "${backend}" ]] && continue
      extra+=" -F=${scheme}://${backend}"
    done
    IFS="${old_ifs}"
  fi

  add_rule_line "${prefix}-lb" "${l_arg}" "${f_arg}" "${extra}"
  echo -e "${YEL}提示: 多个 -F 将交给 gost 进行链路选择/容错（具体策略取决于 gost 版本与配置）${RST}"
}

delete_rule() {
  list_rules
  local name
  prompt_read name "输入要删除的规则名称: " || return
  [[ -n "${name}" ]] || return
  if grep -q "^${name}|" "${RULES_FILE}"; then
    grep -v "^${name}|" "${RULES_FILE}" > "${RULES_FILE}.tmp"
    mv "${RULES_FILE}.tmp" "${RULES_FILE}"
    chmod 600 "${RULES_FILE}"
    echo -e "${GRN}已删除: ${name}${RST}"
  else
    echo -e "${YEL}未找到规则: ${name}${RST}"
  fi
}

install_acme() {
  need_cmd curl
  command -v sh >/dev/null 2>&1 || { echo -e "${RED}缺少依赖: sh${RST}"; return 1; }
  if [[ ! -x "$HOME/.acme.sh/acme.sh" ]]; then
    curl -fsSL https://get.acme.sh | sh -s email=admin@example.com
  fi
  "$HOME/.acme.sh/acme.sh" --upgrade --auto-upgrade
  echo -e "${GRN}acme.sh 已安装/更新: $HOME/.acme.sh/acme.sh${RST}"
}

issue_cert_standalone() {
  local domain email cert_home acme_log
  need_cmd socat
  install_acme

  prompt_read domain "输入域名(需已解析到本机): " || return
  prompt_read email "输入邮箱(用于 ACME 注册): " || return
  [[ -n "${domain}" && -n "${email}" ]] || { echo -e "${RED}域名和邮箱不能为空${RST}"; return; }
  validate_domain "${domain}" || return

  cert_home="${CERT_DIR}/${domain}"
  mkdir -p "${cert_home}"
  acme_log="$(mktemp)"

  "$HOME/.acme.sh/acme.sh" --register-account -m "${email}" >/dev/null 2>&1 || true
  if ! "$HOME/.acme.sh/acme.sh" --issue --standalone -d "${domain}" --keylength ec-256 >"${acme_log}" 2>&1; then
    echo -e "${RED}证书申请失败，请检查日志: ${acme_log}${RST}"
    sed 's/^/  /' "${acme_log}" >&2 || true
    return 1
  fi
  "$HOME/.acme.sh/acme.sh" --install-cert -d "${domain}" --ecc \
    --fullchain-file "${cert_home}/fullchain.cer" \
    --key-file "${cert_home}/${domain}.key"

  echo -e "${GRN}证书已签发并安装到: ${cert_home}${RST}"
  echo -e "${YEL}可用于 wss/tls 模板，证书目录名填写: ${domain}${RST}"
}

validate_domain() {
  local domain="$1"
  if [[ ! "${domain}" =~ ^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$ ]]; then
    echo -e "${RED}无效的域名格式: ${domain}${RST}"
    return 1
  fi
}

import_cert_manual() {
  local name fullchain key cert_home
  prompt_read name "证书目录名(自定义，如 mydomain.com): " || return
  prompt_read fullchain "fullchain 路径: " || return
  prompt_read key "private key 路径: " || return
  [[ -n "${name}" && -f "${fullchain}" && -f "${key}" ]] || { echo -e "${RED}输入无效，文件不存在${RST}"; return; }

  cert_home="${CERT_DIR}/${name}"
  mkdir -p "${cert_home}"
  cp -f "${fullchain}" "${cert_home}/fullchain.cer"
  cp -f "${key}" "${cert_home}/${name}.key"
  chmod 600 "${cert_home}/${name}.key"
  echo -e "${GRN}证书已导入: ${cert_home}${RST}"
}

list_certs() {
  echo -e "${BLU}证书目录: ${CERT_DIR}${RST}"
  if [[ -d "${CERT_DIR}" ]]; then
    find "${CERT_DIR}" -maxdepth 2 -type f | sed 's/^/  /' || true
  fi
}

service_status() {
  systemctl status gost --no-pager || true
}

enable_cron_restart() {
  local spec
  echo "示例: 每天4点重启 -> 0 4 * * *"
  prompt_read spec "请输入 crontab 时间表达式: " || return
  [[ -n "${spec}" ]] || { echo -e "${RED}不能为空${RST}"; return; }
  cat > "${CRON_FILE}" <<CRON_EOF
${spec} root systemctl restart gost >/dev/null 2>&1
CRON_EOF
  echo -e "${GRN}已设置定时重启: ${spec}${RST}"
}

disable_cron_restart() {
  rm -f "${CRON_FILE}"
  echo -e "${GRN}已取消定时重启${RST}"
}

uninstall_all() {
  local ans
  prompt_read ans "确认卸载 gost 与配置？[y/N]: " || return
  [[ "${ans}" =~ ^[Yy]$ ]] || return
  systemctl stop gost >/dev/null 2>&1 || true
  systemctl disable gost >/dev/null 2>&1 || true
  rm -f "${SERVICE_FILE}" "${BIN_PATH}" "${CRON_FILE}"
  rm -rf "${CONF_DIR}"
  systemctl daemon-reload
  echo -e "${GRN}卸载完成${RST}"
}

show_menu() {
  echo
  echo -e "${BLU}==== GOST Manager v2 ====${RST}"
  echo "1) 安装/升级 gost"
  echo "2) 初始化 systemd 服务"
  echo "3) 查看规则"
  echo "4) 添加规则(快速模板)"
  echo "5) 添加规则(自定义)"
  echo "6) 添加规则(负载均衡模板: 中转+多落地 ws/wss/tls)"
  echo "7) 删除规则"
  echo "8) 重载规则并重启服务"
  echo "9) 启动服务"
  echo "10) 停止服务"
  echo "11) 重启服务"
  echo "12) 查看服务状态"
  echo "13) 安装/更新 acme.sh"
  echo "14) 自动申请证书(standalone)"
  echo "15) 手动导入证书"
  echo "16) 查看证书"
  echo "17) 启用定时重启"
  echo "18) 关闭定时重启"
  echo "19) 卸载"
  echo "0) 退出"
}

main() {
  require_root
  prepare_env

  while true; do
    show_menu
    if ! prompt_read c "请选择 [0-19]: "; then
      echo -e "${YEL}检测到非交互输入，请使用: bash <(curl -fsSL URL) 或先下载后执行脚本。${RST}"
      exit 1
    fi
    case "${c}" in
      1) install_gost_latest ;;
      2) create_service; echo -e "${GRN}服务初始化完成${RST}" ;;
      3) list_rules ;;
      4) add_rule_quick ;;
      5) add_rule_custom ;;
      6) add_lb_template ;;
      7) delete_rule ;;
      8) create_service; reload_service; echo -e "${GRN}已应用规则${RST}" ;;
      9) systemctl start gost ;;
      10) systemctl stop gost ;;
      11) systemctl restart gost ;;
      12) service_status ;;
      13) install_acme ;;
      14) issue_cert_standalone ;;
      15) import_cert_manual ;;
      16) list_certs ;;
      17) enable_cron_restart ;;
      18) disable_cron_restart ;;
      19) uninstall_all ;;
      0) exit 0 ;;
      *) echo -e "${YEL}无效输入${RST}" ;;
    esac
  done
}

main "$@"
