#!/bin/bash
# Set parameters

DOWNLOAD_DIR="http://dl.fogworks.io/maxio/online"
LOG_DIR="/var/log"
LOG_FILE="$LOG_DIR/maxio_install.log"
VERSION_DIR="/opt/.maxio_version/"
DIRS=("$VERSION_DIR")
InstallPath="/opt"
InstallPacketPath="$InstallPath/facmgr_install_maxio.tar.bz2"
VFS_DIR="$DATADIR/vfs"
USE_CN_MIRROR=true
docker_env_file_path="/etc/apt/docker_sources.env"


declare -A packages=(

    ["facmgr"]="1.0.1"
    ["sensor-data"]="1.0.1"
    ["sensor"]="1.0.1"

)





storage_services=("prox_mpd")


file_name=""

upgrade=false
reinstall=false
cron_upgrade=false

function log {
    echo "$(date +"%Y-%m-%d %H:%M:%S") - $1" | tee -a $LOG_FILE
}


while getopts ":furhk" opt; do
  case $opt in
    u)
      upgrade=true
      ;;
    r)
      reinstall=true
      ;;
    h)
      cron_upgrade=true
      ;;
    k)
      USE_CN_MIRROR=false
      ;;
    f)
      file_name="$OPTARG"
      ;;
    \?)
      echo "Invalid option: -$OPTARG" >&2
      exit 1
      ;;
    :)
      echo "Option -$OPTARG requires an argument." >&2
      exit 1
      ;;
  esac
done

if [ "$reinstall" = true ]; then
  log "Reinstall is set to true."
else
  log "Reinstall is set to false."
fi

if [ "$upgrade" = true ]; then
  log "Upgrade is set to true."
else
  log "Upgrade is set to false."
fi


log "USE_CN_MIRROR is $USE_CN_MIRROR"

#CN_DOCKER_HUB_REPO="registry.cn-hangzhou.aliyuncs.com"
CN_DOCKER_HUB_REPO="maxio2-registry.cn-hangzhou.cr.aliyuncs.com"
FACMGR_INSTALL_PATH="/opt/facmgr_install"
DEFAULT_DOCKER_HUB_REPO="docker.io"
if [ "$USE_CN_MIRROR" = "true" ]; then
  DOCKER_HUB_REPO="DOCKER_HUB_REPO=$CN_DOCKER_HUB_REPO"
  COMPOSE_URL_AUTO_ENCRYPT="$FACMGR_INSTALL_PATH/docker-compose-vault_cn.yml"
  COMPOSE_URL_AUTO_ENCRYPT_TEMP="$FACMGR_INSTALL_PATH/temp/docker-compose-vault_cn.yml"
else
  DOCKER_HUB_REPO="DOCKER_HUB_REPO=$DEFAULT_DOCKER_HUB_REPO"
  COMPOSE_URL_AUTO_ENCRYPT="$FACMGR_INSTALL_PATH/docker-compose-vault.yml"
  COMPOSE_URL_AUTO_ENCRYPT_TEMP="$FACMGR_INSTALL_PATH/temp/docker-compose-vault.yml"
fi
DOCKER_HUB_REPO_LIST=("$CN_DOCKER_HUB_REPO" "$DEFAULT_DOCKER_HUB_REPO")
SERVICES="vault kes minio minio-token ipfs prox_mpd"

COMPOSE_DIR="/opt/facmgr/service"
COMPOSE_FILE="$COMPOSE_DIR/docker-compose-vault.yml"





if [ -n "$file_name" ]; then
  file="$VERSION_DIR/$file_name"

  if [ ! -f "$file" ]; then
    echo "Error: File $file not found."
    exit 1
  fi

  while read -r package version; do
    packages["$package"]="$version"
  done < "$file"
fi



if [ "$upgrade" = true ] && [ "$reinstall" = true ]; then
    log "The parameter is incorrect"
    exit
fi


upgrade_flag=""
if [ "$upgrade" = true ]; then
  upgrade_flag="-u"
fi



check_root() {
  if [[ $EUID -ne 0 ]]; then
    log "This script must be run as root"
    exit 1
  fi
}


create_directory() {
  local dir="$1"
  if [ ! -d "$dir" ]; then
    log "Directory $dir does not exist. Creating..."
    mkdir -p "$dir" || {
      log "Failed to create directory $dir"
      exit 1
    }
  fi
}

create_log() {

  if [ ! -f "$LOG_FILE" ]; then
    touch "$LOG_FILE"
  fi

}


unmount_directory() {
  local mount_point
  mount_point=$(df -P "$VFS_DIR" | awk 'END{print $NF}')
  if [ "$VFS_DIR" = "$mount_point" ]; then
    echo "Directory $VFS_DIR is a mount point. Unmounting..."
    if ! umount -l "$VFS_DIR"; then
      echo "Failed to unmount directory $VFS_DIR"
      exit 1
    fi
  fi
  umount $VFS_DIR 2>/dev/null || true
  echo "Directory $VFS_DIR is a mount point. Unmounting..."
}



upgrade_storage_service() {
    for service in "${storage_services[@]}"; do
        if docker-compose -f "$COMPOSE_FILE" ps -q "$service" &>/dev/null; then

          image_name=$(grep -A 5 "$service:" $COMPOSE_FILE | grep "^\s*image:" | awk '{print $2}')
          if [ "$cron_upgrade" = true ]; then

              updated_image_sha=$(docker inspect --format='{{.Id}}' "$image_name")
          else  

            #current_image_sha=$(docker inspect --format='{{.Id}}' "$(docker-compose -f "$COMPOSE_FILE" images -q "$service")")
            current_image_sha=$(docker inspect --format='{{.Id}}' "$image_name")

            timeout 300 docker-compose -f "$COMPOSE_FILE" pull "$service" >> "$LOG_FILE" 2>&1

            if [ $? -ne 0 ]; then
              log "Failed to pull image with timeout"
              return 1 
            fi


            updated_image_sha=$(docker inspect --format='{{.Id}}' "$image_name")
            
          fi
          log "image_name is $image_name, $service current_image_sha is $current_image_sha,updated_image_sha is $updated_image_sha"
          if [ "$current_image_sha" != "$updated_image_sha" ]; then
                docker stop "$service" >> "$LOG_FILE" 2>&1
                docker rm "$service" >> "$LOG_FILE" 2>&1
                docker image prune -f >> "$LOG_FILE" 2>&1
                docker-compose -f "$COMPOSE_FILE" up -d --force-recreate "$service" >> "$LOG_FILE" 2>&1
                log "Container for service $service has been recreated."
          else
                log "No updates found for service $service. Container remains unchanged."
          fi
        else
            log "Container for service $service is not running. Skipping update and recreation for this service."
        fi
    done
}



facmgr_install() {

    local version=$1


    if [ "$upgrade_flag" = "-u" ]; then
      log "Currently upgrading: facmgr_${version}"
    else
      log "Currently installing: facmgr_${version}"
    fi


    if [ "$cron_upgrade" = true ]; then
        log ""
    else
      if [ -f "$InstallPacketPath" ]; then
        existing_md5=$(md5sum "$InstallPacketPath" | cut -d' ' -f1)
        remote_md5=$(curl -s $DOWNLOAD_DIR/facmgr/facmgr_install_md5 | awk '{print $1}')


        if [ "$existing_md5" = "$remote_md5" ]; then
          log "facmgr_install_maxio.tar.bz2 file already exists and has the same MD5. No need to download."
        else
          rm -rf $InstallPacketPath
          wget "$DOWNLOAD_DIR/facmgr/facmgr_install_maxio.tar.bz2" -P "$InstallPath"
          log "New facmgr_install_maxio.tar.bz2 file downloaded."
          download=true
        fi
      else
        wget "$DOWNLOAD_DIR/facmgr/facmgr_install_maxio.tar.bz2" -P "$InstallPath"
        log "File facmgr_install_maxio.tar.bz2 downloaded."
        download=true
      fi
    fi


    if [ "$download" = true ]; then
            tar -xf $InstallPacketPath -C $InstallPath    
            if [ -f "/opt/facmgr_install/upgrade.sh" ]; then
              bash /opt/facmgr_install/upgrade.sh
            fi
    fi

    if [ -z "$version" ]; then
      curl $DOWNLOAD_DIR/facmgr/install_facmgr.sh|bash -s -- $upgrade_flag
    else
      curl $DOWNLOAD_DIR/facmgr/install_facmgr.sh|bash -s -- -v $version $upgrade_flag
    fi
}


system_service() {

    version_file="$VERSION_DIR/$(date +'%Y%m%d%H%M%S').txt"
    for package_name in "${!packages[@]}"; do
      version="${packages[$package_name]}"
      if [ "$package_name" = "facmgr" ]; then
        facmgr_install ${version}
        echo "$package_name $version" >> "$version_file"
      else
        InstallPacketPath=/opt/${package_name}_${version}.tar.bz2
        download=false
        url="$DOWNLOAD_DIR/${package_name}/${package_name}_${version}.tar.bz2"
        if [ -f "$InstallPacketPath" ]; then
          if [ "$reinstall" = true ]; then
              if [ -f "/opt/${package_name}_${version}/uninstall.sh" ]; then
                cd /opt/${package_name}_${version} && bash uninstall.sh 
              fi
          fi

          existing_md5=$(md5sum "$InstallPacketPath" | cut -d' ' -f1)
          remote_md5=$(curl -s "$DOWNLOAD_DIR/${package_name}/${package_name}_${version}_md5" | awk '{print $1}')

          log "${package_name}_${version} Existing MD5: $existing_md5, Remote MD5: $remote_md5"
          if [ "$existing_md5" = "$remote_md5" ]; then
            log "${package_name}_${version} File already exists and has the same MD5. No need to download."
          else
            rm -rf $InstallPacketPath
            log "Downloading $url to /opt"
            wget -O "$InstallPacketPath" "$url"
            download=true
          fi
        else
          log "Downloading $url to /opt"
          wget -O "$InstallPacketPath" "$url"
          download=true
        fi

        InstallDir="/opt/${package_name}_${version}"
        if [ "$download" = true ]; then
          if [ -d $InstallDir ]; then
            rm -r $InstallDir
            log "Old ${package_name}_${version} Packet dir removed."
          fi
        fi

        if [ ! -d $InstallDir ]; then
          tar -jxvf "/opt/${package_name}_${version}.tar.bz2" -C /opt

        fi

        if [ "$download" = true ]; then

          if [ -f "/opt/${package_name}_${version}/install.sh" ]; then
            if [ "$upgrade_flag" = "-u" ]; then
              if [ -f "/opt/${package_name}_${version}/upgrade.sh" ]; then
                log "Currently upgrading: ${package_name}_${version}"
                cd /opt/${package_name}_${version} && bash upgrade.sh -s -- $upgrade_flag
              else
                log "Upgrade script not found for ${package_name}_${version}. Skipping upgrade."
              fi

            else
              log "Currently installing: ${package_name}_${version}"
              cd /opt/${package_name}_${version} && bash install.sh -s -- $upgrade_flag
            fi

            echo "$package_name $version" >> "$version_file"
          else
            log "No install.sh found for $package_name. Skipping installation."
          fi

        fi

      fi
    done

}



cron_upgrade() {

    command_to_run="curl $DOWNLOAD_DIR/install_maxio.sh | bash -s - -u"
    logfile="/var/log/maxio_cron.log"

    existing_cron=$(crontab -l 2>/dev/null)
    if [[ $existing_cron == *"curl $DOWNLOAD_DIR"* ]]; then
        new_cron=$(echo "$existing_cron" | grep -v "install_maxio.sh")
        echo "$new_cron" | crontab -
        log "Existing cron job(s) with '$command_to_run' removed"
    fi

    (crontab -l 2>/dev/null; echo "0 0 * * * $command_to_run >> $logfile 2>&1") | crontab -
    echo "New cron job added: $command_to_run"
    #/usr/sbin/service cron restart

}


function is_package_installed() {
    if dpkg -s $1 >/dev/null 2>&1; then
        return 0
    else
        return 1
    fi
}

function install_package() {
    MAX_RETRIES=3
    RETRIES=0

    if ! is_package_installed $1; then
        while [ $RETRIES -lt $MAX_RETRIES ]; do
            log "Installing $1..."
            apt-get install -y $1
            if [ $? -eq 0 ]; then
                log "$1 successfully installed."
                break
            else
                RETRIES=$((RETRIES + 1))
                log "Failed to install $1. Retrying... (Attempt $RETRIES)"
            fi
        done

        if [ $RETRIES -eq $MAX_RETRIES ]; then
            log "Failed to install $1 after $MAX_RETRIES attempts. Please check your network connection and try again."
            exit 1
        fi
    else
        log "$1 is already installed. Skipping..."
    fi
}



function install_docker {

    apt install -y apt-transport-https ca-certificates curl software-properties-common gnupg lsb-release

    rm -f /usr/share/keyrings/docker-archive-keyring.gpg
    sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
    echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    apt update -y

}


package_install(){
  if [ -f /var/lib/dpkg/lock ]; then
    rm /var/lib/dpkg/lock
  fi
  dpkg --configure -a

  if ! dpkg -s docker-ce &>/dev/null; then
    install_docker
    install_package docker-ce
    check_command "docker-ce"
  fi

  if ! dpkg -s docker-compose &>/dev/null; then
    install_package docker-compose
    check_command "docker-compose"
  fi

  if ! dpkg -s bzip2 &>/dev/null; then
    install_package bzip2
    check_command "bzip2"
  fi

  if ! dpkg -s lbzip2 &>/dev/null; then
    install_package lbzip2
    check_command "lbzip2"
  fi

  if ! dpkg -s iw &>/dev/null; then
    install_package iw
    check_command "iw"
  fi

  if ! dpkg -s dnsmasq &>/dev/null; then
    install_package dnsmasq
    check_command "dnsmasq"
  fi

  if ! dpkg -s hostapd &>/dev/null; then
    install_package hostapd
    check_command "hostapd"
  fi
  
}

function check_command {
  if [ $? -eq 0 ]; then
    log "$1 successfully installed."
  else
    log "Failed to install $1. Please check your network connection and try again."
    exit 1
  fi
}


remove_existing_cron_jobs() {
    local script_path="$1"
    if [ -z "$script_path" ]; then
        log "Usage: remove_existing_cron_jobs <script_path>"
        return 1
    fi

    crontab -l | grep -vF "$script_path" | crontab -
}


schedule_install_script() {
    local script_path="/usr/bin/cron_upgrade.sh"
    if [ -z "$script_path" ]; then
        log "Usage: schedule_install_script <script_path>"
        return 1
    fi

    remove_existing_cron_jobs "$script_path"
    uuid=$(uuidgen)
    hash=$(echo -n "$uuid" | md5sum | awk '{print $1}')

    time_slot=$((16#${hash:0:8} % 144))

    hours=$((time_slot / 6))
    minutes=$((time_slot % 6 * 10))
    time=$(printf "%02d %02d * * *" "$minutes" "$hours")
    log "schedule time is $time"

    remove_existing_cron_jobs() {
        crontab -l | grep -v "$1" | crontab -
    }

    remove_existing_cron_jobs "$script_path"


    (crontab -l ; echo "$time $script_path") | crontab -
}





create_cron_upgrade_script() {
    cat <<EOF > /usr/bin/cron_upgrade.sh
#!/bin/bash

# 安装脚本的路径
install_script="$1"

# 日志文件路径
log_file="/var/log/cron_upgrade.log"

# 执行安装脚本并将输出写入日志文件
echo "[$(date +'%Y-%m-%d %H:%M:%S')] Starting upgrade process..." >> "\$log_file"
"\$install_script" >> "\$log_file" 2>&1
echo "[$(date +'%Y-%m-%d %H:%M:%S')] Upgrade process completed." >> "\$log_file"
EOF

    # 设置cron_upgrade.sh脚本的执行权限
    chmod +x /usr/bin/cron_upgrade.sh
}


pull_image() {
        
    export DOCKER_CLIENT_TIMEOUT=600
    export COMPOSE_HTTP_TIMEOUT=600
    log "More than a week has passed since the last update. Checking for updates..."
    if [ "$upgrade" = false ]; then
        #systemctl restart docker
        docker builder prune -f
        log "docker builder prune successful"
    fi
    
    COMPOSE_HTTP_TIMEOUT=600 docker-compose -f $COMPOSE_FILE pull $SERVICES
    
    log "docker-compose pull image"
    
    if [ $? -eq 124 ]; then
        log "Pull command timed out. Performing individual image pulls..."
        exit 1
    fi
}

function update_compose_env {



  if [ -f "$docker_env_file_path" ] && [ -s "$docker_env_file_path" ]; then
    OLD_DOCKER_HUB_REPO=$(grep '^DOCKER_HUB_REPO=' "$docker_env_file_path" | cut -d'=' -f2)
    
    if [[ " ${DOCKER_HUB_REPO_LIST[@]} " =~ " ${OLD_DOCKER_HUB_REPO} " ]]; then
        log "DOCKER_HUB_REPO is in the DOCKER_HUB_REPO_LIST."
        rm "$docker_env_file_path"
    fi  
  else  
    log "File $docker_env_file_path does not exist or is empty."
  fi
  
  log "DOCKER_HUB_REPO is $DOCKER_HUB_REPO " 
  if [ -f "$docker_env_file_path" ] && [ -s "$docker_env_file_path" ]; then
    log "File $docker_env_file_path already exists and has content, writes are ignored."
  else
    echo "$DOCKER_HUB_REPO" > "$docker_env_file_path"
    log "Contents have been written to file $docker_env_file_path."
  fi
  
  if [ -f "$docker_env_file_path" ] && [ -s "$docker_env_file_path" ]; then
    NEW_DOCKER_HUB_REPO=$(grep '^DOCKER_HUB_REPO=' "$docker_env_file_path" | cut -d'=' -f2)

    if [ -n "$NEW_DOCKER_HUB_REPO" ]; then
        sed "s/\${DOCKER_HUB_REPO}/$NEW_DOCKER_HUB_REPO/g" "$COMPOSE_URL_AUTO_ENCRYPT_TEMP" > "$COMPOSE_URL_AUTO_ENCRYPT"
        log "$COMPOSE_URL_AUTO_ENCRYPT,Replacement successful: \${DOCKER_HUB_REPO} variable in docker-compose file has been replaced with specific content: $DOCKER_HUB_REPO"
    else
        log "DOCKER_HUB_REPO variable value in file $file_path not found"
    fi
  else
    log "The file $file_path does not exist or has empty contents."
  fi

}


auto_pull_image() {
  if [ "$upgrade" = false ]; then
    update_compose_env
    cp "$COMPOSE_URL_AUTO_ENCRYPT" "$COMPOSE_FILE"
    pull_image
  fi

}

function check_domain_accessibility {
    domain="dl.fogworks.io"
    max_retries="3"
    for ((i = 1; i <= max_retries; i++)); do
        ping -c 1 "$domain" >/dev/null 2>&1
        if [ $? -eq 0 ]; then
            log "Domain $domain is accessible."
            return 0
        else
            log "Attempt $i: Domain $domain is not accessible."
        fi
    done

    log "Failed to access domain $domain after $max_retries attempts."
    exit 1
}



check_and_stop_cron() {
    if [ -f /lib/systemd/system/cron.service ]; then
        log "cron service file exists."
        systemctl stop cron
        if [ $? -eq 0 ]; then
            log "cron service has been successfully stopped."
        else
            log "Failed to stop cron service."
        fi
    else
        log "cron service is not running."
    fi
}


check_and_start_cron() {
    if [ -f /lib/systemd/system/cron.service ]; then
        log "cron service file exists."
        systemctl restart cron
        if [ $? -eq 0 ]; then
            log "cron service has been successfully started."
        else
            log "Failed to start cron service."
        fi
    else
        log "cron service is not running."
    fi
}

function check_and_remove_container() {
    local container_name="$1"
    
    if docker ps --filter "name=$container_name" --format "{{.Names}}" | grep -w "$container_name" > /dev/null; then
        log "Container $container_name is running. Stopping and removing it..."
        docker stop "$container_name"
        docker rm "$container_name"
    elif docker ps -a --filter "name=$container_name" --format "{{.Names}}" | grep -w "$container_name" > /dev/null; then
        log "Container $container_name exists but is not running. Removing it..."
        docker rm "$container_name"
    else
        log "Container $container_name does not exist."
    fi
}

function check_docker() {

  containers=("ipfs" "prox_mpd" "minio" "kes" "vault" "filecoin_station")

  for container in "${containers[@]}"; do
    check_and_remove_container "$container"
  done

  log "Sleeping for 5 seconds..."
  sleep 5

  for container in "${containers[@]}"; do
    check_and_remove_container "$container"
  done

}

function check_before_cron_service() {
    local service_file="/etc/systemd/system/facmgr_update.service"
    local pattern="Before=cron.service"

    if [[ ! -f "$service_file" ]]; then
        echo "false"
        return
    fi

    if grep -q "$pattern" "$service_file"; then
        echo "true"
    else
        echo "false"
    fi
}


manage_cron_upgrade_flag() {
    local cron_upgrade_file="/opt/.cron_upgrade"

    if [ "$cron_upgrade" = "true" ]; then
        if [ ! -f "$cron_upgrade_file" ]; then
            touch "$cron_upgrade_file"
            log "Created $cron_upgrade_file"
        else
            log "$cron_upgrade_file already exists"
        fi
    elif [ "$cron_upgrade" = "false" ]; then
        if [ -f "$cron_upgrade_file" ]; then
            rm -f "$cron_upgrade_file"
            log "Deleted $cron_upgrade_file"
        else
            log "$cron_upgrade_file does not exist"
        fi
    else
        log "Invalid parameter: cron_upgrade must be 'true' or 'false'"
    fi
}


check_and_backup_cron_upgrade() {
    DIRECTORY="/opt/.upgrade_file"
    FILE_PATH="$DIRECTORY/upgrade_file.yaml"

    if [ "$cron_upgrade" == "true" ]; then
        if [ ! -d "$DIRECTORY" ]; then
            mkdir -p "$DIRECTORY"
            log "Directory created: $DIRECTORY"
        else
            log "Directory already exists: $DIRECTORY"
        fi

        if [ -f "$FILE_PATH" ]; then
            TIMESTAMP=$(date +"%Y%m%d%H%M%S")
            BACKUP_FILE_PATH="$DIRECTORY/upgrade_file_$TIMESTAMP.yaml"
            cp "$FILE_PATH" "$BACKUP_FILE_PATH"
            log "File backed up: $BACKUP_FILE_PATH"
        else
            log "File not found: $FILE_PATH"
        fi
    else
        log "CRON_UPGRADE is not true, no action taken."
    fi
}


main() {
  check_root
  create_log

  check_domain_accessibility 
  manage_cron_upgrade_flag
  check_and_backup_cron_upgrade

  for dir in "${DIRS[@]}"; do
    create_directory "$dir"
  done
  package_install
  system_service
  if [ "$upgrade_flag" = "-u" ]; then
      svc_state=$(sqlite3 /var/lib/facmgr/db/facmgr.db "select svc_state from service;")
      log "svc service state is $svc_state"

      if [ "$svc_state" = "finish" ]; then
          upgrade_storage_service
      fi
  fi
  cron_upgrade
  if [ "$cron_upgrade" = true ]; then
      create_cron_upgrade_script
      schedule_install_script 
  fi
  auto_pull_image
}

main
log "MAX IO system initialization module installation completed."
