#!/usr/bin/env bash
# sync_cfg.sh
# Usage: sync_cfg.sh <mode> <ip1> [ip2 ...]
# mode: all | port | dev | auto
# STDOUT only contains lines like:
# SN：xxxxx SUCCESS
# SN：xxxxx ERROR:错误原因
# Other logs go to STDERR.

PASS='lnxall123'
SSH_OPTS="-o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o PreferredAuthentications=password,keyboard-interactive -o KbdInteractiveAuthentication=yes -o PubkeyAuthentication=no -o ConnectTimeout=3"
SSHPASS_BIN="$(command -v sshpass || true)"

if [[ -z "$SSHPASS_BIN" ]]; then
  echo "ERROR: sshpass not found. Please install sshpass." >&2
  exit 2
fi

if [[ $# -lt 2 ]]; then
  echo "Usage: $0 <all|port|dev|auto> <ip1> [ip2 ...]" >&2
  exit 2
fi

MODE="$1"
shift
IFS=',' read -ra IPS <<< "$*"

declare -A SKIP_IP=()

LOCAL_LANG="$(grep LANG /etc/default/locale | tail -n 1 | cut -d= -f2 | cut -d. -f1)"
if [[ "$LOCAL_LANG" == "zh_CN" ]]; then
  MSG_SUCCESS="同步成功"
  MSG_ERROR="错误"
  MSG_LC_MISMATCH="LC 目录结构不一致"
  MSG_NO_PORT="本地 /app/config/port_config.json 未找到"
  MSG_PORT_FAIL="复制 port_config.json 失败"
  MSG_NO_DEV="未找到可复制的 device_config 文件"
  MSG_DEV_FAIL="复制 device_config 文件失败"
  MSG_NO_AUTO="未找到可复制的 automatic_control 文件"
  MSG_AUTO_FAIL="复制 automatic_control 文件失败"
  MSG_NO_CFG="未找到可复制的配置文件"
  MSG_CFG_FAIL="复制配置文件失败"
  MSG_INVALID="无效模式"
  MSG_TPL_NOT_FOUND="模板文件未找到"
  MSG_TPL_FAIL="复制模板文件失败"
else
  MSG_SUCCESS="SUCCESS"
  MSG_ERROR="ERROR"
  MSG_LC_MISMATCH="LC directory structure mismatch"
  MSG_NO_PORT="local /app/config/port_config.json not found"
  MSG_PORT_FAIL="failed to copy port_config.json"
  MSG_NO_DEV="No device_config files found locally to copy"
  MSG_DEV_FAIL="failed to copy device_config files"
  MSG_NO_AUTO="No automatic_control files found locally to copy"
  MSG_AUTO_FAIL="failed to copy automatic_control files"
  MSG_NO_CFG="No config files found locally to copy"
  MSG_CFG_FAIL="failed to copy config files"
  MSG_INVALID="Invalid mode"
  MSG_TPL_NOT_FOUND="template file not found"
  MSG_TPL_FAIL="failed to copy template files"
fi

run_ssh() {
  local ip="$1"; shift
  "$SSHPASS_BIN" -p "$PASS" ssh $SSH_OPTS "root@${ip}" -- "$@"
}

run_ssh_output() {
  local ip="$1"; shift
  "$SSHPASS_BIN" -p "$PASS" ssh $SSH_OPTS "root@${ip}" -- "$@" 2>/dev/null
}

scp_to_remote() {
  local ip="$1"; shift
  local src="$1"; shift
  local dest="$1"; shift

  local destdir
  destdir="$(dirname "$dest")"
  run_ssh "$ip" mkdir -p "$destdir" 2>/dev/null || return 10

  if [[ -d "$src" ]]; then
    "$SSHPASS_BIN" -p "$PASS" scp $SSH_OPTS -r "$src/" "root@${ip}:$dest" >/dev/null 2>&1
  else
    "$SSHPASS_BIN" -p "$PASS" scp $SSH_OPTS "$src" "root@${ip}:$dest" >/dev/null 2>&1
  fi
  return $?
}

stream_paths_to_remote() {
  local ip="$1"; shift

  local -a rel_paths=()
  local p rel
  for p in "$@"; do
    if [[ -e "$p" ]]; then
      rel="${p#/}"
      rel_paths+=("$rel")
    fi
  done

  if (( ${#rel_paths[@]} == 0 )); then
    return 11
  fi

  local comp_cmd decomp_cmd
  if command -v pigz >/dev/null 2>&1; then
    comp_cmd="pigz -1 -c"
    decomp_cmd="pigz -d"
  else
    comp_cmd="gzip -1 -c"
    decomp_cmd="gzip -d"
  fi

  tar -C / -cf - "${rel_paths[@]}" 2>/dev/null \
    | eval "$comp_cmd" 2>/dev/null \
    | "$SSHPASS_BIN" -p "$PASS" ssh $SSH_OPTS "root@${ip}" "$decomp_cmd | tar -C / -xf -" >/dev/null 2>&1

  local rc=${PIPESTATUS[2]}
  return ${rc:-1}
}

get_remote_sn() {
  local ip="$1"
  local out
  out="$("$SSHPASS_BIN" -p "$PASS" ssh $SSH_OPTS "root@${ip}" sh -lc '
    (. /etc/profile 2>/dev/null || true)
    (. /root/.profile 2>/dev/null || true)
    (. /root/.bashrc 2>/dev/null || true)

    if [ -x /opt/lnxall_app/bin/factory ]; then
      /opt/lnxall_app/bin/factory get 2>/dev/null | grep -E "SN[:=]" || true
      exit 0
    fi

    true
  ' 2>/dev/null)"

  local sn
  sn="$(printf '%s' "$out" | sed -n -E 's/.*SN[=:][[:space:]]*([^[:space:]]+).*/\1/p' | tr -d '\r\n' | head -n1)"

  if [[ -z "$sn" ]]; then
    sn="UNKNOWN_${ip}"
  fi
  printf '%s' "$sn"
}

local_lc_list() {
  if [[ -d /app/config/lc_devs ]]; then
    (
      for d in /app/config/lc_devs/*; do
        [ -d "$d" ] || continue
        basename "$d"
      done
    ) | sed -e 's/\r$//' -e 's/[[:space:]]\+$//' | sort
  fi
}

remote_lc_list() {
  local ip="$1"
  "$SSHPASS_BIN" -p "$PASS" ssh $SSH_OPTS "root@${ip}" sh -lc '
    if [ -d /app/config/lc_devs ]; then
      for d in /app/config/lc_devs/*; do
        [ -d "$d" ] || continue
        basename "$d"
      done | sed -e "s/\r$//" -e "s/[[:space:]]\+$//" | sort
    fi
  ' 2>/dev/null || true
}

compare_lc_dirs() {
  local ip="$1"
  local local_list remote_list

  local_list="$(local_lc_list)"
  remote_list="$(remote_lc_list "$ip")"

  local_list="$(printf '%s\n' "$local_list" | sed -e 's/\r$//' -e 's/[[:space:]]\+$//' | sed '/^[[:space:]]*$/d')"
  remote_list="$(printf '%s\n' "$remote_list" | sed -e 's/\r$//' -e 's/[[:space:]]\+$//' | sed '/^[[:space:]]*$/d')"

  if [[ -z "$local_list" && -z "$remote_list" ]]; then
    return 0
  fi

  if [[ "$local_list" == "$remote_list" ]]; then
    return 0
  else
    return 1
  fi
}

precheck_lc_dirs() {
  if [[ "$MODE" != "all" && "$MODE" != "dev" && "$MODE" != "auto" && "$MODE" != "tplate" ]]; then
    return 0
  fi

  local ip
  for ip in "${IPS[@]}"; do
    if ! compare_lc_dirs "$ip"; then
      local sn
      sn="$(get_remote_sn "$ip")"
      echo "SN：${sn} ${MSG_ERROR}:${MSG_LC_MISMATCH}"
      SKIP_IP["$ip"]=1
    fi
  done
}

process_ip() {
  local ip="$1"

  if [[ -n "${SKIP_IP[$ip]}" ]]; then
    return
  fi

  local sn
  sn="$(get_remote_sn "$ip")"

  local rc=0
  case "$MODE" in
    tplate)
      local -a tpl_files=()
      if [[ -f /app/config/device_config.json ]]; then
        mapfile -t _tpls < <(grep -o '"template"[[:space:]]*:[[:space:]]*"[^"]\+"' /app/config/device_config.json | sed -E 's/.*"template"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/')
        tpl_files+=("${_tpls[@]}")
      fi
      if [[ -d /app/config/lc_devs ]]; then
        while IFS= read -r lc; do
          local lc_dev_cfg="/app/config/lc_devs/${lc}/device_config.json"
          if [[ -f "$lc_dev_cfg" ]]; then
            mapfile -t _lc_tpls < <(grep -o '"template"[[:space:]]*:[[:space:]]*"[^"]\+"' "$lc_dev_cfg" | sed -E 's/.*"template"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/')
            tpl_files+=("${_lc_tpls[@]}")
          fi
        done < <(local_lc_list)
      fi

      mapfile -t tpl_files < <(printf '%s\n' "${tpl_files[@]}" | sort -u)

      if [[ ${#tpl_files[@]} -eq 0 ]]; then
        echo "SN：${sn} ${MSG_SUCCESS}"
        return
      fi

      local -a to_copy=()
      local found=0
      for tpl in "${tpl_files[@]}"; do
        local zh_tpl="/app/global/template/zh_CN/${tpl}"
        local en_tpl="/app/global/template/en_US/${tpl}"
        if [[ -f "$zh_tpl" ]]; then
          to_copy+=("$zh_tpl")
          found=1
        fi
        if [[ -f "$en_tpl" ]]; then
          to_copy+=("$en_tpl")
          found=1
        fi
        if [[ ! -f "$zh_tpl" && ! -f "$en_tpl" ]]; then
          echo "SN：${sn} ${MSG_ERROR}:${MSG_TPL_NOT_FOUND}: $tpl"
        fi
      done

      if [[ $found -eq 0 ]]; then
        echo "SN：${sn} ${MSG_SUCCESS}"
        return
      fi

      if ! stream_paths_to_remote "$ip" "${to_copy[@]}"; then
        echo "SN：${sn} ${MSG_ERROR}:${MSG_TPL_FAIL}"
        return
      fi
      ;;
    port)
      if [[ -f /app/config/port_config.json ]]; then
        if ! stream_paths_to_remote "$ip" "/app/config/port_config.json"; then
          echo "SN：${sn} ${MSG_ERROR}:${MSG_PORT_FAIL}"
          return
        fi
      else
        echo "SN：${sn} ${MSG_ERROR}:${MSG_NO_PORT}"
        return
      fi
      ;;
    dev)
      local -a to_copy=()
      local any_copied=0

      if [[ -f /app/config/device_config.json ]]; then
        to_copy+=("/app/config/device_config.json")
        any_copied=1
      fi

      if [[ -d /app/config/lc ]]; then
        mapfile -t __lc_dev_jsons < <(find /app/config/lc -type f -name 'device_config.json' 2>/dev/null || true)
        if [[ ${#__lc_dev_jsons[@]} -gt 0 ]]; then
          to_copy+=("${__lc_dev_jsons[@]}")
          any_copied=1
        fi
      fi

      if [[ -d /app/config/lc_devs ]]; then
        while IFS= read -r lc; do
          local src="/app/config/lc_devs/${lc}/device_config.json"
          if [[ -f "$src" ]]; then
            to_copy+=("$src")
            any_copied=1
          fi
        done < <(local_lc_list)
      fi

      if [[ $any_copied -eq 0 ]]; then
        echo "SN：${sn} ${MSG_ERROR}:${MSG_NO_DEV}"
        return
      fi

      if ! stream_paths_to_remote "$ip" "${to_copy[@]}"; then
        echo "SN：${sn} ${MSG_ERROR}:${MSG_DEV_FAIL}"
        return
      fi
      ;;
    auto)
      local -a to_copy=()
      local any_copied=0

      if [[ -f /app/config/automatic_control.json ]]; then
        to_copy+=("/app/config/automatic_control.json")
        any_copied=1
      fi

      if [[ -d /app/config/lc_devs ]]; then
        while IFS= read -r lc; do
          local src="/app/config/lc_devs/${lc}/automatic_control.json"
          if [[ -f "$src" ]]; then
            to_copy+=("$src")
            any_copied=1
          fi
        done < <(local_lc_list)
      fi

      if [[ $any_copied -eq 0 ]]; then
        echo "SN：${sn} ${MSG_ERROR}:${MSG_NO_AUTO}"
        return
      fi

      if ! stream_paths_to_remote "$ip" "${to_copy[@]}"; then
        echo "SN：${sn} ${MSG_ERROR}:${MSG_AUTO_FAIL}"
        return
      fi
      ;;
    all)
      local -a to_copy=()
      local any_copied=0

      if [[ -d /app/config ]]; then
        mapfile -t __top_jsons < <(compgen -G '/app/config/*.json' || true)
        if [[ ${#__top_jsons[@]} -gt 0 ]]; then
          to_copy+=("${__top_jsons[@]}")
          any_copied=1
        fi
      fi

      if [[ -d /app/config/lc ]]; then
        to_copy+=("/app/config/lc")
        any_copied=1
      fi

      if [[ -d /app/config/lc_devs ]]; then
        while IFS= read -r lc; do
          local src="/app/config/lc_devs/${lc}"
          if [[ -d "$src" ]]; then
            to_copy+=("$src")
            any_copied=1
          fi
        done < <(local_lc_list)
      fi

      local -a tpl_files=()
      if [[ -f /app/config/device_config.json ]]; then
        mapfile -t _tpls < <(grep -o '"template"[[:space:]]*:[[:space:]]*"[^"]\+"' /app/config/device_config.json | sed -E 's/.*"template"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/')
        tpl_files+=("${_tpls[@]}")
      fi
      if [[ -d /app/config/lc_devs ]]; then
        while IFS= read -r lc; do
          local lc_dev_cfg="/app/config/lc_devs/${lc}/device_config.json"
          if [[ -f "$lc_dev_cfg" ]]; then
            mapfile -t _lc_tpls < <(grep -o '"template"[[:space:]]*:[[:space:]]*"[^"]\+"' "$lc_dev_cfg" | sed -E 's/.*"template"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/')
            tpl_files+=("${_lc_tpls[@]}")
          fi
        done < <(local_lc_list)
      fi
      # 去重
      mapfile -t tpl_files < <(printf '%s\n' "${tpl_files[@]}" | sort -u)

      for tpl in "${tpl_files[@]}"; do
        local zh_tpl="/app/global/template/zh_CN/${tpl}"
        local en_tpl="/app/global/template/en_US/${tpl}"
        if [[ -f "$zh_tpl" ]]; then
          to_copy+=("$zh_tpl")
          any_copied=1
        fi
        if [[ -f "$en_tpl" ]]; then
          to_copy+=("$en_tpl")
          any_copied=1
        fi
        if [[ ! -f "$zh_tpl" && ! -f "$en_tpl" ]]; then
          echo "SN：${sn} ${MSG_ERROR}:${MSG_TPL_NOT_FOUND}: $tpl"
        fi
      done

      if [[ $any_copied -eq 0 ]]; then
        echo "SN：${sn} ${MSG_ERROR}:${MSG_NO_CFG}"
        return
      fi

      if ! stream_paths_to_remote "$ip" "${to_copy[@]}"; then
        echo "SN：${sn} ${MSG_ERROR}:${MSG_CFG_FAIL}"
        return
      fi
      ;;
    *)
      echo "${MSG_INVALID}" >&2
      echo "SN：UNKNOWN ${MSG_ERROR}:${MSG_INVALID}"
      return
      ;;
  esac

  echo "SN：${sn} ${MSG_SUCCESS}"
}

precheck_lc_dirs

for ip in "${IPS[@]}"; do
  process_ip "$ip"
done

exit 0