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

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
GO_MOD_FILE="${ROOT_DIR}/go.mod"
DEFAULT_GO_VERSION="$(awk '/^go / { print $2; exit }' "${GO_MOD_FILE}")"

GO_VERSION="${DEFAULT_GO_VERSION}"
OUTPUT_PATH="${ROOT_DIR}/api"
TOOLS_DIR="${ROOT_DIR}/.build-tools"
CACHE_DIR="${ROOT_DIR}/.build-cache"
CLEAN_GO="false"
INSTALL_SERVICE="false"
SERVICE_NAME="singbox-gopanel"
RUN_USER="${SUDO_USER:-${USER:-root}}"

usage() {
  cat <<EOF
Usage: scripts/build_install.sh [options]

Options:
  --go-version <version>  Go version to download, default: ${GO_VERSION}
  --output <path>         Build output path, default: ${OUTPUT_PATH}
  --tools-dir <dir>       Toolchain directory, default: ${TOOLS_DIR}
  --cache-dir <dir>       Go cache directory, default: ${CACHE_DIR}
  --clean-go              Re-download the Go toolchain even if it exists
  --install-service       Install and start the systemd service
  --service-name <name>  Systemd service name, default: ${SERVICE_NAME}
  --run-user <user>      Runtime user, default: ${RUN_USER}
  -h, --help              Show this help

Examples:
  bash scripts/build_install.sh
  bash scripts/build_install.sh --output ./package/api
  sudo bash scripts/build_install.sh --install-service
EOF
}

parse_args() {
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --go-version)
        GO_VERSION="$2"
        shift 2
        ;;
      --output)
        OUTPUT_PATH="$2"
        shift 2
        ;;
      --tools-dir)
        TOOLS_DIR="$2"
        shift 2
        ;;
      --cache-dir)
        CACHE_DIR="$2"
        shift 2
        ;;
      --clean-go)
        CLEAN_GO="true"
        shift
        ;;
      --install-service)
        INSTALL_SERVICE="true"
        shift
        ;;
      --service-name)
        SERVICE_NAME="$2"
        shift 2
        ;;
      --run-user)
        RUN_USER="$2"
        shift 2
        ;;
      -h|--help)
        usage
        exit 0
        ;;
      *)
        echo "error: unknown option: $1" >&2
        usage
        exit 1
        ;;
    esac
  done
}

require_cmd() {
  if ! command -v "$1" >/dev/null 2>&1; then
    echo "error: required command not found: $1" >&2
    exit 1
  fi
}

detect_platform() {
  local uname_os uname_arch
  uname_os="$(uname -s)"
  uname_arch="$(uname -m)"

  case "${uname_os}" in
    Linux) GO_OS="linux" ;;
    Darwin) GO_OS="darwin" ;;
    *)
      echo "error: unsupported operating system: ${uname_os}" >&2
      exit 1
      ;;
  esac

  case "${uname_arch}" in
    x86_64|amd64) GO_ARCH="amd64" ;;
    aarch64|arm64) GO_ARCH="arm64" ;;
    *)
      echo "error: unsupported architecture: ${uname_arch}" >&2
      exit 1
      ;;
  esac
}

download_go() {
  local archive_name archive_path download_url current_version

  mkdir -p "${TOOLS_DIR}"
  archive_name="go${GO_VERSION}.${GO_OS}-${GO_ARCH}.tar.gz"
  archive_path="${TOOLS_DIR}/${archive_name}"
  GO_ROOT="${TOOLS_DIR}/go"

  if [[ -x "${GO_ROOT}/bin/go" ]]; then
    current_version="$("${GO_ROOT}/bin/go" version | awk '{print $3}' | sed 's/^go//')"
    if [[ "${current_version}" != "${GO_VERSION}" ]]; then
      rm -rf "${GO_ROOT}"
    fi
  fi

  if [[ "${CLEAN_GO}" == "true" ]]; then
    rm -rf "${GO_ROOT}"
    rm -f "${archive_path}"
  fi

  if [[ ! -x "${GO_ROOT}/bin/go" ]]; then
    download_url="https://go.dev/dl/${archive_name}"
    echo "downloading Go ${GO_VERSION} for ${GO_OS}/${GO_ARCH}..."
    if [[ ! -f "${archive_path}" ]]; then
      if command -v curl >/dev/null 2>&1; then
        curl -fsSL "${download_url}" -o "${archive_path}"
      elif command -v wget >/dev/null 2>&1; then
        wget -O "${archive_path}" "${download_url}"
      else
        echo "error: curl or wget is required to download Go" >&2
        exit 1
      fi
    fi

    rm -rf "${GO_ROOT}"
    tar -C "${TOOLS_DIR}" -xzf "${archive_path}"
  fi
}

build_binary() {
  local output_dir

  mkdir -p "${CACHE_DIR}/gocache" "${CACHE_DIR}/gomodcache"
  output_dir="$(dirname "${OUTPUT_PATH}")"
  mkdir -p "${output_dir}"

  echo "building ${OUTPUT_PATH}..."
  (
    cd "${ROOT_DIR}"
    export GOROOT="${GO_ROOT}"
    export PATH="${GO_ROOT}/bin:${PATH}"
    export GOCACHE="${CACHE_DIR}/gocache"
    export GOMODCACHE="${CACHE_DIR}/gomodcache"
    "${GO_ROOT}/bin/go" build -trimpath -o "${OUTPUT_PATH}" ./cmd/api
  )
  chmod +x "${OUTPUT_PATH}" || true
}

install_systemd_service() {
  if [[ "${INSTALL_SERVICE}" != "true" ]]; then
    return
  fi

  if [[ "${EUID}" -ne 0 ]]; then
    echo "error: systemd service installation requires root/sudo" >&2
    exit 1
  fi

  if ! command -v systemctl >/dev/null 2>&1; then
    echo "systemctl not found, skipping service installation"
    return
  fi

  echo "installing systemd service: ${SERVICE_NAME}..."

  cat >"/etc/systemd/system/${SERVICE_NAME}.service" <<EOF
[Unit]
Description=SingBox GoPanel API
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=${RUN_USER}
WorkingDirectory=${ROOT_DIR}
EnvironmentFile=-${ROOT_DIR}/.env
ExecStart=${OUTPUT_PATH}
Restart=always
RestartSec=5
LimitNOFILE=1048576

[Install]
WantedBy=multi-user.target
EOF

  systemctl daemon-reload
  systemctl enable "${SERVICE_NAME}"
  systemctl restart "${SERVICE_NAME}"
  echo "service ${SERVICE_NAME} installed and started."
}

print_summary() {
  cat <<EOF

Build complete.

Go version:   ${GO_VERSION}
Toolchain:    ${GO_ROOT}
Output:       ${OUTPUT_PATH}
Cache dir:    ${CACHE_DIR}

To use this Go environment in your current shell:
  export PATH="${GO_ROOT}/bin:\$PATH"
  export GOROOT="${GO_ROOT}"
EOF
}

main() {
  parse_args "$@"
  require_cmd tar
  detect_platform
  download_go
  build_binary
  install_systemd_service
  print_summary
}

main "$@"
