#!/bin/bash
#
# Confidential and Proprietary for Oracle Corporation
#
# This computer program contains valuable, confidential, and
# proprietary information.  Disclosure, use, or reproduction
# without the written authorization of Oracle is prohibited.
# This unpublished work by Oracle is protected by the laws
# of the United States and other countries.  If publication
# of this computer program should occur, the following notice
# shall apply:
#
# Copyright (c) 2024 Oracle Corp.
# All rights reserved.
#
# Author: cec_devops_ww_grp@oracle.com
#
#################################################################

SCRIPT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && cd ../ && pwd)"
PATCH_SCRIPTS_DIR=${SCRIPT_PATH}/patch

SCRIPTS_FILENAME='scripts.zip'

INPUT_ARGS="$@"


check_url_availability() {
  local url="$1"
  local response_code
  response_code=$(curl -sS -o /dev/null -w "%{http_code}\n" "$url")

  case "$response_code" in
    200)
      return 0
      ;;
    404)
      echo "Resource not found (HTTP 404)"
      return 1
      ;;
    403)
      echo "Access forbidden (HTTP 403)"
      return 1
      ;;
    401)
      echo "Authentication required (HTTP 401)"
      return 1
      ;;
    *)
      echo "Unexpected HTTP response code: $response_code"
      return 1
      ;;
  esac
}

download_and_extract() {
  local download_url="$1"
  local output_file="$2"
  local extract_path="$3"
  local temp_dir

  # Create temporary directory for downloads
  temp_dir=$(mktemp -d)
  trap 'rm -rf "$temp_dir"' RETURN

  if ! download_with_retry "$download_url" "${temp_dir}/${output_file}"; then
    echo "Failed to download $output_file"
    return 1
  fi

  # Verify file exists and has size
  if [ ! -s "${temp_dir}/${output_file}" ]; then
    echo "Downloaded file is empty or missing"
    return 1
  fi

  if ! unzip -qo "${temp_dir}/${output_file}" -d "$extract_path"; then
    echo "Failed to extract $output_file"
    return 1
  fi
  return 0
}

download_with_retry() {
  local url="$1"
  local output_file="$2"
  local retries=0
  local max_retries=${MAX_RETRIES:-3}
  local wait_time=5

  while [ $retries -lt $max_retries ]; do
    if curl -sS \
      --retry 3 \
      --retry-delay 5 \
      --max-time 60 \
      --location \
      --output "$output_file" \
      "$url"; then
      # Verify download
      if [ -s "$output_file" ]; then
        return 0
      else
        echo "Downloaded file is empty"
      fi
    else
      echo "Download failed with curl exit code $?"
    fi

    retries=$((retries + 1))
    if [ $retries -lt $max_retries ]; then
      wait_time=$((wait_time * 2))
      echo "Retrying in $wait_time seconds..."
      sleep $wait_time
    fi
  done

  echo "Failed to download after $max_retries attempts"
  return 1
}

validate_mode() {
  local offline="$1"
  local patch_dir="$2"

  if [[ -z "$patch_dir" ]]; then
    echo "Offline mode requires --patch_dir parameter"
    return 1
  fi
  if [[ ! -d "$patch_dir" ]]; then
    echo "Patch directory does not exist: $patch_dir"
    return 1
  fi
  if [[ ! -f "${patch_dir}/${SCRIPTS_FILENAME}" ]]; then
    echo "Required file ${SCRIPTS_FILENAME} not found in patch directory"
    return 1
  fi
  echo "Offline mode validated with patch directory: $patch_dir"
  return 0
}

parse_arguments() {
  local has_env=false

  # Set default environment to prod
  env="prod"
  # set default offline mode false
  offline=false
  # set default silent false
  silent=false
  while [[ $# -gt 0 ]]; do
    case $1 in
      --env)
        if [[ -z "$2" ]]; then
          echo "Missing value for --env parameter"
          exit 1
        fi
        if [[ ! "$2" =~ ^(dev|qa|prod)$ ]]; then
          echo "Invalid environment: $2. Must be dev, qa, or prod"
          exit 1
        fi
        env="$2"
        echo "Environment specified: $env"
        has_env=true
        shift 2
        ;;
      --offline)
        offline=true
        shift
        ;;
      --silent)
        silent=true
        shift
        ;;
      --patch_dir)
        if [[ -z "$2" ]]; then
          echo "Missing value for --patch_dir parameter"
          exit 1
        fi
        patch_dir="$2"
        shift 2
        ;;
      --par)
        if [[ -z "$2" ]]; then
          echo "Missing value for --par parameter"
          exit 1
        fi
        override_url="$2/scripts.zip"
        shift 2
        ;;
      *)
        shift
        ;;
    esac
  done

  if $offline; then
    if ! validate_mode "$offline" "$patch_dir"; then
      exit 1
    fi
  fi
}

generate_oracle_url() {
  local p_token="$1"
  local n_namespace="$2"
  local b_bucket="$3"
  local base_url="https://${n_namespace}.objectstorage.us-ashburn-1.oci.customer-oci.com"
  local full_url="${base_url}/p/${p_token}/n/${n_namespace}/b/${b_bucket}/o/scripts.zip"
  echo "$full_url"
}

generate_environment_urls() {
  local environment="$1"

  case "$environment" in
    "prod")
      generate_oracle_url \
        "TZ6ROonkxKGBGqBZDM2XWf92bo4njRSIGJEBGu6xhEnoESf2__xD4x6BSOw5ADLw" \
        "mpaasocicec" \
        "patch"
      ;;
    "dev")
      generate_oracle_url \
        "aYzI8BH7BESvnH3jLXhiQcp3T9Ttd9q4_vPaArBq3_q5Q15nfsYXmuJM7h87uk_e" \
        "idpgwz59jcag" \
        "dev_patch"
      ;;
    "qa")
      generate_oracle_url \
        "xoY4NmR69yGq2ED_sSkPKEf5qhv3XfZXCQ6SUW753IFZb7c5srzaC0hoNpT-57mN" \
        "idpgwz59jcag" \
        "qa_patch"
      ;;
    *)
      echo "ERROR: Unknown environment '$environment'"
      echo "Available environments: patch, dev_patch, qa_patch"
      return 1
      ;;
  esac
}

function cleanUp() {
  # clean all patch scripts on exit.
  if [ -d "${PATCH_SCRIPTS_DIR}/scripts" ]; then
    rm -rf "${PATCH_SCRIPTS_DIR}/scripts"
  fi
  if [ -f "${PATCH_SCRIPTS_DIR}/patch_tool_internal.sh" ]; then
    rm -f "${PATCH_SCRIPTS_DIR}/patch_tool_internal.sh"
  fi

  # cleanup deprecated files
  upgrade_scripts_dir=${SCRIPT_PATH}/lcm/upgrade
  # cleanup deprecated scripts
  delete_files=(
    "${PATCH_SCRIPTS_DIR}/patch.yaml"
    "${PATCH_SCRIPTS_DIR}/patch_14.yaml"
    "${PATCH_SCRIPTS_DIR}/../patch_tool.sh"
    "${upgrade_scripts_dir}/wc_1412_pre_upgrade.sh"
    "${upgrade_scripts_dir}/wc_backup.sh"
    "${upgrade_scripts_dir}/wc_1412_install.sh"
    "${upgrade_scripts_dir}/wc_1412_post_upgrade.sh"
  )
  for filename in "${delete_files[@]}"; do
    if [ -f "${filename}" ]; then
      rm -f "${filename}"
    fi
  done
}
# This handles program EXIT as well as interrupts (signal INT) and kills (signal TERM)
trap cleanUp EXIT INT TERM

main() {
  # Parse and validate arguments
  parse_arguments "$@"

  # Check prerequisites
  if [ $EUID -eq 0 ]; then
    echo "Execution with root user not allowed"
    exit 1
  fi
  # Set environment-specific variables
  if [[ -n $env ]]; then
    case "$env" in
      dev)
        scripts_par=${override_url:-$(generate_environment_urls "dev")}
        ;;
      qa)
        scripts_par=$(generate_environment_urls "qa")
        ;;
      prod)
        scripts_par=$(generate_environment_urls "prod")
        ;;
    esac
  fi

    # Execute in offline or online mode
    if $offline; then
      echo "Executing patch tool in offline mode with patch_dir: $patch_dir"
      if ! unzip -qo "${patch_dir}/scripts.zip" -d "${SCRIPT_PATH}"; then
        echo "Failed to extract scripts from ${patch_dir}/scripts.zip"
        exit 1
      fi
    else
      local download_url="${scripts_par}"

      # Check if URL is accessible
      if ! check_url_availability "$download_url"; then
        echo "Download URL is not accessible"
        exit 1
      fi

        # Download and extract scripts
        if ! download_and_extract "$download_url" "$SCRIPTS_FILENAME" "$SCRIPT_PATH"; then
          echo "Failed to download and extract scripts"
          exit 1
        fi
    fi

    delete_dirs=("python" "configuration" "lcm" "patch" "props" "ps" "service" "sh" "sql")
    for dir in "${delete_dirs[@]}"; do
      if [ -d "${PATCH_SCRIPTS_DIR}/${dir}" ]; then
        rm -rf "${PATCH_SCRIPTS_DIR}/${dir}"
      fi
    done

    # Set execute permissions and run internal script
    chmod +x "${PATCH_SCRIPTS_DIR}"/*.sh "${SCRIPT_PATH}"/sh/*.sh "${SCRIPT_PATH}"/lcm/sh/*.sh 
    sh ${PATCH_SCRIPTS_DIR}/patch_tool_internal.sh ${INPUT_ARGS}
    if [ $? -ne 0 ]; then
      echo "Patching operation failed."
      exit 1
    fi
}

# Execute main function with all arguments
main "$@"
