#!/bin/bash
set -e
shopt -s inherit_errexit 2>/dev/null || true

RED_COLOR="\e[1;31m"
PURPLE_COLOR="\e[1;35m"
GREEN_COLOR="\e[1;32m"
CLEAR_COLOR="\e[0m"

_DEF_PARALLEL_JOBS=4
_ENV_PARALLEL_JOBS="${PARALLEL_JOBS}"
source /etc/leaf.conf
if [ -n "${_ENV_PARALLEL_JOBS}" ]; then
  export PARALLEL_JOBS="${_ENV_PARALLEL_JOBS}"
elif [ -z "${PARALLEL_JOBS}" ]; then
  export PARALLEL_JOBS="${_DEF_PARALLEL_JOBS}"
else
  export PARALLEL_JOBS
fi
export MAKEFLAGS="-j${PARALLEL_JOBS}"
export NINJAJOBS="${PARALLEL_JOBS}"
export TESTSUITEFLAGS="-j${PARALLEL_JOBS}"
export LANG=C
export LC_CTYPE=C.UTF-8
export leaf_flags="CFLAGS CXXFLAGS FCFLAGS FFLAGS RUSTFLAGS"
for flag in ${leaf_flags}; do
  export $flag
done

declare -A BUILD_OPTIONS
BUILD_OPTIONS=([strip]="0" [libtool]="0" [zipman]="0")

# global message file list
declare -a LEAF_MESSAGE_FILES=()

main() {
  leaf_check_directories
  if [ $# -le 1 ]; then
    case $1 in
      clean)
        leaf_check_permission
        rm -rf {"${BUILD_DIR}","${TEMP_DIR}"}/*
        ;;
      list)
        cat "${INSTALLED_PACKAGES}"
        ;;
      *)
        leaf_print_usage
        ;;
    esac
  else
    local _item
    case $1 in
      prepare|build|install|force-install|remove|pack|dirct-install)
        leaf_check_permission
        for _item in "${@:2}"; do
          if [[ x"$1" == x"remove" ]]; then
            leaf_find_remove_pkgbuild "${_item}"
          else
            leaf_find_pkgbuild "${_item}"
          fi
          leaf_reset_state
          leaf_message_init
          if [[ x"$1" == x"remove" ]]; then
            source "${TRACE_DIR}/${PKG_PREFIX}/${PKG_NAME}/PKGBUILD" || leaf_record_message "cannot find PKGBUILD, ingore."
          else
            source "${PKGBUILD_DIR}/${PKG_PREFIX}/${PKG_NAME}".PKGBUILD
          fi
          leaf_parse_options
          srcdir="${BUILD_DIR}/${PKG_PREFIX}/${PKG_NAME}"
          TMPDIR="${srcdir}/.leaf-tmp"
          HOME="${srcdir}/.leaf-home"
          export TMPDIR
          export HOME
          pkgdir="${srcdir}"/__pkgdir__
          if [[ x"$1" != x"dirct-install" ]]; then
            rm -rf "${srcdir}" && install -dm755 "${srcdir}" && cd "${srcdir}"
          fi
          leaf_${1}_package
        done
        ;;
      upgrade)
        leaf_check_permission
        [ $# -eq 3 ] || leaf_error "Usage: leaf upgrade <oldpkg> <newpkg>"
        leaf_upgrade_package "$2" "$3"
        ;;
      search)
        leaf_search_package "${@:2}"
        ;;
      show)
        for _item in "${@:2}"; do
          leaf_find_pkgbuild "${_item}"
          leaf_show_package
        done
        ;;
      unpack)
        for _item in "${@:2}"; do
          leaf_check_permission
          leaf_reset_state
          leaf_unpack_package "${_item}"
        done
        ;;
      *)
        leaf_print_usage
        ;;
    esac
    leaf_print_message
  fi
}

###########################################################
# Help functions that give PKGBUILD to use
###########################################################

leaf_clear_flags() {
  for flag in $leaf_flags; do
    unset $flag
  done
}

leaf_install_hook() {
  local _hook_file="$1"
  local _hook_dest="${pkgdir}${HOOK_DIR}"

  # check file name
  [[ -f "$_hook_file" ]] || leaf_error "Hook file '$_hook_file' does not exist"
  [[ -r "$_hook_file" ]] || leaf_error "Hook file '$_hook_file' is not readable"
  [[ "$(basename "$_hook_file")" == *.HOOK ]] || leaf_error "Hook file must end with .HOOK extension"

  # check hook file by load it in a sub shell
  (
    unset triggers target operation 2>/dev/null
    declare -a triggers
    declare -a target
    source "$_hook_file" || leaf_error "Source ${_hook_file} failed."
    [[ ${#triggers[@]} -gt 0 ]] || leaf_error "${_hook_file} missing 'triggers' array"
    [[ ${#target[@]} -gt 0 ]]    || leaf_error "${_hook_file} missing 'target' array"
    declare -f operation &>/dev/null || leaf_error "${_hook_file} missing 'operation' function"
  ) || leaf_error "Hook file ${_hook_file} is not valid. Please check your package ${PKG_NAME}."

  # install hook file
  install -Dm644 -- "$_hook_file" "${_hook_dest}/$(basename "$_hook_file")" || {
    leaf_error "Failed to install hook file to ${_hook_dest}"
  }

  echo -e "Installed hook: $(basename "$_hook_file")"
}

leaf_check_directories() {
  if [ -z "${BUILD_DIR}" ]; then
    leaf_error "Directory \${BUILD_DIR} must be existed"
  elif [ -z "${TEMP_DIR}" ]; then
    leaf_error "Directory \${TEMP_DIR} must be existed"
  fi
}

leaf_check_permission() {
  [ ${EUID} -eq 0 ] || leaf_error "Permission denied"
}

leaf_compress_man_pages() {
  pushd "${pkgdir}" > /dev/null 2>&1
  [ ! -d usr/share/man ] || {
    local _item
    find usr/share/man -type f,l | while read -r _item; do
      case $(file -bi "${_item}") in
        *text*)
          /usr/bin/gzip -c -9 "${_item}" > "${_item}".gz
          rm -fv "${_item}"
          ;;
        *symlink*)
          ln -srv "$(readlink -m "${_item}")".gz "$(realpath "${_item}".gz)"
          rm -fv "${_item}"
          ;;
        *)
          ;;
      esac
    done
  }
  popd > /dev/null 2>&1
}

leaf_error() {
  leaf_print_message
  echo -e "${RED_COLOR}* $1${CLEAR_COLOR}" && exit 1
}

leaf_find_pkgbuild() {
  local _location="$(find "${PKGBUILD_DIR}" -type f -wholename "*$1*.PKGBUILD" -printf "%P\n")"
  if [ -z "${_location}" ]; then
    leaf_error "Package $1's PKGBUILD does NOT exist"
  elif [[ $(echo "${_location}" | wc -l) == 1 ]]; then
    PKG_PREFIX="$(echo "${_location}" | awk -F'/' '{print $1}')"
    PKG_NAME="$(echo "${_location%.PKGBUILD}" | awk -F'/' '{print $2}')"
  else
    leaf_error "Ambiguous packages, the prefix or version of package must be specified"
  fi
}

leaf_find_remove_pkgbuild() {
  local _location="$(find "${TRACE_DIR}" -type d -wholename "*$1*" -printf "%P\n")"
  if [ -z "${_location}" ]; then
    leaf_error "Package $1 does NOT exist"
  elif [[ $(echo "${_location}" | wc -l) == 1 ]]; then
    PKG_PREFIX="$(echo "${_location}" | awk -F'/' '{print $1}')"
    PKG_NAME="$(echo "${_location%.PKGBUILD}" | awk -F'/' '{print $2}')"
  else
    leaf_error "Ambiguous packages, the prefix or version of package must be specified"
  fi
}

leaf_invoke_hooks() {
  local _hook _target _hook_dir
  local _trace_dir="${TRACE_DIR}/${PKG_PREFIX}/${PKG_NAME}"
  case "$1" in
    install|remove)
      ;;
    *)
      leaf_error "leaf_invoke_hooks: $1 is invalid!"
      ;;
  esac
  if [ ! -d "${HOOK_DIR}" ]; then
    leaf_error "Hook dir ${HOOK_DIR} does not exist. Check your leaf installation."
  fi
  find "${HOOK_DIR}" -type f -name "*.HOOK" | while read -r _hook; do
    unset triggers target operation
    declare -a triggers
    declare -a target
    source "${_hook}" || leaf_error "Failed to source hook file: ${_hook}"
    [[ " ${triggers[*]} " =~ " $1 " ]] || continue
    for _target in "${target[@]}"; do
      if grep -qe "${_target}" "${_trace_dir}"/FILES 2>/dev/null; then
        echo -e "${GREEN_COLOR}*** Apply the hook: ${_hook}...${CLEAR_COLOR}"
        operation
        break
      fi
    done
  done
}

leaf_merge_package() {
  pushd $1 > /dev/null 2>&1
  local _trace_dir="${TRACE_DIR}/${PKG_PREFIX}/${PKG_NAME}"
  local _item _item_conflict _mode _owner _group
  local _time="$(date +%Y%m%d%H%M%S)"
  cat "${_trace_dir}"/DIRS | while read -r _item; do
    _mode=$(stat -c %a ."${_item}")
    _owner=$(stat -c %u ."${_item}")
    _group=$(stat -c %g ."${_item}")
    install -d -m ${_mode} -o ${_owner} -g ${_group} "${_item}"
  done

    cat "${_trace_dir}"/FILES | while read -r _item; do
    _mode=$(stat -c %a ."${_item}")
    _owner=$(stat -c %u ."${_item}")
    _group=$(stat -c %g ."${_item}")

    # Fast path: non-/etc, or /etc path that doesn't exist yet -> install/overwrite directly
    if [[ "${_item}" != /etc/* || ! -e "${_item}" ]]; then
      install -D -m ${_mode} -o ${_owner} -g ${_group} ."${_item}" "${_item}"
      continue
    fi

    # Now: /etc/* and already exists on the system.
    # Upgrade optimization: if OLD_TRACE_DIR is set and the current /etc file matches old baseline,
    # then user didn't modify it -> overwrite with new version directly.
    if [[ -n "${OLD_TRACE_DIR:-}" ]] \
       && leaf_conffile_is_unmodified "${_item}" "${OLD_TRACE_DIR}"; then
      install -D -m ${_mode} -o ${_owner} -g ${_group} ."${_item}" "${_item}"
      continue
    fi

    # Otherwise, fall back to conflict logic:
    # - if content is identical -> overwrite (keeps permissions/ownership from pkg)
    # - else -> write conflict file and keep user's current file
    _item_conflict="$(dirname "${_item}")/._$(basename "${_item}").conflict_${PKG_NAME}_${_time}"
    if diff -q ."${_item}" "${_item}" >/dev/null 2>&1; then
      install -D -m ${_mode} -o ${_owner} -g ${_group} ."${_item}" "${_item}"
    else
      install -D -m ${_mode} -o ${_owner} -g ${_group} ."${_item}" "${_item_conflict}"
      leaf_record_message "Config file confliction on ${_item}, the package provided version is installed as ${_item_conflict}."
    fi
  done

  cat "${_trace_dir}"/LINKS | while read -r _item; do
    cp -dp ."${_item}" "${_item}"
  done
  leaf_invoke_hooks install
  popd > /dev/null 2>&1
}

leaf_parse_options() {
  local _option_array=(${DEFAULT_BUILD_OPTIONS[@]} ${options[@]})
  for option in ${_option_array[@]}; do
    if [[ x"${option}" == x"strip" ]]; then
      BUILD_OPTIONS["strip"]="1"
    fi
    if [[ x"${option}" == x"!strip" ]]; then
      BUILD_OPTIONS["strip"]="0"
    fi
    if [[ x"${option}" == x"libtool" ]]; then
      BUILD_OPTIONS["libtool"]="1"
    fi
    if [[ x"${option}" == x"!libtool" ]]; then
      BUILD_OPTIONS["libtool"]="0"
    fi
    if [[ x"${option}" == x"zipman" ]]; then
      BUILD_OPTIONS["zipman"]="1"
    fi
    if [[ x"${option}" == x"!zipman" ]]; then
      BUILD_OPTIONS["zipman"]="0"
    fi
  done
}

leaf_print_usage() {
  cat << EOF
Usage: leaf [option] [packages]
Option:     prepare        prepare packages but do not build
            build          build packages but do not install
            install        install packages
            forece-install install packages even if test failed
            dirct-install  install packages directly from previous build
            remove         remove packages
            upgrade        upgrade oldpkg to newpkg (leaf upgrade old new)
            clean          clean all source folders
            list           list installed packages
            search         search packages by keyword
            show           show package details
            pack           build packages and pack
            unpack         unpack binary packages
                               This leaf does not have Super Cow Powers.
EOF
}

leaf_message_init() {
  # one log per package invocation
  local d="${TEMP_DIR}/leaf-messages/${PKG_PREFIX}/${PKG_NAME}"
  install -dm755 -- "$d"
  LEAF_MESSAGE_FILE="${d}/messages.$$.$(date +%Y%m%d%H%M%S).log"
  : > "${LEAF_MESSAGE_FILE}"
  export LEAF_MESSAGE_FILE

  LEAF_MESSAGE_FILES+=("${LEAF_MESSAGE_FILE}")
}

leaf_record_message() {
  local out
  local _item
  for _item in "${@:1}"; do
    out+="${GREEN_COLOR}* ${_item}${CLEAR_COLOR}\n"
  done

  if [ -n "${LEAF_MESSAGE_FILE}" ]; then
    # printf %b will explain \n 和 \e
    printf '%b' "${out}" >> "${LEAF_MESSAGE_FILE}"
  fi
}

leaf_print_message() {
  local f prefix name

  for f in "${LEAF_MESSAGE_FILES[@]}"; do
    [ -n "$f" ] || continue
    [ -s "$f" ] || continue

    # ${TEMP_DIR}/leaf-messages/<prefix>/<name>/messages....
    prefix="${f#${TEMP_DIR}/leaf-messages/}"
    prefix="${prefix%%/*}"
    name="${f#${TEMP_DIR}/leaf-messages/${prefix}/}"
    name="${name%%/*}"

    echo -e "\n${PURPLE_COLOR}* Message from ${prefix}/${name}${CLEAR_COLOR}\n"
    cat -- "$f"
    rm -f -- "$f"
  done

  LEAF_MESSAGE_FILES=()
  unset LEAF_MESSAGE_FILE
}


leaf_remove_libtool_files() {
  find "${pkgdir}" -name "*.la" -delete
}

leaf_noop() { :; }

leaf_reset_state() {
  options=()
  src_preinstall() { leaf_noop; }
  src_postinstall() { leaf_noop; }
  src_preremove() { leaf_noop; }
  src_postremove() { leaf_noop; }

  src_prepare() { leaf_noop; }
  src_build() { leaf_noop; }
  src_check() { leaf_noop; }
  src_install() { leaf_noop; }
}

leaf_is_noop_func() {
  # usage: leaf_is_noop_func <funcname>
  local fn="$1" body

  declare -F "$fn" >/dev/null || return 0

  body="$(declare -f "$fn" \
    | sed -n '0,/{/d; :a; /}/q; p; n; ba')"

  body="$(printf '%s' "$body" | tr -d '[:space:]' | tr -d ';')"

  # allowd no-op form: leaf_noop, :, true
  case "$body" in
    leaf_noop|:|true) return 0 ;;
  esac

  return 1
}

leaf_strip_files() {
  pushd "${pkgdir}" > /dev/null 2>&1
  local _file
  find . -type f | while read -r _file; do
    case $(file -bi "${_file}") in
      *application/x-sharedlib*)       # libraries(.so)
        strip --strip-unneeded "${_file}"
        ;;
      *application/x-pie-executable*)  # libraries(.so)
        strip --strip-unneeded "${_file}"
        ;;
      *application/x-archive*)         # libraries(.a)
        strip --strip-debug "${_file}"
        ;;
      *application/x-object*)
        case "${_file}" in
          *.ko)                        # kernel module
            strip --strip-unneeded "${_file}"
            ;;
          *)
            ;;
        esac
        ;;
      *application/x-executable*)      # binaries
        strip --strip-all "${_file}"
        ;;
      *)
        ;;
    esac
  done
  popd > /dev/null 2>&1
}

leaf_trace_package() {
  pushd "$1" > /dev/null 2>&1
  local _trace_dir="${TRACE_DIR}/${PKG_PREFIX}/${PKG_NAME}"
  install -dm755 "${_trace_dir}"
  find . -type d | sort > "${_trace_dir}"/DIRS
  sed -i "1d" "$_trace_dir"/DIRS
  find . -type f | sort > "${_trace_dir}"/FILES
  find . -type l | sort > "${_trace_dir}"/LINKS
  sed -i "s/.//" "${_trace_dir}"/{DIRS,FILES,LINKS}
  install -vm644 "${PKGBUILD_DIR}/${PKG_PREFIX}/${PKG_NAME}.PKGBUILD" "${_trace_dir}/PKGBUILD"
  leaf_trace_conffiles "${_trace_dir}"
  popd > /dev/null 2>&1
}

leaf_trace_conffiles() {
  # usage: leaf_trace_conffiles <trace_dir>
  # called with CWD = pkgdir (same as leaf_trace_package)
  local _trace_dir="$1"
  local _out="${_trace_dir}/CONFFILES"
  local _p _type _val _mode _uid _gid

  [ -d ./etc ] || return 0

  : > "${_out}"

  # regular files
  while IFS= read -r -d '' _p; do
    _mode=$(stat -c %a -- "$_p")
    _uid=$(stat -c %u -- "$_p")
    _gid=$(stat -c %g -- "$_p")
    _val=$(sha256sum -- "$_p" | awk '{print $1}')
    printf '%s\tf\t%s\t%s\t%s\t%s\n' "/${_p#./}" "${_val}" "${_mode}" "${_uid}" "${_gid}" >> "${_out}"
  done < <(find ./etc -type f -print0)

  # symlinks
  while IFS= read -r -d '' _p; do
    _mode=$(stat -c %a -- "$_p")
    _uid=$(stat -c %u -- "$_p")
    _gid=$(stat -c %g -- "$_p")
    _val=$(readlink -- "$_p")
    printf '%s\tl\t%s\t%s\t%s\t%s\n' "/${_p#./}" "${_val}" "${_mode}" "${_uid}" "${_gid}" >> "${_out}"
  done < <(find ./etc -type l -print0)

  sort -o "${_out}" "${_out}" 2>/dev/null || true
}

leaf_conffile_is_unmodified() {
  # usage: leaf_conffile_is_unmodified <abs_path> <trace_dir>
  # return 0 if unmodified (safe to remove), 1 otherwise
  local path="$1" tdir="$2"
  local cf="${tdir}/CONFFILES"
  local line type val mode uid gid
  local cur_val cur_mode cur_uid cur_gid

  [ -r "${cf}" ] || return 1
  [ -e "${path}" ] || return 1

  line="$(grep -F -m1 -- "${path}"$'\t' "${cf}" 2>/dev/null)" || return 1

  IFS=$'\t' read -r _p type val mode uid gid <<< "${line}"

  cur_mode="$(stat -c %a -- "${path}" 2>/dev/null)" || return 1
  cur_uid="$(stat -c %u -- "${path}" 2>/dev/null)" || return 1
  cur_gid="$(stat -c %g -- "${path}" 2>/dev/null)" || return 1

  # metadata changed -> treat as modified
  if [ "${cur_mode}" != "${mode}" ] || [ "${cur_uid}" != "${uid}" ] || [ "${cur_gid}" != "${gid}" ]; then
    return 1
  fi

  case "${type}" in
    f)
      cur_val="$(sha256sum -- "${path}" 2>/dev/null | awk '{print $1}')" || return 1
      [ "${cur_val}" = "${val}" ] && return 0 || return 1
      ;;
    l)
      cur_val="$(readlink -- "${path}" 2>/dev/null)" || return 1
      [ "${cur_val}" = "${val}" ] && return 0 || return 1
      ;;
    *)
      return 1
      ;;
  esac
}

leaf_update_package_database() {
  local _item="$(echo "$2" | awk 'BEGIN{FS="/";OFS="\\/"}{print $1,$2}')"
  case $1 in
    add)
      sed -i "/${_item}/d" "${INSTALLED_PACKAGES}"
      echo "$2" >> ${INSTALLED_PACKAGES}
      ;;
    delete)
      sed -i "/${_item}/d" "${INSTALLED_PACKAGES}"
      ;;
  esac
}

leaf_update_environment() {
  ldconfig
  [ ! -r /etc/profile ] || source /etc/profile
}

leaf_run_phase() {
  # usage: leaf_run_phase <phase_name> <func_name>
  local phase="$1" fn="$2"

  LEAF_PHASE_RC=0

  declare -F "$fn" >/dev/null || {
    LEAF_PHASE_RC=127
    return 0
  }

  if leaf_is_noop_func "$fn"; then
    LEAF_PHASE_RC=0
    return 0
  fi

  echo -e "${GREEN_COLOR}>>> ${PKG_PREFIX}/${PKG_NAME}: ${phase}${CLEAR_COLOR}"

  set +e
  (
    set -eE -o pipefail
    "$fn"
  )
  LEAF_PHASE_RC=$?
  set -e

  return 0
}

leaf_phase_must() {
  local phase="$1" fn="$2" msg="$3"
  leaf_run_phase "$phase" "$fn"
  if [ "${LEAF_PHASE_RC}" -ne 0 ]; then
    leaf_error "${msg:-${fn} failed (rc=${LEAF_PHASE_RC})}"
  fi
}

leaf_phase_try() {
  # just run, caller checks $LEAF_PHASE_RC
  leaf_run_phase "$1" "$2"
}


leaf_prepare_package() {
  local url sourcefile index _md5sum local_src

  distdir="${DIST_DIR}"
  filedir="${PKGBUILD_DIR}/${PKG_PREFIX}/${PKG_NAME}"
  mkdir -pv -- "${distdir}"

  pushd "${distdir}" >/dev/null || leaf_error "cannot enter distdir: ${distdir}"

  for index in "${!sources[@]}"; do
    sourcefile="${sources[$index]}"
    url="${urls[$index]}"

    if [ -z "${url}" ]; then
      url="${sourcefile}"
    fi

    if [ -f "${sourcefile}" ]; then
      echo "${sourcefile} exists, checking..."
      _md5sum="$(md5sum -- "${sourcefile}" | awk '{print $1}')"
      if [ "${_md5sum}" = "${md5sums[$index]}" ]; then
        echo "md5sum match, skip ${sourcefile}."
        continue
      fi
      echo "md5sum does not match, refetching..."
      rm -f -- "${sourcefile}"
    fi

    case "${url}" in
      http://*|https://*|ftp://*)
        echo "Downloading ${sourcefile} from ${url}..."
        wget -O "${sourcefile}" -- "${url}" || leaf_error "wget failed: ${url}"
        ;;
      *)
        local_src="${filedir}/${url}"
        echo "Copying ${sourcefile} from local file ${local_src}..."
        [ -f "${local_src}" ] || leaf_error "local source not found: ${local_src}"
        cp -f -- "${local_src}" "${sourcefile}" || leaf_error "cp failed: ${local_src}"
        ;;
    esac

    _md5sum="$(md5sum -- "${sourcefile}" | awk '{print $1}')"
    if [ "${_md5sum}" != "${md5sums[$index]}" ]; then
      leaf_error "md5sums of ${sourcefile}(${_md5sum}) does not match with ${md5sums[$index]}!"
    fi
  done

  popd >/dev/null || true
  echo "Sources ready."
  leaf_phase_must "Preparing" src_prepare "Prepare failed."
}

leaf_build_package() {
  mkdir -pv ${TMPDIR}
  mkdir -pv ${HOME}

  leaf_prepare_package
  leaf_phase_must "Building" src_build "Build failed."

  leaf_phase_try "Checking" src_check
  if [ "${LEAF_PHASE_RC}" -ne 0 ]; then
    if [ "${FORCE_INSTALL:-0}" = "1" ]; then
      echo -e "${RED_COLOR}* Tests failed, but is in force-install mode.${CLEAR_COLOR}"
    else
      leaf_error "Tests failed, please check. Aborting installation."
    fi
  fi

  ## run tests (strict: any failing command inside src_check fails the whole check)
  #local _check_rc=0
  #set +e
  #( set -e; src_check )
  #_check_rc=$?
  #set -e
  #
  #if [ "${_check_rc}" -ne 0 ]; then
  #  if [ "${FORCE_INSTALL}" = "1" ]; then
  #    echo -e "${RED_COLOR}* Tests failed, but is in force-install mode.${CLEAR_COLOR}"
  #  else
  #    leaf_error "Tests failed, please check. Aborting installation."
  #  fi
  #fi

  # ensure pkgdir exists
  rm -rf -- "${pkgdir}"
  install -dm755 -- "${pkgdir}"

  leaf_phase_must "Installing" src_install "Installing failed."
  local _option
  for _option in $(echo ${!BUILD_OPTIONS[*]}); do
    if [[ x"${_option}" == x"strip" ]] && [[ x"${BUILD_OPTIONS[$_option]}" == x"1" ]]; then
      leaf_strip_files
    fi
    if [[ x"${_option}" == x"libtool" ]] && [[ x"${BUILD_OPTIONS[$_option]}" == x"0" ]]; then
      leaf_remove_libtool_files
    fi
    if [[ x"${_option}" == x"zipman" ]] && [[ x"${BUILD_OPTIONS[$_option]}" = x"1" ]]; then
      leaf_compress_man_pages
    fi
  done
  if [[ x"${ENABLE_DEBUG_TREE}" == x"true" ]]; then
    [ ! -x /usr/bin/tree ] || /usr/bin/tree "${pkgdir}"
  fi
}

leaf_install_package() {
  # install need to be in fakeroot
  leaf_build_package
  # trace need to to in the same fakeroot
  leaf_trace_package "${pkgdir}"
  # ROOT starts
  leaf_phase_must "Preinstall"  src_preinstall  "Preinstall failed."
  leaf_merge_package "${pkgdir}"
  leaf_phase_must "Postinstall" src_postinstall "Postinstall failed."
  leaf_update_package_database add "${PKG_PREFIX}/${PKG_NAME}"
  leaf_update_environment
  # ROOT ends
  echo -e "${GREEN_COLOR}* Package ${PKG_PREFIX}/${PKG_NAME} has been installed${CLEAR_COLOR}\n"
}

leaf_dirct-install_package() {
  # install need to be in fakeroot
  #leaf_build_package
  # trace need to to in the same fakeroot
  leaf_trace_package "${pkgdir}"
  # ROOT starts
  leaf_phase_must "Preinstall"  src_preinstall  "Preinstall failed."
  leaf_merge_package "${pkgdir}"
  leaf_phase_must "Postinstall" src_postinstall "Postinstall failed."
  leaf_update_package_database add "${PKG_PREFIX}/${PKG_NAME}"
  leaf_update_environment
  # ROOT ends
  echo -e "${GREEN_COLOR}* Package ${PKG_PREFIX}/${PKG_NAME} has been installed${CLEAR_COLOR}\n"
}

leaf_force-install_package(){
  FORCE_INSTALL=1
  leaf_install_package
}

leaf_upgrade_package() {
  local old_query="$1" new_query="$2"
  local old_prefix old_name new_prefix new_name
  local new_srcdir new_pkgdir
  local old_trace_dir new_trace_dir
  local skipfile

  # -------- resolve OLD (installed) --------
  leaf_find_remove_pkgbuild "${old_query}"
  old_prefix="${PKG_PREFIX}"
  old_name="${PKG_NAME}"
  old_trace_dir="${TRACE_DIR}/${old_prefix}/${old_name}"
  OLD_TRACE_DIR="${old_trace_dir}"

  # -------- resolve NEW (available) --------
  leaf_find_pkgbuild "${new_query}"
  new_prefix="${PKG_PREFIX}"
  new_name="${PKG_NAME}"

  # sanity
  if [[ "${old_prefix}/${old_name}" == "${new_prefix}/${new_name}" ]]; then
    leaf_error "upgrade: old and new refer to the same package: ${old_prefix}/${old_name}"
  fi

  # =========================================================
  # 1) BUILD NEW (no ROOT touch)
  # =========================================================
  PKG_PREFIX="${new_prefix}"
  PKG_NAME="${new_name}"

  leaf_reset_state
  leaf_message_init
  source "${PKGBUILD_DIR}/${PKG_PREFIX}/${PKG_NAME}.PKGBUILD"
  leaf_parse_options

  srcdir="${BUILD_DIR}/${PKG_PREFIX}/${PKG_NAME}"
  TMPDIR="${srcdir}/.leaf-tmp"
  HOME="${srcdir}/.leaf-home"
  export TMPDIR HOME
  pkgdir="${srcdir}/__pkgdir__"

  rm -rf -- "${srcdir}" && install -dm755 -- "${srcdir}" && cd "${srcdir}"
  mkdir -pv -- "${TMPDIR}" "${HOME}"

  leaf_build_package

  new_srcdir="${srcdir}"
  new_pkgdir="${pkgdir}"

  # =========================================================
  # 2) INSTALL NEW (touch ROOT)
  #    Use dirct-install path so we don't rebuild.
  # =========================================================
  PKG_PREFIX="${new_prefix}"
  PKG_NAME="${new_name}"

  leaf_reset_state
  leaf_message_init
  source "${PKGBUILD_DIR}/${PKG_PREFIX}/${PKG_NAME}.PKGBUILD"
  leaf_parse_options

  srcdir="${new_srcdir}"
  pkgdir="${new_pkgdir}"
  TMPDIR="${srcdir}/.leaf-tmp"
  HOME="${srcdir}/.leaf-home"
  export TMPDIR HOME

  [ -d "${pkgdir}" ] || leaf_error "upgrade: built pkgdir missing: ${pkgdir}"
  cd "${srcdir}" || leaf_error "upgrade: cannot enter build dir: ${srcdir}"

  leaf_dirct-install_package

  # new trace dir now exists (generated by leaf_dirct-install_package -> leaf_trace_package)
  new_trace_dir="${TRACE_DIR}/${new_prefix}/${new_name}"
  [ -d "${new_trace_dir}" ] || leaf_error "upgrade: new trace dir missing: ${new_trace_dir}"

  # =========================================================
  # 3) REMOVE OLD, but SKIP paths owned by NEW
  # =========================================================
  skipfile="$(mktemp "${TEMP_DIR}/leaf-upgrade-skip.XXXXXX")" || leaf_error "mktemp failed"
  cat "${new_trace_dir}/FILES" "${new_trace_dir}/LINKS" "${new_trace_dir}/DIRS" > "${skipfile}" || {
    rm -f -- "${skipfile}"
    leaf_error "upgrade: cannot create skiplist"
  }

  PKG_PREFIX="${old_prefix}"
  PKG_NAME="${old_name}"

  leaf_reset_state
  leaf_message_init
  # old remove sources PKGBUILD from TRACE (best effort)
  source "${old_trace_dir}/PKGBUILD" 2>/dev/null || true

  export LEAF_REMOVE_SKIPLIST="${skipfile}"
  leaf_remove_package
  unset LEAF_REMOVE_SKIPLIST

  rm -f -- "${skipfile}"

  echo -e "${GREEN_COLOR}* Upgraded ${old_prefix}/${old_name} -> ${new_prefix}/${new_name}${CLEAR_COLOR}\n"
}

leaf_in_skiplist() {
  # usage: leaf_in_skiplist <abs_path>
  # return 0 if path in LEAF_REMOVE_SKIPLIST file
  local p="$1"
  [ -n "${LEAF_REMOVE_SKIPLIST:-}" ] || return 1
  [ -r "${LEAF_REMOVE_SKIPLIST}" ] || return 1
  grep -Fxq -- "$p" "${LEAF_REMOVE_SKIPLIST}" 2>/dev/null
}

leaf_remove_package() {
  [ -n "$(grep "${PKG_PREFIX}/${PKG_NAME}" ${INSTALLED_PACKAGES})" ] || {
    leaf_error "Package ${PKG_PREFIX}/${PKG_NAME} is NOT installed"
  }

  leaf_phase_must "Preremove" src_preremove "Preremove failed."

  local _file _link _directory _etc_backup_path _relative_path _backup_etc=false
  local _trace_dir="${TRACE_DIR}/${PKG_PREFIX}/${PKG_NAME}"
  local _has_conffiles=0

  [ -r "${_trace_dir}/CONFFILES" ] && _has_conffiles=1

  _etc_backup_path="/etc/.leaf_backup/${PKG_NAME}_$(date +%Y%m%d%H%M%S)"

  # FILES
  while read -r _file; do
    if leaf_in_skiplist "${_file}"; then
      continue
    fi
    if [[ "${_file}" == /etc/* && -e "${_file}" ]]; then
      if [ "${_has_conffiles}" -eq 1 ]; then
        if leaf_conffile_is_unmodified "${_file}" "${_trace_dir}"; then
          rm -fv -- "${_file}"
        else
          leaf_record_message "Preserved modified config file: ${_file}"
        fi
        continue
      fi

      # fallback for old packages without CONFFILES (keep old behavior)
      if [[ -f "${_file}" ]]; then
        _backup_etc=true
        _relative_path="${_file#/etc/}"
        mkdir -pv "${_etc_backup_path}/$(dirname "${_relative_path}")"
        mv -v -- "${_file}" "${_etc_backup_path}/${_relative_path}"
      else
        rm -fv -- "${_file}"
      fi
    else
      rm -fv -- "${_file}"
    fi
  done < "${_trace_dir}/FILES"

  if [[ "${_backup_etc}" == true ]]; then
    leaf_record_message "Found files in /etc, backuped to ${_etc_backup_path}."
  fi

  # LINKS
  while read -r _link; do
    if leaf_in_skiplist "${_link}"; then
      continue
    fi
    if [[ "${_link}" == /etc/* && -e "${_link}" ]]; then
      if [ "${_has_conffiles}" -eq 1 ]; then
        if leaf_conffile_is_unmodified "${_link}" "${_trace_dir}"; then
          rm -fv -- "${_link}"
        else
          leaf_record_message "Preserved modified config link: ${_link}"
        fi
        continue
      fi
    else
      [ ! -L "${_link}" ] || rm -fv -- "${_link}"
    fi
  done < "${_trace_dir}/LINKS"

  # DIRS
  while read -r _directory; do
    if leaf_in_skiplist "${_directory}"; then
      continue
    fi
    if [[ -d "${_directory}" ]]; then
      rmdir --ignore-fail-on-non-empty "${_directory}"
    fi
  done < "${_trace_dir}/DIRS"

  leaf_phase_must "Postremove" src_postremove "Postremove failed."
  leaf_invoke_hooks remove
  rm -rf "${_trace_dir}"
  leaf_update_package_database delete "${PKG_PREFIX}/${PKG_NAME}"
  leaf_update_environment
  echo -e "${GREEN_COLOR}<<< removed ${PKG_PREFIX}/${PKG_NAME}${CLEAR_COLOR}"
}

leaf_search_package() {
  local _item _regex
  for _item in "$@"; do
    _regex+="${_item}\|"
  done
  _regex="${_regex:0:${#_regex}-2}"
  find "${PKGBUILD_DIR}" -type f -regex ".*\(${_regex}\).*\.PKGBUILD" -printf "%P\n" | while read -r _item; do
    echo "${_item%.PKGBUILD}"
  done
}

leaf_show_package() {
  source "${PKGBUILD_DIR}/${PKG_PREFIX}/${PKG_NAME}".PKGBUILD
  echo "Package: ${pkgname}"
  echo "Version: ${pkgver}"
  echo "Description: ${pkgdesc}"
  echo "Homepage: ${homepage}"
  echo -e "License: ${license[@]}\n"
}

leaf_pack_package() {
  leaf_build_package
  pushd "${srcdir}" > /dev/null 2>&1
  echo "${PKG_PREFIX}" > .PKG_PREFIX
  install -m644 "${PKGBUILD_DIR}/${PKG_PREFIX}/${PKG_NAME}".PKGBUILD .PKGBUILD
  install -dm755 "${BINARY_DIR}"
  local _target="${BINARY_DIR}/${pkgname}-${pkgver}${BINARY_EXT}"
  rm -fv "${_target}"
  tar -I "${COMPRESS_PROG}" -cf "${_target}" "$(basename ${pkgdir})" .PKG{_PREFIX,BUILD}
  popd > /dev/null 2>&1
}

leaf_unpack_package() {
  srcdir="${TEMP_DIR}/$(basename ${1%.pkg.*})"
  pkgdir="${srcdir}"/__pkgdir__
  rm -rf "${srcdir}" && install -dm755 "${srcdir}" && cd "${srcdir}"
  tar -xf $1 -C "${srcdir}"
  source "${srcdir}"/.PKGBUILD
  PKG_PREFIX="$(cat "${srcdir}"/.PKG_PREFIX)"
  PKG_NAME="${pkgname}-${pkgver}"
  install -Dm644 "${srcdir}"/.PKGBUILD "${PKGBUILD_DIR}/${PKG_PREFIX}/${PKG_NAME}".PKGBUILD
  leaf_trace_package "${pkgdir}"
  src_preinstall
  leaf_merge_package "${pkgdir}"
  src_postinstall
  leaf_update_package_database add "${PKG_PREFIX}/${PKG_NAME}"
  leaf_update_environment
  echo -e "${GREEN_COLOR}* Package ${PKG_PREFIX}/${PKG_NAME} has been installed${CLEAR_COLOR}\n"
}

leaf_add_shell() {
  # usage: leaf_add_shell <abs_shell_path>
  local shell="$1" f="/etc/shells" tmp

  # ensure file exists
  if [ ! -f "$f" ]; then
    install -m 0644 -o root -g root /dev/null "$f" || return 1
  fi

  # already present? (exact whole-line match)
  grep -Fxq -- "$shell" "$f" && return 0

  tmp="$(mktemp "${f}.leaf.XXXXXX")" || return 1
  cat "$f" > "$tmp" || { rm -f "$tmp"; return 1; }

  # append with newline
  printf '%s\n' "$shell" >> "$tmp" || { rm -f "$tmp"; return 1; }

  # atomic replace
  install -m 0644 -o root -g root "$tmp" "$f" || { rm -f "$tmp"; return 1; }
  rm -f "$tmp"
}

leaf_remove_shell() {
  # usage: leaf_remove_shell <abs_shell_path>
  local shell="$1" f="/etc/shells" tmp

  [ -f "$f" ] || return 0
  # if not present, nothing to do
  grep -Fxq -- "$shell" "$f" || return 0

  tmp="$(mktemp "${f}.leaf.XXXXXX")" || return 1
  # delete exact matching lines only
  grep -Fvx -- "$shell" "$f" > "$tmp" || true

  install -m 0644 -o root -g root "$tmp" "$f" || { rm -f "$tmp"; return 1; }
  rm -f "$tmp"
}


main $@
