#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2018-present Team LibreELEC (https://libreelec.tv)

# Handler for git
# Usage (in package.mk):
# PKG_URL (mandatory) must point to a git repository (git://... or https://example.com/repo.git)
# PKG_VERSION (mandatory) must point to a commit SHA, e.g. a1b2c3d
# PKG_GIT_SHA (optional) full hash of git commit
# PKG_GIT_CLONE_BRANCH (optional) clone specific branch
# PKG_GIT_CLONE_SINGLE (optional) clone single branch only (set to yes)
# PKG_GIT_CLONE_DEPTH (optional) history to clone, must be a number
# PKG_GIT_SUBMODULE_DEPTH (optional) history of submodules to clone, must be a number

set -e
set -o pipefail


#_debug=true
if [[ "${_debug}" ]] ; then
  export PS4='+${BASH_SOURCE}:${LINENO}: '
  set -x
fi


_clean_repo() {
  git clean -fdx
  git restore --staged . || git switch --force # Clear changes and ensure correct branch
}

_repo_exists_and_valid() {
  if [[ -d "${PACKAGE}" ]]; then
    pushd "${PACKAGE}" > /dev/null
    _clean_repo || return 1

    if ! git ls-remote . | grep -q -m1 "^${PKG_VERSION}"; then
      echo "ERROR: ${PACKAGE}: failed to determine git HEAD" >&2
      return 1
    fi

    if [[ "${PKG_URL}" != "$(git remote get-url origin)" ]]; then
      echo "ERROR: ${PACKAGE}: failed to obtain URL of origin" >&2
      return 1
    fi

    if [[ -n "${PKG_GIT_CLONE_BRANCH}" && "${PKG_GIT_CLONE_BRANCH}" != "$(git rev-parse --abbrev-ref HEAD)" ]]; then
      echo "ERROR: ${PACKAGE}: failed to determine current git branch" >&2
      return 1
    fi

    popd > /dev/null
    return 0
  fi
  return 1
}

_update_submodules() {
  if [[ -n "${GIT_SUBMODULE_PARAMS}" ]]; then
    submodules=$(git config --file .gitmodules --get-regexp path | awk '{ print $2 }')
  else
    submodules=$(git submodule | awk '{ print $2 }')
  fi

  for submodule in $submodules; do
    echo "Updating submodule: $submodule"
    
    # Try to update the submodule
    git submodule update --init --recursive ${GIT_SUBMODULE_PARAMS} -- "${submodule}" 
    _rv=$?

    if [[ $_rv != 0 ]] ; then
      echo "Failed to update submodule: $submodule"

      # Deinit, sync, and reinit the submodule
      echo "Deinitializing submodule: $submodule"
      git submodule deinit -f "${submodule}"

      echo "Syncing submodule: $submodule"
      git submodule sync -- "${submodule}"

      echo "Reinitializing submodule: $submodule"
      git submodule update --init --recursive ${GIT_SUBMODULE_PARAMS} -- "${submodule}"
      _rv=$?
      if [[ $_rv != 0 ]] ; then
        echo "Failed to reinitialize submodule: $submodule"
        return 1
      fi
    fi
    
    #popd > /dev/null
  done

  return 0
}


_get_repo() {
  local git_params=("--recursive" "--shallow-submodules" "--filter=blob:none" "--no-tags")

  [[ -n "${PKG_GIT_CLONE_BRANCH}" ]] && git_params+=("--branch" "${PKG_GIT_CLONE_BRANCH}")
  [[ "${PKG_GIT_CLONE_SINGLE}" == "yes" ]] && git_params+=("--single-branch")

  if [[ -n "${PKG_GIT_CLONE_DEPTH}" ]]; then
    if [[ "${PKG_GIT_CLONE_DEPTH}" =~ ^[0-9]+$ ]]; then
      git_params+=("--depth" "${PKG_GIT_CLONE_DEPTH}")
    else
      echo "ERROR: PKG_GIT_CLONE_DEPTH is not a number! (${PKG_GIT_CLONE_DEPTH})" >&2
      return 1
    fi
  fi

  if [[ -n "${PKG_GIT_SUBMODULE_DEPTH}" ]]; then
    if [[ "${PKG_GIT_SUBMODULE_DEPTH}" =~ ^[0-9]+$ ]]; then
      GIT_SUBMODULE_PARAMS="${GIT_SUBMODULE_PARAMS} --depth ${PKG_GIT_SUBMODULE_DEPTH}"
    else
      die "ERROR: PKG_GIT_SUBMODULE_DEPTH is not a number! (${PKG_GIT_SUBMODULE_DEPTH})"
    fi
  fi

  # Check if the destination directory exists and is non-empty
  if [[ -d "${PACKAGE}" && "$(ls -A "${PACKAGE}")" ]]; then
    echo "Directory ${PACKAGE} already exists and is not empty. Removing it."
    rm -rf "${PACKAGE}"  && mkdir "${PACKAGE}" || return 1
  fi

  cd ..

  echo "Cloning repository with parameters: ${git_params[*]}"
  git clone "${git_params[@]}" "${PKG_URL}" "${PACKAGE}" || return 1

  pushd "${PACKAGE}" > /dev/null

  echo "Resetting to commit: ${PKG_VERSION}"
  git reset --hard "${PKG_VERSION}" || return 1

  echo "Updating submodules..."
  _update_submodules || {
    echo "Submodule update failed."
    return 1
  }

  popd > /dev/null

  # Get the SHA of the current HEAD 
  local GIT_SHA
  while read -r line; do
    if [[ $line == *HEAD ]]; then
      GIT_SHA="${line%%[[:space:]]*}"
      break
    fi
  done < <(git ls-remote "${PACKAGE}")

  if [[ -z "${GIT_SHA}" ]]; then
    echo "Failed to retrieve GIT_SHA."
    return 1
  fi

  if [[ -n "${PKG_GIT_SHA}" && "${PKG_GIT_SHA}" != "${GIT_SHA}" ]]; then
    build_msg "CLR_WARNING" "WARNING" "Incorrect git hash in repository: got ${GIT_SHA}, wanted ${PKG_GIT_SHA}" >&2
    return 1
  fi

  echo "Repository setup completed successfully."
  return 0
}


_fetch_missing_submodule_commits() {
  git submodule sync --recursive
  for submodule in $(git config --file .gitmodules --get-regexp path | awk '{ print $2 }'); do
    pushd "${submodule}" > /dev/null
    
    echo "Fetching all refs and tags for submodule: ${submodule}"
    git fetch --all --tags --prune || return 1
    
    if ! git rev-parse "${PKG_VERSION}" &> /dev/null; then
      echo "Commit ${PKG_VERSION} not found in ${submodule}, attempting to fetch it explicitly."
      git fetch origin "${PKG_VERSION}:${PKG_VERSION}" || return 1
    fi
    
    if ! git checkout "${PKG_VERSION}" ; then
      echo "Failed to checkout commit ${PKG_VERSION} in ${submodule}, trying branch instead."
      git checkout -B "${PKG_GIT_CLONE_BRANCH}" || return 1
    fi
    
    popd > /dev/null
  done
}


main(){
  # Acquire lock on source dir
  lock_source_dir "${1}"

  # Check if repo already exists and is valid
  _repo_exists_and_valid && exit 0

  if [[ "${PKG_USETOKEN}" == "yes" && -n "${ARCHR_GHTOKEN}" ]]; then
    PKG_URL="${PKG_URL/https:\/\//https:\/\/${ARCHR_GHTOKEN}@}"
  fi

  # If not valid, proceed to get the repo
  build_msg "CLR_GET" "GET" "${1} (git)" "indent"

  # Remove stale stamps
  rm -f "${STAMP_URL}" "${STAMP_SHA}"

  # Clone or update the repository
  _get_repo || exit 1

  # Verify the git hash 
  GIT_SHA=""
  while IFS=$'\t' read -r sha ref; do
    if [[ "$ref" == "HEAD" ]]; then
      GIT_SHA="$sha"
      break
    fi
  done < <(git ls-remote "${PACKAGE}")

  # Check if GIT_SHA was successfully retrieved
  if [[ -z "$GIT_SHA" ]]; then
    echo "Failed to retrieve GIT_SHA." >&2
    exit 1
  fi

  # Verify that the retrieved SHA matches the expected one
  if [[ -n "${PKG_GIT_SHA}" && "${PKG_GIT_SHA}" != "${GIT_SHA}" ]]; then
    build_msg "CLR_WARNING" "WARNING" "Incorrect git hash in repository: got ${GIT_SHA}, wanted ${PKG_GIT_SHA}" >&2
    exit 1
  fi

  echo "Repository setup completed successfully."
}


main
